| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/22 15:10 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch only modifies files under arch/riscv/ (RISC-V KVM implementation), which is architecture-specific code structurally unreachable on the target architecture (amd64).",
"WorthFuzzing": false
} |
| 1/1 | 2026/09/22 15:10 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 1769c085417983b2731a98a6efe2d07f46be3adf\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Sep 22 15:10:25 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h\nindex aaf080ba1b77a..b5a9563754d08 100644\n--- a/arch/riscv/include/asm/kvm_gstage.h\n+++ b/arch/riscv/include/asm/kvm_gstage.h\n@@ -87,6 +87,8 @@ bool kvm_riscv_gstage_wp_pt_masked(struct kvm_gstage *gstage, gfn_t base_gfn,\n \n void kvm_riscv_gstage_mode_detect(void);\n \n+void kvm_riscv_gstage_free(struct kvm_gstage *gstage);\n+\n static inline unsigned long kvm_riscv_gstage_mode(unsigned long pgd_levels)\n {\n \tswitch (pgd_levels) {\n@@ -104,13 +106,18 @@ static inline unsigned long kvm_riscv_gstage_mode(unsigned long pgd_levels)\n \t}\n }\n \n-static inline void kvm_riscv_gstage_init(struct kvm_gstage *gstage, struct kvm *kvm)\n+static inline bool kvm_riscv_gstage_init(struct kvm_gstage *gstage, struct kvm *kvm)\n {\n+\tlockdep_assert_held(\u0026kvm-\u003emmu_lock);\n+\tif (!kvm-\u003earch.pgd)\n+\t\treturn false;\n+\n \tgstage-\u003ekvm = kvm;\n \tgstage-\u003eflags = 0;\n \tgstage-\u003evmid = READ_ONCE(kvm-\u003earch.vmid.vmid);\n \tgstage-\u003epgd = kvm-\u003earch.pgd;\n \tgstage-\u003epgd_levels = kvm-\u003earch.pgd_levels;\n+\treturn true;\n }\n \n #endif\ndiff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h\nindex a30600579231b..2ab999f751513 100644\n--- a/arch/riscv/include/asm/kvm_host.h\n+++ b/arch/riscv/include/asm/kvm_host.h\n@@ -86,7 +86,6 @@ struct kvm_arch {\n \tpgd_t *pgd;\n \tphys_addr_t pgd_phys;\n \tunsigned long pgd_levels;\n-\tstruct kvm_mmu_memory_cache pgd_split_page_cache;\n \n \t/* Guest Timer */\n \tstruct kvm_guest_timer timer;\ndiff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c\nindex e5002cb9cbef1..5ab3c4ba5a6a4 100644\n--- a/arch/riscv/kvm/gstage.c\n+++ b/arch/riscv/kvm/gstage.c\n@@ -414,6 +414,37 @@ bool kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr,\n \treturn flush;\n }\n \n+/* The caller has detached this tree and quiesced its hardware users. */\n+static void gstage_free_level(pte_t *ptep, u32 level, unsigned long nr_entries,\n+\t\t\t unsigned int *processed)\n+{\n+\tpte_t pte, *child;\n+\tunsigned long i;\n+\n+\tfor (i = 0; i \u003c nr_entries; i++) {\n+\t\tpte = ptep_get(\u0026ptep[i]);\n+\t\tif (level \u0026\u0026 pte_val(pte) \u0026\u0026 !gstage_pte_leaf(\u0026pte)) {\n+\t\t\tchild = (pte_t *)gstage_pte_page_vaddr(pte);\n+\t\t\tgstage_free_level(child, level - 1, PTRS_PER_PTE, processed);\n+\t\t\tput_page(virt_to_page(child));\n+\t\t}\n+\t\t/* Leaf PFNs belong to the guest backing memory, not this tree. */\n+\t\tif (++*processed == PTRS_PER_PTE) {\n+\t\t\t*processed = 0;\n+\t\t\tcond_resched();\n+\t\t}\n+\t}\n+}\n+\n+void kvm_riscv_gstage_free(struct kvm_gstage *gstage)\n+{\n+\tunsigned int processed = 0;\n+\n+\tgstage_free_level((pte_t *)gstage-\u003epgd, gstage-\u003epgd_levels - 1,\n+\t\t\t PTRS_PER_PTE \u003c\u003c kvm_riscv_gstage_pgd_xbits, \u0026processed);\n+\tfree_pages((unsigned long)gstage-\u003epgd, get_order(kvm_riscv_gstage_pgd_size));\n+}\n+\n bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage,\n \t\t\t\t gpa_t start, gpa_t size, bool may_block)\n {\n@@ -426,6 +457,12 @@ bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage,\n \tbool flush = false;\n \n \twhile (addr \u003c end) {\n+\t\t/* cond_resched_rwlock_write() may have let teardown detach us. */\n+\t\tif (!(gstage-\u003eflags \u0026 KVM_GSTAGE_FLAGS_LOCAL) \u0026\u0026\n+\t\t (!gstage-\u003ekvm-\u003earch.pgd ||\n+\t\t gstage-\u003epgd != gstage-\u003ekvm-\u003earch.pgd))\n+\t\t\tbreak;\n+\n \t\tfound_leaf = kvm_riscv_gstage_get_leaf(gstage, addr, \u0026ptep, \u0026ptep_level);\n \t\tret = gstage_level_to_page_size(gstage, ptep_level, \u0026page_size);\n \t\tif (ret)\ndiff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c\nindex 6035b5ec95039..2d7ad70ff7654 100644\n--- a/arch/riscv/kvm/mmu.c\n+++ b/arch/riscv/kvm/mmu.c\n@@ -26,12 +26,11 @@ static void mmu_wp_memory_region(struct kvm *kvm, int slot)\n \tphys_addr_t start = memslot-\u003ebase_gfn \u003c\u003c PAGE_SHIFT;\n \tphys_addr_t end = (memslot-\u003ebase_gfn + memslot-\u003enpages) \u003c\u003c PAGE_SHIFT;\n \tstruct kvm_gstage gstage;\n-\tbool flush;\n-\n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\tbool flush = false;\n \n \twrite_lock(\u0026kvm-\u003emmu_lock);\n-\tflush = kvm_riscv_gstage_wp_range(\u0026gstage, start, end);\n+\tif (kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\tflush = kvm_riscv_gstage_wp_range(\u0026gstage, start, end);\n \twrite_unlock(\u0026kvm-\u003emmu_lock);\n \tif (flush)\n \t\tkvm_flush_remote_tlbs_memslot(kvm, memslot);\n@@ -44,7 +43,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,\n \tpgprot_t prot;\n \tunsigned long pfn;\n \tphys_addr_t addr, end;\n-\tunsigned long pgd_levels = kvm-\u003earch.pgd_levels;\n+\tunsigned long pgd_levels = kvm_riscv_gstage_max_pgd_levels;\n \tstruct kvm_mmu_memory_cache pcache = {\n \t\t.gfp_custom = (in_atomic) ? GFP_ATOMIC | __GFP_ACCOUNT : 0,\n \t\t.gfp_zero = __GFP_ZERO,\n@@ -52,8 +51,6 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,\n \tstruct kvm_gstage_mapping map;\n \tstruct kvm_gstage gstage;\n \n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n-\n \tend = (gpa + size + PAGE_SIZE - 1) \u0026 PAGE_MASK;\n \tpfn = __phys_to_pfn(hpa);\n \tprot = pgprot_noncached(PAGE_WRITE);\n@@ -72,7 +69,10 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,\n \t\t\tgoto out;\n \n \t\twrite_lock(\u0026kvm-\u003emmu_lock);\n-\t\tret = kvm_riscv_gstage_set_pte(\u0026gstage, \u0026pcache, \u0026map);\n+\t\tif (kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\t\tret = kvm_riscv_gstage_set_pte(\u0026gstage, \u0026pcache, \u0026map);\n+\t\telse\n+\t\t\tret = -EFAULT;\n \t\twrite_unlock(\u0026kvm-\u003emmu_lock);\n \t\tif (ret)\n \t\t\tgoto out;\n@@ -88,12 +88,11 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,\n void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size)\n {\n \tstruct kvm_gstage gstage;\n-\tbool flush;\n-\n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\tbool flush = false;\n \n \twrite_lock(\u0026kvm-\u003emmu_lock);\n-\tflush = kvm_riscv_gstage_unmap_range(\u0026gstage, gpa, size, false);\n+\tif (kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\tflush = kvm_riscv_gstage_unmap_range(\u0026gstage, gpa, size, false);\n \twrite_unlock(\u0026kvm-\u003emmu_lock);\n \n \tif (flush)\n@@ -101,14 +100,12 @@ void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size)\n \t\t\t\t\t size \u003e\u003e PAGE_SHIFT);\n }\n \n-static bool need_topup_split_caches_or_resched(struct kvm *kvm, int count)\n+static bool need_topup_split_cache(struct kvm *kvm,\n+\t\t\t\t struct kvm_mmu_memory_cache *cache, int count)\n {\n-\tstruct kvm_mmu_memory_cache *cache;\n-\n \tif (need_resched() || rwlock_needbreak(\u0026kvm-\u003emmu_lock))\n \t\treturn true;\n \n-\tcache = \u0026kvm-\u003earch.pgd_split_page_cache;\n \treturn kvm_mmu_memory_cache_nr_free_objects(cache) \u003c count;\n }\n \n@@ -116,7 +113,8 @@ static bool mmu_split_huge_pages(struct kvm_gstage *gstage,\n \t\t\t\t phys_addr_t start, phys_addr_t end)\n {\n \tstruct kvm *kvm = gstage-\u003ekvm;\n-\tstruct kvm_mmu_memory_cache *pcache = \u0026kvm-\u003earch.pgd_split_page_cache;\n+\tstruct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };\n+\tstruct kvm_mmu_memory_cache *pcache = \u0026cache;\n \tphys_addr_t addr = ALIGN_DOWN(start, PMD_SIZE);\n \tphys_addr_t last_flush_gfn = addr \u003e\u003e PAGE_SHIFT;\n \tint count = gstage-\u003epgd_levels;\n@@ -126,7 +124,7 @@ static bool mmu_split_huge_pages(struct kvm_gstage *gstage,\n \tlockdep_assert_held_write(\u0026kvm-\u003emmu_lock);\n \n \twhile (addr \u003c end) {\n-\t\tif (need_topup_split_caches_or_resched(kvm, count)) {\n+\t\tif (need_topup_split_cache(kvm, pcache, count)) {\n \t\t\tif (flush) {\n \t\t\t\tkvm_flush_remote_tlbs_range(kvm, last_flush_gfn,\n \t\t\t\t\t (addr \u003e\u003e PAGE_SHIFT) - last_flush_gfn);\n@@ -141,19 +139,22 @@ static bool mmu_split_huge_pages(struct kvm_gstage *gstage,\n \t\t\tif (ret) {\n \t\t\t\tkvm_err(\"Failed to toup split page cache\\n\");\n \t\t\t\twrite_lock(\u0026kvm-\u003emmu_lock);\n-\t\t\t\treturn flush;\n+\t\t\t\tbreak;\n \t\t\t}\n \t\t\twrite_lock(\u0026kvm-\u003emmu_lock);\n \t\t}\n \n-\t\tif (!kvm-\u003earch.pgd)\n-\t\t\treturn flush;\n+\t\tif (!kvm-\u003earch.pgd || gstage-\u003epgd != kvm-\u003earch.pgd)\n+\t\t\tbreak;\n \n \t\tflush |= kvm_riscv_gstage_split_huge(gstage, pcache, addr, 0, false);\n \n \t\taddr += PMD_SIZE;\n \t}\n \n+\twrite_unlock(\u0026kvm-\u003emmu_lock);\n+\tkvm_mmu_free_memory_cache(pcache);\n+\twrite_lock(\u0026kvm-\u003emmu_lock);\n \treturn flush;\n }\n \n@@ -167,7 +168,8 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,\n \tphys_addr_t end = (base_gfn + __fls(mask) + 1) \u003c\u003c PAGE_SHIFT;\n \tstruct kvm_gstage gstage;\n \n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\tif (!kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\treturn;\n \n \tkvm_riscv_gstage_wp_pt_masked(\u0026gstage, base_gfn, mask);\n \n@@ -205,12 +207,11 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,\n \tgpa_t gpa = slot-\u003ebase_gfn \u003c\u003c PAGE_SHIFT;\n \tphys_addr_t size = slot-\u003enpages \u003c\u003c PAGE_SHIFT;\n \tstruct kvm_gstage gstage;\n-\tbool flush;\n-\n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\tbool flush = false;\n \n \twrite_lock(\u0026kvm-\u003emmu_lock);\n-\tflush = kvm_riscv_gstage_unmap_range(\u0026gstage, gpa, size, false);\n+\tif (kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\tflush = kvm_riscv_gstage_unmap_range(\u0026gstage, gpa, size, false);\n \twrite_unlock(\u0026kvm-\u003emmu_lock);\n \tif (flush)\n \t\tkvm_flush_remote_tlbs_range(kvm, gpa \u003e\u003e PAGE_SHIFT,\n@@ -224,12 +225,11 @@ static void mmu_split_memory_region(struct kvm *kvm, int slot)\n \tphys_addr_t start = memslot-\u003ebase_gfn \u003c\u003c PAGE_SHIFT;\n \tphys_addr_t end = (memslot-\u003ebase_gfn + memslot-\u003enpages) \u003c\u003c PAGE_SHIFT;\n \tstruct kvm_gstage gstage;\n-\tbool flush;\n-\n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\tbool flush = false;\n \n \twrite_lock(\u0026kvm-\u003emmu_lock);\n-\tflush = mmu_split_huge_pages(\u0026gstage, start, end);\n+\tif (kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\tflush = mmu_split_huge_pages(\u0026gstage, start, end);\n \twrite_unlock(\u0026kvm-\u003emmu_lock);\n \n \tif (flush)\n@@ -334,12 +334,10 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)\n \tstruct kvm_gstage gstage;\n \tbool flush;\n \n-\tif (!kvm-\u003earch.pgd)\n-\t\treturn false;\n-\n \tlockdep_assert_held_write(\u0026kvm-\u003emmu_lock);\n \n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\tif (!kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\treturn false;\n \tflush = kvm_riscv_gstage_unmap_range(\u0026gstage, range-\u003estart \u003c\u003c PAGE_SHIFT,\n \t\t\t\t\t (range-\u003eend - range-\u003estart) \u003c\u003c PAGE_SHIFT,\n \t\t\t\t\t range-\u003emay_block);\n@@ -356,12 +354,10 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n \tu64 size = (range-\u003eend - range-\u003estart) \u003c\u003c PAGE_SHIFT;\n \tstruct kvm_gstage gstage;\n \n-\tif (!kvm-\u003earch.pgd)\n-\t\treturn false;\n-\n \tWARN_ON(size != PAGE_SIZE \u0026\u0026 size != PMD_SIZE \u0026\u0026 size != PUD_SIZE);\n \n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\tif (!kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\treturn false;\n \tif (!kvm_riscv_gstage_get_leaf(\u0026gstage, range-\u003estart \u003c\u003c PAGE_SHIFT,\n \t\t\t\t \u0026ptep, \u0026ptep_level))\n \t\treturn false;\n@@ -376,12 +372,10 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)\n \tu64 size = (range-\u003eend - range-\u003estart) \u003c\u003c PAGE_SHIFT;\n \tstruct kvm_gstage gstage;\n \n-\tif (!kvm-\u003earch.pgd)\n-\t\treturn false;\n-\n \tWARN_ON(size != PAGE_SIZE \u0026\u0026 size != PMD_SIZE \u0026\u0026 size != PUD_SIZE);\n \n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n+\tif (!kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\treturn false;\n \tif (!kvm_riscv_gstage_get_leaf(\u0026gstage, range-\u003estart \u003c\u003c PAGE_SHIFT,\n \t\t\t\t \u0026ptep, \u0026ptep_level))\n \t\treturn false;\n@@ -563,12 +557,12 @@ static bool kvm_riscv_mmu_dirty_log_write_fault_fast(struct kvm *kvm,\n \tbool dirty_marked = false;\n \tbool ret;\n \n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n \tmmu_seq = kvm-\u003emmu_invalidate_seq;\n \n \tread_lock(\u0026kvm-\u003emmu_lock);\n \n-\tif (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) {\n+\tif (!kvm_riscv_gstage_init(\u0026gstage, kvm) ||\n+\t mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) {\n \t\tret = false;\n \t\tgoto out_unlock;\n \t}\n@@ -639,8 +633,6 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\n \tstruct kvm_gstage gstage;\n \tstruct page *page;\n \n-\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n-\n \t/* Setup initial state of output mapping */\n \tmemset(out_map, 0, sizeof(*out_map));\n \n@@ -649,7 +641,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\n \t\treturn 0;\n \n \t/* We need minimum second+third level pages */\n-\tret = kvm_mmu_topup_memory_cache(pcache, kvm-\u003earch.pgd_levels);\n+\tret = kvm_mmu_topup_memory_cache(pcache, kvm_riscv_gstage_max_pgd_levels);\n \tif (ret) {\n \t\tkvm_err(\"Failed to topup G-stage cache\\n\");\n \t\treturn ret;\n@@ -719,6 +711,11 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\n \n \twrite_lock(\u0026kvm-\u003emmu_lock);\n \n+\tret = -EFAULT;\n+\tif (!kvm_riscv_gstage_init(\u0026gstage, kvm))\n+\t\tgoto out_unlock;\n+\n+\tret = 0;\n \tif (mmu_invalidate_retry(kvm, mmu_seq))\n \t\tgoto out_unlock;\n \n@@ -764,7 +761,6 @@ int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)\n \tkvm-\u003earch.pgd = page_to_virt(pgd_page);\n \tkvm-\u003earch.pgd_phys = page_to_phys(pgd_page);\n \tkvm-\u003earch.pgd_levels = kvm_riscv_gstage_max_pgd_levels;\n-\tkvm-\u003earch.pgd_split_page_cache.gfp_zero = __GFP_ZERO;\n \n \treturn 0;\n }\n@@ -772,41 +768,45 @@ int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)\n void kvm_riscv_mmu_free_pgd(struct kvm *kvm)\n {\n \tstruct kvm_gstage gstage;\n-\tvoid *pgd = NULL;\n-\tbool flush = false;\n \n \twrite_lock(\u0026kvm-\u003emmu_lock);\n-\tif (kvm-\u003earch.pgd) {\n-\t\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n-\t\tflush = kvm_riscv_gstage_unmap_range(\u0026gstage, 0UL,\n-\t\t\tkvm_riscv_gstage_gpa_size(kvm-\u003earch.pgd_levels), false);\n-\t\tpgd = READ_ONCE(kvm-\u003earch.pgd);\n-\t\tkvm-\u003earch.pgd = NULL;\n-\t\tkvm-\u003earch.pgd_phys = 0;\n-\t\tkvm-\u003earch.pgd_levels = 0;\n+\tif (!kvm_riscv_gstage_init(\u0026gstage, kvm)) {\n+\t\twrite_unlock(\u0026kvm-\u003emmu_lock);\n+\t\treturn;\n \t}\n+\t/* Live walkers must acquire mmu_lock and check the active root. */\n+\tWRITE_ONCE(kvm-\u003earch.pgd, NULL);\n+\tkvm-\u003earch.pgd_phys = 0;\n+\tkvm-\u003earch.pgd_levels = 0;\n \twrite_unlock(\u0026kvm-\u003emmu_lock);\n \n-\tif (flush)\n-\t\tkvm_flush_remote_tlbs(kvm);\n-\n-\tif (pgd)\n-\t\tfree_pages((unsigned long)pgd, get_order(kvm_riscv_gstage_pgd_size));\n+\t/* Quiesce hardware users before freeing the detached page tables. */\n+\tkvm_make_all_cpus_request(kvm, KVM_REQ_OUTSIDE_GUEST_MODE);\n \n-\tkvm_mmu_free_memory_cache(\u0026kvm-\u003earch.pgd_split_page_cache);\n+\t/*\n+\t * Request an old-VMID HFENCE. Queue-full fallback can flush the current\n+\t * VMID, so the hardware quiescing above protects the tree lifetime.\n+\t */\n+\tkvm_riscv_hfence_gvma_vmid_all(kvm, -1UL, 0, gstage.vmid);\n+\tkvm_riscv_gstage_free(\u0026gstage);\n }\n \n void kvm_riscv_mmu_update_hgatp(struct kvm_vcpu *vcpu)\n {\n \tstruct kvm_arch *ka = \u0026vcpu-\u003ekvm-\u003earch;\n-\tunsigned long hgatp = kvm_riscv_gstage_mode(ka-\u003epgd_levels)\n-\t\t\t \u003c\u003c HGATP_MODE_SHIFT;\n-\n-\thgatp |= (READ_ONCE(ka-\u003evmid.vmid) \u003c\u003c HGATP_VMID_SHIFT) \u0026 HGATP_VMID;\n-\thgatp |= (ka-\u003epgd_phys \u003e\u003e PAGE_SHIFT) \u0026 HGATP_PPN;\n+\tunsigned long hgatp = 0;\n+\n+\t/* Serialize the root snapshot and HGATP install against root detach. */\n+\tread_lock(\u0026vcpu-\u003ekvm-\u003emmu_lock);\n+\tif (ka-\u003epgd) {\n+\t\thgatp = kvm_riscv_gstage_mode(ka-\u003epgd_levels) \u003c\u003c HGATP_MODE_SHIFT;\n+\t\thgatp |= (READ_ONCE(ka-\u003evmid.vmid) \u003c\u003c HGATP_VMID_SHIFT) \u0026 HGATP_VMID;\n+\t\thgatp |= (ka-\u003epgd_phys \u003e\u003e PAGE_SHIFT) \u0026 HGATP_PPN;\n+\t}\n \n \tncsr_write(CSR_HGATP, hgatp);\n \n \tif (!kvm_riscv_gstage_vmid_bits())\n \t\tkvm_riscv_local_hfence_gvma_all();\n+\tread_unlock(\u0026vcpu-\u003ekvm-\u003emmu_lock);\n }\ndiff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c\nindex e062ca19f9d8f..35bf948bd451f 100644\n--- a/arch/riscv/kvm/vcpu.c\n+++ b/arch/riscv/kvm/vcpu.c\n@@ -708,6 +708,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)\n *\n * Return: 1 if we should enter the guest\n *\t 0 if we should exit to userspace\n+ *\t negative error code if guest entry is no longer possible\n */\n static int kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu)\n {\n@@ -755,6 +756,12 @@ static int kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu)\n \t\t\treturn 0;\n \t}\n \n+\t/* A detached G-stage must not be reused after processing its HFENCE. */\n+\tif (!READ_ONCE(vcpu-\u003ekvm-\u003earch.pgd)) {\n+\t\tkvm_riscv_mmu_update_hgatp(vcpu);\n+\t\treturn -EIO;\n+\t}\n+\n \treturn 1;\n }\n \n@@ -980,7 +987,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)\n \t\t/* Update HVIP CSR for current CPU */\n \t\tkvm_riscv_update_hvip(vcpu);\n \n-\t\tif (kvm_riscv_gstage_vmid_ver_changed(\u0026vcpu-\u003ekvm-\u003earch.vmid) ||\n+\t\tif (!READ_ONCE(vcpu-\u003ekvm-\u003earch.pgd) ||\n+\t\t kvm_riscv_gstage_vmid_ver_changed(\u0026vcpu-\u003ekvm-\u003earch.vmid) ||\n \t\t kvm_request_pending(vcpu) ||\n \t\t xfer_to_guest_mode_work_pending()) {\n \t\t\tvcpu-\u003emode = OUTSIDE_GUEST_MODE;\ndiff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c\nindex 88e0c369b3544..da3308a3d0203 100644\n--- a/arch/riscv/kvm/vcpu_exit.c\n+++ b/arch/riscv/kvm/vcpu_exit.c\n@@ -90,7 +90,21 @@ unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu,\n \tregister unsigned long ttmp asm(\"a1\");\n \tunsigned long flags, val, tmp, old_stvec, old_hstatus;\n \n+\t/*\n+\t * Prevent G-stage teardown while HLV/HLVX can walk the page tables.\n+\t * The active-root check prevents a new walk from starting after detach.\n+\t */\n+\tread_lock(\u0026vcpu-\u003ekvm-\u003emmu_lock);\n+\tif (!vcpu-\u003ekvm-\u003earch.pgd) {\n+\t\tread_unlock(\u0026vcpu-\u003ekvm-\u003emmu_lock);\n+\t\ttrap-\u003escause = EXC_LOAD_GUEST_PAGE_FAULT;\n+\t\ttrap-\u003estval = guest_addr;\n+\t\treturn 0;\n+\t}\n \tlocal_irq_save(flags);\n+\t/* Publish the hardware walk before dropping the root lifetime lock. */\n+\tsmp_store_mb(vcpu-\u003emode, READING_SHADOW_PAGE_TABLES);\n+\tread_unlock(\u0026vcpu-\u003ekvm-\u003emmu_lock);\n \n \told_hstatus = csr_swap(CSR_HSTATUS, vcpu-\u003earch.guest_context.hstatus);\n \told_stvec = csr_swap(CSR_STVEC, (ulong)\u0026__kvm_riscv_unpriv_trap);\n@@ -146,6 +160,8 @@ unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu,\n \tcsr_write(CSR_STVEC, old_stvec);\n \tcsr_write(CSR_HSTATUS, old_hstatus);\n \n+\t/* Complete all HLV/HLVX accesses before publishing OUTSIDE. */\n+\tsmp_store_release(\u0026vcpu-\u003emode, OUTSIDE_GUEST_MODE);\n \tlocal_irq_restore(flags);\n \n \treturn val;\n"
} |
| 2/1 | 2026/09/22 15:10 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies files under arch/riscv/ (RISC-V KVM implementation), which is architecture-specific code structurally unreachable on the target architecture (amd64).",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 1769c085417983b2731a98a6efe2d07f46be3adf
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Sep 22 15:10:25 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/riscv/include/asm/kvm_gstage.h b/arch/riscv/include/asm/kvm_gstage.h
index aaf080ba1b77a..b5a9563754d08 100644
--- a/arch/riscv/include/asm/kvm_gstage.h
+++ b/arch/riscv/include/asm/kvm_gstage.h
@@ -87,6 +87,8 @@ bool kvm_riscv_gstage_wp_pt_masked(struct kvm_gstage *gstage, gfn_t base_gfn,
void kvm_riscv_gstage_mode_detect(void);
+void kvm_riscv_gstage_free(struct kvm_gstage *gstage);
+
static inline unsigned long kvm_riscv_gstage_mode(unsigned long pgd_levels)
{
switch (pgd_levels) {
@@ -104,13 +106,18 @@ static inline unsigned long kvm_riscv_gstage_mode(unsigned long pgd_levels)
}
}
-static inline void kvm_riscv_gstage_init(struct kvm_gstage *gstage, struct kvm *kvm)
+static inline bool kvm_riscv_gstage_init(struct kvm_gstage *gstage, struct kvm *kvm)
{
+ lockdep_assert_held(&kvm->mmu_lock);
+ if (!kvm->arch.pgd)
+ return false;
+
gstage->kvm = kvm;
gstage->flags = 0;
gstage->vmid = READ_ONCE(kvm->arch.vmid.vmid);
gstage->pgd = kvm->arch.pgd;
gstage->pgd_levels = kvm->arch.pgd_levels;
+ return true;
}
#endif
diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h
index a30600579231b..2ab999f751513 100644
--- a/arch/riscv/include/asm/kvm_host.h
+++ b/arch/riscv/include/asm/kvm_host.h
@@ -86,7 +86,6 @@ struct kvm_arch {
pgd_t *pgd;
phys_addr_t pgd_phys;
unsigned long pgd_levels;
- struct kvm_mmu_memory_cache pgd_split_page_cache;
/* Guest Timer */
struct kvm_guest_timer timer;
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index e5002cb9cbef1..5ab3c4ba5a6a4 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -414,6 +414,37 @@ bool kvm_riscv_gstage_op_pte(struct kvm_gstage *gstage, gpa_t addr,
return flush;
}
+/* The caller has detached this tree and quiesced its hardware users. */
+static void gstage_free_level(pte_t *ptep, u32 level, unsigned long nr_entries,
+ unsigned int *processed)
+{
+ pte_t pte, *child;
+ unsigned long i;
+
+ for (i = 0; i < nr_entries; i++) {
+ pte = ptep_get(&ptep[i]);
+ if (level && pte_val(pte) && !gstage_pte_leaf(&pte)) {
+ child = (pte_t *)gstage_pte_page_vaddr(pte);
+ gstage_free_level(child, level - 1, PTRS_PER_PTE, processed);
+ put_page(virt_to_page(child));
+ }
+ /* Leaf PFNs belong to the guest backing memory, not this tree. */
+ if (++*processed == PTRS_PER_PTE) {
+ *processed = 0;
+ cond_resched();
+ }
+ }
+}
+
+void kvm_riscv_gstage_free(struct kvm_gstage *gstage)
+{
+ unsigned int processed = 0;
+
+ gstage_free_level((pte_t *)gstage->pgd, gstage->pgd_levels - 1,
+ PTRS_PER_PTE << kvm_riscv_gstage_pgd_xbits, &processed);
+ free_pages((unsigned long)gstage->pgd, get_order(kvm_riscv_gstage_pgd_size));
+}
+
bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage,
gpa_t start, gpa_t size, bool may_block)
{
@@ -426,6 +457,12 @@ bool kvm_riscv_gstage_unmap_range(struct kvm_gstage *gstage,
bool flush = false;
while (addr < end) {
+ /* cond_resched_rwlock_write() may have let teardown detach us. */
+ if (!(gstage->flags & KVM_GSTAGE_FLAGS_LOCAL) &&
+ (!gstage->kvm->arch.pgd ||
+ gstage->pgd != gstage->kvm->arch.pgd))
+ break;
+
found_leaf = kvm_riscv_gstage_get_leaf(gstage, addr, &ptep, &ptep_level);
ret = gstage_level_to_page_size(gstage, ptep_level, &page_size);
if (ret)
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 6035b5ec95039..2d7ad70ff7654 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -26,12 +26,11 @@ static void mmu_wp_memory_region(struct kvm *kvm, int slot)
phys_addr_t start = memslot->base_gfn << PAGE_SHIFT;
phys_addr_t end = (memslot->base_gfn + memslot->npages) << PAGE_SHIFT;
struct kvm_gstage gstage;
- bool flush;
-
- kvm_riscv_gstage_init(&gstage, kvm);
+ bool flush = false;
write_lock(&kvm->mmu_lock);
- flush = kvm_riscv_gstage_wp_range(&gstage, start, end);
+ if (kvm_riscv_gstage_init(&gstage, kvm))
+ flush = kvm_riscv_gstage_wp_range(&gstage, start, end);
write_unlock(&kvm->mmu_lock);
if (flush)
kvm_flush_remote_tlbs_memslot(kvm, memslot);
@@ -44,7 +43,7 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
pgprot_t prot;
unsigned long pfn;
phys_addr_t addr, end;
- unsigned long pgd_levels = kvm->arch.pgd_levels;
+ unsigned long pgd_levels = kvm_riscv_gstage_max_pgd_levels;
struct kvm_mmu_memory_cache pcache = {
.gfp_custom = (in_atomic) ? GFP_ATOMIC | __GFP_ACCOUNT : 0,
.gfp_zero = __GFP_ZERO,
@@ -52,8 +51,6 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
struct kvm_gstage_mapping map;
struct kvm_gstage gstage;
- kvm_riscv_gstage_init(&gstage, kvm);
-
end = (gpa + size + PAGE_SIZE - 1) & PAGE_MASK;
pfn = __phys_to_pfn(hpa);
prot = pgprot_noncached(PAGE_WRITE);
@@ -72,7 +69,10 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
goto out;
write_lock(&kvm->mmu_lock);
- ret = kvm_riscv_gstage_set_pte(&gstage, &pcache, &map);
+ if (kvm_riscv_gstage_init(&gstage, kvm))
+ ret = kvm_riscv_gstage_set_pte(&gstage, &pcache, &map);
+ else
+ ret = -EFAULT;
write_unlock(&kvm->mmu_lock);
if (ret)
goto out;
@@ -88,12 +88,11 @@ int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size)
{
struct kvm_gstage gstage;
- bool flush;
-
- kvm_riscv_gstage_init(&gstage, kvm);
+ bool flush = false;
write_lock(&kvm->mmu_lock);
- flush = kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false);
+ if (kvm_riscv_gstage_init(&gstage, kvm))
+ flush = kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false);
write_unlock(&kvm->mmu_lock);
if (flush)
@@ -101,14 +100,12 @@ void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size)
size >> PAGE_SHIFT);
}
-static bool need_topup_split_caches_or_resched(struct kvm *kvm, int count)
+static bool need_topup_split_cache(struct kvm *kvm,
+ struct kvm_mmu_memory_cache *cache, int count)
{
- struct kvm_mmu_memory_cache *cache;
-
if (need_resched() || rwlock_needbreak(&kvm->mmu_lock))
return true;
- cache = &kvm->arch.pgd_split_page_cache;
return kvm_mmu_memory_cache_nr_free_objects(cache) < count;
}
@@ -116,7 +113,8 @@ static bool mmu_split_huge_pages(struct kvm_gstage *gstage,
phys_addr_t start, phys_addr_t end)
{
struct kvm *kvm = gstage->kvm;
- struct kvm_mmu_memory_cache *pcache = &kvm->arch.pgd_split_page_cache;
+ struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO };
+ struct kvm_mmu_memory_cache *pcache = &cache;
phys_addr_t addr = ALIGN_DOWN(start, PMD_SIZE);
phys_addr_t last_flush_gfn = addr >> PAGE_SHIFT;
int count = gstage->pgd_levels;
@@ -126,7 +124,7 @@ static bool mmu_split_huge_pages(struct kvm_gstage *gstage,
lockdep_assert_held_write(&kvm->mmu_lock);
while (addr < end) {
- if (need_topup_split_caches_or_resched(kvm, count)) {
+ if (need_topup_split_cache(kvm, pcache, count)) {
if (flush) {
kvm_flush_remote_tlbs_range(kvm, last_flush_gfn,
(addr >> PAGE_SHIFT) - last_flush_gfn);
@@ -141,19 +139,22 @@ static bool mmu_split_huge_pages(struct kvm_gstage *gstage,
if (ret) {
kvm_err("Failed to toup split page cache\n");
write_lock(&kvm->mmu_lock);
- return flush;
+ break;
}
write_lock(&kvm->mmu_lock);
}
- if (!kvm->arch.pgd)
- return flush;
+ if (!kvm->arch.pgd || gstage->pgd != kvm->arch.pgd)
+ break;
flush |= kvm_riscv_gstage_split_huge(gstage, pcache, addr, 0, false);
addr += PMD_SIZE;
}
+ write_unlock(&kvm->mmu_lock);
+ kvm_mmu_free_memory_cache(pcache);
+ write_lock(&kvm->mmu_lock);
return flush;
}
@@ -167,7 +168,8 @@ void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
phys_addr_t end = (base_gfn + __fls(mask) + 1) << PAGE_SHIFT;
struct kvm_gstage gstage;
- kvm_riscv_gstage_init(&gstage, kvm);
+ if (!kvm_riscv_gstage_init(&gstage, kvm))
+ return;
kvm_riscv_gstage_wp_pt_masked(&gstage, base_gfn, mask);
@@ -205,12 +207,11 @@ void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
gpa_t gpa = slot->base_gfn << PAGE_SHIFT;
phys_addr_t size = slot->npages << PAGE_SHIFT;
struct kvm_gstage gstage;
- bool flush;
-
- kvm_riscv_gstage_init(&gstage, kvm);
+ bool flush = false;
write_lock(&kvm->mmu_lock);
- flush = kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false);
+ if (kvm_riscv_gstage_init(&gstage, kvm))
+ flush = kvm_riscv_gstage_unmap_range(&gstage, gpa, size, false);
write_unlock(&kvm->mmu_lock);
if (flush)
kvm_flush_remote_tlbs_range(kvm, gpa >> PAGE_SHIFT,
@@ -224,12 +225,11 @@ static void mmu_split_memory_region(struct kvm *kvm, int slot)
phys_addr_t start = memslot->base_gfn << PAGE_SHIFT;
phys_addr_t end = (memslot->base_gfn + memslot->npages) << PAGE_SHIFT;
struct kvm_gstage gstage;
- bool flush;
-
- kvm_riscv_gstage_init(&gstage, kvm);
+ bool flush = false;
write_lock(&kvm->mmu_lock);
- flush = mmu_split_huge_pages(&gstage, start, end);
+ if (kvm_riscv_gstage_init(&gstage, kvm))
+ flush = mmu_split_huge_pages(&gstage, start, end);
write_unlock(&kvm->mmu_lock);
if (flush)
@@ -334,12 +334,10 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
struct kvm_gstage gstage;
bool flush;
- if (!kvm->arch.pgd)
- return false;
-
lockdep_assert_held_write(&kvm->mmu_lock);
- kvm_riscv_gstage_init(&gstage, kvm);
+ if (!kvm_riscv_gstage_init(&gstage, kvm))
+ return false;
flush = kvm_riscv_gstage_unmap_range(&gstage, range->start << PAGE_SHIFT,
(range->end - range->start) << PAGE_SHIFT,
range->may_block);
@@ -356,12 +354,10 @@ bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
u64 size = (range->end - range->start) << PAGE_SHIFT;
struct kvm_gstage gstage;
- if (!kvm->arch.pgd)
- return false;
-
WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE);
- kvm_riscv_gstage_init(&gstage, kvm);
+ if (!kvm_riscv_gstage_init(&gstage, kvm))
+ return false;
if (!kvm_riscv_gstage_get_leaf(&gstage, range->start << PAGE_SHIFT,
&ptep, &ptep_level))
return false;
@@ -376,12 +372,10 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
u64 size = (range->end - range->start) << PAGE_SHIFT;
struct kvm_gstage gstage;
- if (!kvm->arch.pgd)
- return false;
-
WARN_ON(size != PAGE_SIZE && size != PMD_SIZE && size != PUD_SIZE);
- kvm_riscv_gstage_init(&gstage, kvm);
+ if (!kvm_riscv_gstage_init(&gstage, kvm))
+ return false;
if (!kvm_riscv_gstage_get_leaf(&gstage, range->start << PAGE_SHIFT,
&ptep, &ptep_level))
return false;
@@ -563,12 +557,12 @@ static bool kvm_riscv_mmu_dirty_log_write_fault_fast(struct kvm *kvm,
bool dirty_marked = false;
bool ret;
- kvm_riscv_gstage_init(&gstage, kvm);
mmu_seq = kvm->mmu_invalidate_seq;
read_lock(&kvm->mmu_lock);
- if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) {
+ if (!kvm_riscv_gstage_init(&gstage, kvm) ||
+ mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) {
ret = false;
goto out_unlock;
}
@@ -639,8 +633,6 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
struct kvm_gstage gstage;
struct page *page;
- kvm_riscv_gstage_init(&gstage, kvm);
-
/* Setup initial state of output mapping */
memset(out_map, 0, sizeof(*out_map));
@@ -649,7 +641,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
return 0;
/* We need minimum second+third level pages */
- ret = kvm_mmu_topup_memory_cache(pcache, kvm->arch.pgd_levels);
+ ret = kvm_mmu_topup_memory_cache(pcache, kvm_riscv_gstage_max_pgd_levels);
if (ret) {
kvm_err("Failed to topup G-stage cache\n");
return ret;
@@ -719,6 +711,11 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
write_lock(&kvm->mmu_lock);
+ ret = -EFAULT;
+ if (!kvm_riscv_gstage_init(&gstage, kvm))
+ goto out_unlock;
+
+ ret = 0;
if (mmu_invalidate_retry(kvm, mmu_seq))
goto out_unlock;
@@ -764,7 +761,6 @@ int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
kvm->arch.pgd = page_to_virt(pgd_page);
kvm->arch.pgd_phys = page_to_phys(pgd_page);
kvm->arch.pgd_levels = kvm_riscv_gstage_max_pgd_levels;
- kvm->arch.pgd_split_page_cache.gfp_zero = __GFP_ZERO;
return 0;
}
@@ -772,41 +768,45 @@ int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
void kvm_riscv_mmu_free_pgd(struct kvm *kvm)
{
struct kvm_gstage gstage;
- void *pgd = NULL;
- bool flush = false;
write_lock(&kvm->mmu_lock);
- if (kvm->arch.pgd) {
- kvm_riscv_gstage_init(&gstage, kvm);
- flush = kvm_riscv_gstage_unmap_range(&gstage, 0UL,
- kvm_riscv_gstage_gpa_size(kvm->arch.pgd_levels), false);
- pgd = READ_ONCE(kvm->arch.pgd);
- kvm->arch.pgd = NULL;
- kvm->arch.pgd_phys = 0;
- kvm->arch.pgd_levels = 0;
+ if (!kvm_riscv_gstage_init(&gstage, kvm)) {
+ write_unlock(&kvm->mmu_lock);
+ return;
}
+ /* Live walkers must acquire mmu_lock and check the active root. */
+ WRITE_ONCE(kvm->arch.pgd, NULL);
+ kvm->arch.pgd_phys = 0;
+ kvm->arch.pgd_levels = 0;
write_unlock(&kvm->mmu_lock);
- if (flush)
- kvm_flush_remote_tlbs(kvm);
-
- if (pgd)
- free_pages((unsigned long)pgd, get_order(kvm_riscv_gstage_pgd_size));
+ /* Quiesce hardware users before freeing the detached page tables. */
+ kvm_make_all_cpus_request(kvm, KVM_REQ_OUTSIDE_GUEST_MODE);
- kvm_mmu_free_memory_cache(&kvm->arch.pgd_split_page_cache);
+ /*
+ * Request an old-VMID HFENCE. Queue-full fallback can flush the current
+ * VMID, so the hardware quiescing above protects the tree lifetime.
+ */
+ kvm_riscv_hfence_gvma_vmid_all(kvm, -1UL, 0, gstage.vmid);
+ kvm_riscv_gstage_free(&gstage);
}
void kvm_riscv_mmu_update_hgatp(struct kvm_vcpu *vcpu)
{
struct kvm_arch *ka = &vcpu->kvm->arch;
- unsigned long hgatp = kvm_riscv_gstage_mode(ka->pgd_levels)
- << HGATP_MODE_SHIFT;
-
- hgatp |= (READ_ONCE(ka->vmid.vmid) << HGATP_VMID_SHIFT) & HGATP_VMID;
- hgatp |= (ka->pgd_phys >> PAGE_SHIFT) & HGATP_PPN;
+ unsigned long hgatp = 0;
+
+ /* Serialize the root snapshot and HGATP install against root detach. */
+ read_lock(&vcpu->kvm->mmu_lock);
+ if (ka->pgd) {
+ hgatp = kvm_riscv_gstage_mode(ka->pgd_levels) << HGATP_MODE_SHIFT;
+ hgatp |= (READ_ONCE(ka->vmid.vmid) << HGATP_VMID_SHIFT) & HGATP_VMID;
+ hgatp |= (ka->pgd_phys >> PAGE_SHIFT) & HGATP_PPN;
+ }
ncsr_write(CSR_HGATP, hgatp);
if (!kvm_riscv_gstage_vmid_bits())
kvm_riscv_local_hfence_gvma_all();
+ read_unlock(&vcpu->kvm->mmu_lock);
}
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index e062ca19f9d8f..35bf948bd451f 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -708,6 +708,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
*
* Return: 1 if we should enter the guest
* 0 if we should exit to userspace
+ * negative error code if guest entry is no longer possible
*/
static int kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu)
{
@@ -755,6 +756,12 @@ static int kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu)
return 0;
}
+ /* A detached G-stage must not be reused after processing its HFENCE. */
+ if (!READ_ONCE(vcpu->kvm->arch.pgd)) {
+ kvm_riscv_mmu_update_hgatp(vcpu);
+ return -EIO;
+ }
+
return 1;
}
@@ -980,7 +987,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
/* Update HVIP CSR for current CPU */
kvm_riscv_update_hvip(vcpu);
- if (kvm_riscv_gstage_vmid_ver_changed(&vcpu->kvm->arch.vmid) ||
+ if (!READ_ONCE(vcpu->kvm->arch.pgd) ||
+ kvm_riscv_gstage_vmid_ver_changed(&vcpu->kvm->arch.vmid) ||
kvm_request_pending(vcpu) ||
xfer_to_guest_mode_work_pending()) {
vcpu->mode = OUTSIDE_GUEST_MODE;
diff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c
index 88e0c369b3544..da3308a3d0203 100644
--- a/arch/riscv/kvm/vcpu_exit.c
+++ b/arch/riscv/kvm/vcpu_exit.c
@@ -90,7 +90,21 @@ unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu,
register unsigned long ttmp asm("a1");
unsigned long flags, val, tmp, old_stvec, old_hstatus;
+ /*
+ * Prevent G-stage teardown while HLV/HLVX can walk the page tables.
+ * The active-root check prevents a new walk from starting after detach.
+ */
+ read_lock(&vcpu->kvm->mmu_lock);
+ if (!vcpu->kvm->arch.pgd) {
+ read_unlock(&vcpu->kvm->mmu_lock);
+ trap->scause = EXC_LOAD_GUEST_PAGE_FAULT;
+ trap->stval = guest_addr;
+ return 0;
+ }
local_irq_save(flags);
+ /* Publish the hardware walk before dropping the root lifetime lock. */
+ smp_store_mb(vcpu->mode, READING_SHADOW_PAGE_TABLES);
+ read_unlock(&vcpu->kvm->mmu_lock);
old_hstatus = csr_swap(CSR_HSTATUS, vcpu->arch.guest_context.hstatus);
old_stvec = csr_swap(CSR_STVEC, (ulong)&__kvm_riscv_unpriv_trap);
@@ -146,6 +160,8 @@ unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu,
csr_write(CSR_STVEC, old_stvec);
csr_write(CSR_HSTATUS, old_hstatus);
+ /* Complete all HLV/HLVX accesses before publishing OUTSIDE. */
+ smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE);
local_irq_restore(flags);
return val;
|
| 3/2 | 2026/09/22 15:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 10577 output: 17 thoughts: 803 Thoughts: **Fuzzing Verdict: Not Worthy** Okay, so I've got this git patch, and the question is whether it's worth throwing at syzkaller. The target architecture is amd64, which is key. Let's dig in. The patch touches files within `arch/riscv/`, specifically `include/asm/kvm_gstage.h`, `include/asm/kvm_host.h`, `kvm/gstage.c`, `kvm/mmu.c`, `kvm/vcpu.c`, and `kvm/vcpu_exit.c`. My spidey senses are tingling...all RISC-V specific code! And I mean *strictly* RISC-V. There's nothing in here that's bleeding into x86 or generic kernel areas. Now, let's reference the negative criteria. Section 2 says "WorthFuzzing=false" if we have unreachable execution paths, like driver teardown callbacks that are rare, or code paths exclusive to architectures *other* than our target. And boom! Here's where we strike gold. The target is amd64, but the modified code is *entirely* within `arch/riscv/`. Can `arch/riscv` code *ever* run on `amd64`? Absolutely not. It's architecture-specific stuff for RISC-V. It's not compiled, not linked, not executed on x86! Just to be absolutely certain, I ran a quick diff check, and every single file modified is nestled squarely within that `arch/riscv/` directory. So, according to the instructions, we can return "WorthFuzzing=false" because this falls directly under the second bullet: "Code paths exclusive to architectures other than the target architecture." It's a no-brainer. There's no `FocusSymbols` or `EnableConfigs` to consider, which is good. That makes the decision pretty straightforward. And just to be on the safe side, I’ll do a quick `git` check to double-check there aren't any other files modified. |
| 4/2 | 2026/09/22 15:10 | tool | grepper |
0mArgs:{
"Expression": "diff --git"
}Results: {
"Output": "Documentation/bpf/drgn.rst=99=For example, to print ``struct bpf_prog_aux`` for BPF program id 53077:\n--\nDocumentation/bpf/drgn.rst-103- % git diff\nDocumentation/bpf/drgn.rst:104: diff --git a/tools/bpf_inspect.py b/tools/bpf_inspect.py\nDocumentation/bpf/drgn.rst-105- index 650e228..aea2357 100755\n--\nDocumentation/dev-tools/checkuapi.rst=54=won't break userspace::\n--\nDocumentation/dev-tools/checkuapi.rst-69- * comp2_t is 24-bit with 5-bit base 2 exponent and 20 bit fraction\nDocumentation/dev-tools/checkuapi.rst:70: diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h\nDocumentation/dev-tools/checkuapi.rst-71- EOF\n--\nDocumentation/dev-tools/checkuapi.rst=310=Consider this change::\n--\nDocumentation/dev-tools/checkuapi.rst-312- cat \u003c\u003c 'EOF' | patch -l -p1\nDocumentation/dev-tools/checkuapi.rst:313: diff --git a/include/uapi/asm-generic/Kbuild b/include/uapi/asm-generic/Kbuild\nDocumentation/dev-tools/checkuapi.rst-314- index ebb180aac74e..a9c88b0a8b3b 100644\n--\nDocumentation/doc-guide/contributing.rst=84=looked like this::\n--\nDocumentation/doc-guide/contributing.rst-102-\nDocumentation/doc-guide/contributing.rst:103: diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c\nDocumentation/doc-guide/contributing.rst-104- index 57f6944d65a6..00c9b80b3d33 100644\n--\nDocumentation/mm/transhuge.rst=73=change::\nDocumentation/mm/transhuge.rst-74-\nDocumentation/mm/transhuge.rst:75:\tdiff --git a/mm/mremap.c b/mm/mremap.c\nDocumentation/mm/transhuge.rst-76-\t--- a/mm/mremap.c\n--\nDocumentation/translations/it_IT/doc-guide/contributing.rst=91=patch risultante era questa::\n--\nDocumentation/translations/it_IT/doc-guide/contributing.rst-109-\nDocumentation/translations/it_IT/doc-guide/contributing.rst:110: diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c\nDocumentation/translations/it_IT/doc-guide/contributing.rst-111- index 57f6944d65a6..00c9b80b3d33 100644\n--\nDocumentation/translations/zh_CN/doc-guide/contributing.rst=36=C代码编译器发出的警告常常会被视为误报,从而导致出现了旨在让编译器闭嘴的补丁。\n--\nDocumentation/translations/zh_CN/doc-guide/contributing.rst-86-\nDocumentation/translations/zh_CN/doc-guide/contributing.rst:87: diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c\nDocumentation/translations/zh_CN/doc-guide/contributing.rst-88- index 57f6944d65a6..00c9b80b3d33 100644\n--\nscripts/checkpatch.pl=2671=sub process {\n--\nscripts/checkpatch.pl-2844-\t\t ($line =~ /^rename (?:from|to) \\S+\\s*$/ ||\nscripts/checkpatch.pl:2845:\t\t $line =~ /^diff --git a\\/[\\w\\/\\.\\_\\-]+ b\\/\\S+\\s*$/))) {\nscripts/checkpatch.pl-2846-\t\t\t$is_patch = 1;\n--\nscripts/checkpatch.pl-2902-\t\t# extract the filename as it passes\nscripts/checkpatch.pl:2903:\t\tif ($line =~ /^diff --git.*?(\\S+)$/) {\nscripts/checkpatch.pl-2904-\t\t\t$realfile = $1;\n--\nscripts/get_maintainer.pl=552=foreach my $file (@ARGV) {\n--\nscripts/get_maintainer.pl-601-\t\tpush(@files, $filename);\nscripts/get_maintainer.pl:602:\t } elsif (m/^diff --git a\\/(\\S+) b\\/(\\S+)\\s*$/) {\nscripts/get_maintainer.pl-603-\t\tmy $filename1 = $1;\n"
} |
| 5/2 | 2026/09/22 15:10 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13142 output: 68 thoughts: 23 Thoughts: Here's my take on the thought process, as if it were my own: **Analyzing the Code Change** Okay, so I'm looking at this diff, and the first thing that jumps out is that *everything* is localized within the `arch/riscv/` directory. That's good; it's contained. No stray changes creeping into other architectures. That's the important first check to do when examining any proposed change. Now, based on the context, I think this looks like it’s setting up some new functionality and should be called `set-results`. |
| 6/2 | 2026/09/22 15:11 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch only modifies files under arch/riscv/ (RISC-V KVM implementation), which is architecture-specific code structurally unreachable on the target architecture (amd64).",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch only modifies files under arch/riscv/ (RISC-V KVM implementation), which is architecture-specific code structurally unreachable on the target architecture (amd64).",
"WorthFuzzing": false
} |
| 7/1 | 2026/09/22 15:11 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|