| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/14 13:08 | flow | patch-triage |
4mArgs:null Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_arch_vcpu_pre_fault_memory",
"kvm_riscv_gstage_map_page",
"kvm_riscv_mmu_map"
],
"KMSANReasoning": "The patch introduces support for KVM_CAP_PRE_FAULT_MEMORY on RISC-V. It adds `kvm_arch_vcpu_pre_fault_memory` which uses a local `struct kvm_gstage_mapping out_map` initialized to `{ 0 }`. The `out_map` is further zeroed out via `memset` in `kvm_riscv_mmu_map` before being populated. The `struct kvm_pre_fault_memory` passed from user space is fully initialized via `copy_from_user` (including padding) before being modified and copied back, so there is no kernel info-leak. All variables are properly initialized before use. Any potential bugs introduced by this patch (such as out-of-bounds accesses or locking issues) would be caught by KASAN and LOCKDEP. There are no uninitialized memory risks that require a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false,
"Reasoning": "The patch implements KVM_CAP_PRE_FAULT_MEMORY for RISC-V by adding kvm_arch_vcpu_pre_fault_memory and modifying the page mapping logic in kvm_riscv_gstage_map_page and kvm_riscv_mmu_map. This introduces new reachable ioctl code paths and modifies the core KVM MMU logic on RISC-V, which is highly relevant for fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/14 13:08 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 7fb8cf2c5d8c084cacad1933dc29a06ef91e5e3d\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Aug 14 13:08:14 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig\nindex ec2cee0a39e01..8ac209e8ac870 100644\n--- a/arch/riscv/kvm/Kconfig\n+++ b/arch/riscv/kvm/Kconfig\n@@ -28,6 +28,7 @@ config KVM\n \tselect KVM_COMMON\n \tselect KVM_GENERIC_DIRTYLOG_READ_PROTECT\n \tselect KVM_GENERIC_HARDWARE_ENABLING\n+\tselect KVM_GENERIC_PRE_FAULT_MEMORY\n \tselect KVM_MMIO\n \tselect VIRT_XFER_TO_GUEST_WORK\n \tselect SCHED_INFO\ndiff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c\nindex e5002cb9cbef1..6bd8b8fd6ceb2 100644\n--- a/arch/riscv/kvm/gstage.c\n+++ b/arch/riscv/kvm/gstage.c\n@@ -280,6 +280,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\n \t\t\t\t\t\t out_map-\u003elevel, true);\n \t\t} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {\n \t\t\tkvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);\n+\t\t\tout_map-\u003eaddr = ALIGN_DOWN(gpa, page_size);\n+\t\t\tout_map-\u003elevel = ptep_level;\n+\t\t\tout_map-\u003epte = ptep_get(ptep);\n \t\t\treturn 0;\n \t\t}\n \t}\ndiff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c\nindex 6035b5ec95039..33d4ba406b0de 100644\n--- a/arch/riscv/kvm/mmu.c\n+++ b/arch/riscv/kvm/mmu.c\n@@ -748,6 +748,48 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\n \treturn ret;\n }\n \n+long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\n+\t\t\t\t struct kvm_pre_fault_memory *range)\n+{\n+\tstruct kvm_gstage_mapping out_map = { 0 };\n+\tstruct kvm_memory_slot *memslot;\n+\tunsigned long map_size;\n+\tunsigned long hva;\n+\tgpa_t end;\n+\tgfn_t gfn;\n+\tint ret;\n+\n+\tgfn = gpa_to_gfn(range-\u003egpa);\n+\tmemslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);\n+\tif (!memslot)\n+\t\treturn -ENOENT;\n+\n+\thva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);\n+\tif (kvm_is_error_hva(hva))\n+\t\treturn -ENOENT;\n+\n+\tfor (;;) {\n+\t\tif (signal_pending(current))\n+\t\t\treturn -EINTR;\n+\n+\t\tif (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))\n+\t\t\treturn -EIO;\n+\n+\t\tcond_resched();\n+\t\tret = kvm_riscv_mmu_map(vcpu, memslot, range-\u003egpa, hva, false, \u0026out_map);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\n+\t\tif (pte_val(out_map.pte))\n+\t\t\tbreak;\n+\t}\n+\n+\tmap_size = PAGE_SIZE \u003c\u003c (out_map.level * kvm_riscv_gstage_index_bits);\n+\tend = ALIGN_DOWN(range-\u003egpa, map_size) + map_size;\n+\n+\treturn min_t(u64, range-\u003esize, end - range-\u003egpa);\n+}\n+\n int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)\n {\n \tstruct page *pgd_page;\ndiff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c\nindex a9f083feeb767..58500a19b33bd 100644\n--- a/arch/riscv/kvm/vm.c\n+++ b/arch/riscv/kvm/vm.c\n@@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)\n \tcase KVM_CAP_MP_STATE:\n \tcase KVM_CAP_IMMEDIATE_EXIT:\n \tcase KVM_CAP_SET_GUEST_DEBUG:\n+\tcase KVM_CAP_PRE_FAULT_MEMORY:\n \t\tr = 1;\n \t\tbreak;\n \tcase KVM_CAP_NR_VCPUS:\ndiff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm\nindex 6fc34e9bf8e1b..ac64ac92fd4b0 100644\n--- a/tools/testing/selftests/kvm/Makefile.kvm\n+++ b/tools/testing/selftests/kvm/Makefile.kvm\n@@ -225,6 +225,7 @@ TEST_GEN_PROGS_riscv += coalesced_io_test\n TEST_GEN_PROGS_riscv += dirty_log_perf_test\n TEST_GEN_PROGS_riscv += get-reg-list\n TEST_GEN_PROGS_riscv += mmu_stress_test\n+TEST_GEN_PROGS_riscv += pre_fault_memory_test\n TEST_GEN_PROGS_riscv += rseq_test\n TEST_GEN_PROGS_riscv += steal_time\n \ndiff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h\nindex e3acf2ae9881e..70487c8ed1553 100644\n--- a/tools/testing/selftests/kvm/include/riscv/processor.h\n+++ b/tools/testing/selftests/kvm/include/riscv/processor.h\n@@ -12,6 +12,9 @@\n #include \u003casm/vdso/processor.h\u003e\n #include \"kvm_util.h\"\n \n+#define PAGE_SHIFT\t\t12\n+#define PAGE_SIZE\t\tBIT_ULL(PAGE_SHIFT)\n+\n #define INSN_OPCODE_MASK\t0x007c\n #define INSN_OPCODE_SHIFT\t2\n #define INSN_OPCODE_SYSTEM\t28\n@@ -127,6 +130,9 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, exception_handl\n \n void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handler);\n \n+/* L4 index Bit[56:48] */\n+#define PGTBL_L4_INDEX_MASK\t\t\t0x01FF000000000000ULL\n+#define PGTBL_L4_INDEX_SHIFT\t\t\t48\n /* L3 index Bit[47:39] */\n #define PGTBL_L3_INDEX_MASK\t\t\t0x0000FF8000000000ULL\n #define PGTBL_L3_INDEX_SHIFT\t\t\t39\ndiff --git a/tools/testing/selftests/kvm/lib/riscv/processor.c b/tools/testing/selftests/kvm/lib/riscv/processor.c\nindex ded5429f34483..b4d41a4075536 100644\n--- a/tools/testing/selftests/kvm/lib/riscv/processor.c\n+++ b/tools/testing/selftests/kvm/lib/riscv/processor.c\n@@ -43,6 +43,7 @@ static u64 pte_index_mask[] = {\n \tPGTBL_L1_INDEX_MASK,\n \tPGTBL_L2_INDEX_MASK,\n \tPGTBL_L3_INDEX_MASK,\n+\tPGTBL_L4_INDEX_MASK,\n };\n \n static u32 pte_index_shift[] = {\n@@ -50,6 +51,7 @@ static u32 pte_index_shift[] = {\n \tPGTBL_L1_INDEX_SHIFT,\n \tPGTBL_L2_INDEX_SHIFT,\n \tPGTBL_L3_INDEX_SHIFT,\n+\tPGTBL_L4_INDEX_SHIFT,\n };\n \n static u64 pte_index(struct kvm_vm *vm, gva_t gva, int level)\n"
} |
| 2/1 | 2026/08/14 13:08 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_arch_vcpu_pre_fault_memory",
"kvm_riscv_gstage_map_page",
"kvm_riscv_mmu_map"
],
"Reasoning": "The patch implements KVM_CAP_PRE_FAULT_MEMORY for RISC-V by adding kvm_arch_vcpu_pre_fault_memory and modifying the page mapping logic in kvm_riscv_gstage_map_page and kvm_riscv_mmu_map. This introduces new reachable ioctl code paths and modifies the core KVM MMU logic on RISC-V, which is highly relevant for fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a '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: For your convenience, here is the diff of the changes:
commit 7fb8cf2c5d8c084cacad1933dc29a06ef91e5e3d
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 14 13:08:14 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a39e01..8ac209e8ac870 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -28,6 +28,7 @@ config KVM
select KVM_COMMON
select KVM_GENERIC_DIRTYLOG_READ_PROTECT
select KVM_GENERIC_HARDWARE_ENABLING
+ select KVM_GENERIC_PRE_FAULT_MEMORY
select KVM_MMIO
select VIRT_XFER_TO_GUEST_WORK
select SCHED_INFO
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index e5002cb9cbef1..6bd8b8fd6ceb2 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -280,6 +280,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
out_map->level, true);
} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {
kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);
+ out_map->addr = ALIGN_DOWN(gpa, page_size);
+ out_map->level = ptep_level;
+ out_map->pte = ptep_get(ptep);
return 0;
}
}
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 6035b5ec95039..33d4ba406b0de 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -748,6 +748,48 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
return ret;
}
+long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
+ struct kvm_pre_fault_memory *range)
+{
+ struct kvm_gstage_mapping out_map = { 0 };
+ struct kvm_memory_slot *memslot;
+ unsigned long map_size;
+ unsigned long hva;
+ gpa_t end;
+ gfn_t gfn;
+ int ret;
+
+ gfn = gpa_to_gfn(range->gpa);
+ memslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+ if (!memslot)
+ return -ENOENT;
+
+ hva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);
+ if (kvm_is_error_hva(hva))
+ return -ENOENT;
+
+ for (;;) {
+ if (signal_pending(current))
+ return -EINTR;
+
+ if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
+ return -EIO;
+
+ cond_resched();
+ ret = kvm_riscv_mmu_map(vcpu, memslot, range->gpa, hva, false, &out_map);
+ if (ret)
+ return ret;
+
+ if (pte_val(out_map.pte))
+ break;
+ }
+
+ map_size = PAGE_SIZE << (out_map.level * kvm_riscv_gstage_index_bits);
+ end = ALIGN_DOWN(range->gpa, map_size) + map_size;
+
+ return min_t(u64, range->size, end - range->gpa);
+}
+
int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
{
struct page *pgd_page;
diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
index a9f083feeb767..58500a19b33bd 100644
--- a/arch/riscv/kvm/vm.c
+++ b/arch/riscv/kvm/vm.c
@@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_MP_STATE:
case KVM_CAP_IMMEDIATE_EXIT:
case KVM_CAP_SET_GUEST_DEBUG:
+ case KVM_CAP_PRE_FAULT_MEMORY:
r = 1;
break;
case KVM_CAP_NR_VCPUS:
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 6fc34e9bf8e1b..ac64ac92fd4b0 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -225,6 +225,7 @@ TEST_GEN_PROGS_riscv += coalesced_io_test
TEST_GEN_PROGS_riscv += dirty_log_perf_test
TEST_GEN_PROGS_riscv += get-reg-list
TEST_GEN_PROGS_riscv += mmu_stress_test
+TEST_GEN_PROGS_riscv += pre_fault_memory_test
TEST_GEN_PROGS_riscv += rseq_test
TEST_GEN_PROGS_riscv += steal_time
diff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h
index e3acf2ae9881e..70487c8ed1553 100644
--- a/tools/testing/selftests/kvm/include/riscv/processor.h
+++ b/tools/testing/selftests/kvm/include/riscv/processor.h
@@ -12,6 +12,9 @@
#include <asm/vdso/processor.h>
#include "kvm_util.h"
+#define PAGE_SHIFT 12
+#define PAGE_SIZE BIT_ULL(PAGE_SHIFT)
+
#define INSN_OPCODE_MASK 0x007c
#define INSN_OPCODE_SHIFT 2
#define INSN_OPCODE_SYSTEM 28
@@ -127,6 +130,9 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, exception_handl
void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handler);
+/* L4 index Bit[56:48] */
+#define PGTBL_L4_INDEX_MASK 0x01FF000000000000ULL
+#define PGTBL_L4_INDEX_SHIFT 48
/* L3 index Bit[47:39] */
#define PGTBL_L3_INDEX_MASK 0x0000FF8000000000ULL
#define PGTBL_L3_INDEX_SHIFT 39
diff --git a/tools/testing/selftests/kvm/lib/riscv/processor.c b/tools/testing/selftests/kvm/lib/riscv/processor.c
index ded5429f34483..b4d41a4075536 100644
--- a/tools/testing/selftests/kvm/lib/riscv/processor.c
+++ b/tools/testing/selftests/kvm/lib/riscv/processor.c
@@ -43,6 +43,7 @@ static u64 pte_index_mask[] = {
PGTBL_L1_INDEX_MASK,
PGTBL_L2_INDEX_MASK,
PGTBL_L3_INDEX_MASK,
+ PGTBL_L4_INDEX_MASK,
};
static u32 pte_index_shift[] = {
@@ -50,6 +51,7 @@ static u32 pte_index_shift[] = {
PGTBL_L1_INDEX_SHIFT,
PGTBL_L2_INDEX_SHIFT,
PGTBL_L3_INDEX_SHIFT,
+ PGTBL_L4_INDEX_SHIFT,
};
static u64 pte_index(struct kvm_vm *vm, gva_t gva, int level)
|
| 3/2 | 2026/08/14 13:08 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 4141 output: 27 thoughts: 418 Thoughts: **Analyzing the RISC-V KVM Pre-Fault Memory Patch** Okay, so I've got this patch adding support for `KVM_CAP_PRE_FAULT_MEMORY` on RISC-V. It looks like it touches the usual suspects: `Kconfig`, `gstage.c`, `mmu.c`, and `vm.c` within the `arch/riscv/kvm/` directory. Plus some selftests, naturally. My immediate focus is on `arch/riscv/kvm/mmu.c`. The introduction of `kvm_arch_vcpu_pre_fault_memory` is what I'm looking for - that's the core of this new functionality, providing the RISC-V-specific implementation for pre-faulting memory in KVM. It also sets `KVM_GENERIC_PRE_FAULT_MEMORY` in `Kconfig`, which is important for the overall KVM integration. The question then is, how does userspace access this? Ah, right, through the `KVM_PRE_FAULT_MEMORY` ioctl. That ioctl becomes available when `KVM_CAP_PRE_FAULT_MEMORY` is supported, which is the point of this patch. I see that the patch adds `KVM_CAP_PRE_FAULT_MEMORY` to `kvm_vm_ioctl_check_extension` in `arch/riscv/kvm/vm.c`, confirming that. Now, let's make sure the dependencies are in place. Obviously, `CONFIG_KVM` is a hard requirement, and this is RISC-V, so that part is clear. I'm zeroing in on a couple of key functions now: `kvm_arch_vcpu_pre_fault_memory` (the new one) and the modified `kvm_riscv_gstage_map_page`. I need to confirm that `kvm_arch_vcpu_pre_fault_memory` isn't declared static and is properly exposed. Yes, it looks like it's called by the generic KVM code when `KVM_GENERIC_PRE_FAULT_MEMORY` is selected. That makes sense. I'll do a quick search of the entire tree to be absolutely sure there aren't any hidden calls. |
| 4/2 | 2026/08/14 13:08 | tool | grepper |
0mArgs:{
"Expression": "kvm_arch_vcpu_pre_fault_memory"
}Results: {
"Output": "arch/riscv/kvm/mmu.c=623=int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\n--\narch/riscv/kvm/mmu.c-750-\narch/riscv/kvm/mmu.c:751:long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\narch/riscv/kvm/mmu.c-752-\t\t\t\t struct kvm_pre_fault_memory *range)\n--\narch/s390/kvm/kvm-s390.c=5797=void kvm_arch_commit_memory_region(struct kvm *kvm,\n--\narch/s390/kvm/kvm-s390.c-5840-/**\narch/s390/kvm/kvm-s390.c:5841: * kvm_arch_vcpu_pre_fault_memory() -- pre-fault and link gmap dat tables\narch/s390/kvm/kvm-s390.c-5842- * @vcpu: the vcpu that shall appear to have generated the fault-in.\n--\narch/s390/kvm/kvm-s390.c-5859- */\narch/s390/kvm/kvm-s390.c:5860:long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, struct kvm_pre_fault_memory *range)\narch/s390/kvm/kvm-s390.c-5861-{\n--\narch/x86/kvm/mmu/mmu.c=4973=static int kvm_tdp_page_prefault(struct kvm_vcpu *vcpu, gpa_t gpa,\n--\narch/x86/kvm/mmu/mmu.c-5016-\narch/x86/kvm/mmu/mmu.c:5017:long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\narch/x86/kvm/mmu/mmu.c-5018-\t\t\t\t struct kvm_pre_fault_memory *range)\n--\narch/x86/kvm/vmx/tdx.c=1701=static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level,\n--\narch/x86/kvm/vmx/tdx.c-1713-\t/*\narch/x86/kvm/vmx/tdx.c:1714:\t * Ensure pre_fault_allowed is read by kvm_arch_vcpu_pre_fault_memory()\narch/x86/kvm/vmx/tdx.c-1715-\t * before kvm_tdx-\u003estate. Userspace must not be allowed to pre-fault\n--\ninclude/linux/kvm_host.h=2610=void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);\n--\ninclude/linux/kvm_host.h-2613-#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY\ninclude/linux/kvm_host.h:2614:long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\ninclude/linux/kvm_host.h-2615-\t\t\t\t struct kvm_pre_fault_memory *range);\n--\nvirt/kvm/kvm_main.c=4334=static int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\n--\nvirt/kvm/kvm_main.c-4358-\nvirt/kvm/kvm_main.c:4359:\t\tr = kvm_arch_vcpu_pre_fault_memory(vcpu, range);\nvirt/kvm/kvm_main.c-4360-\t\tif (WARN_ON_ONCE(r == 0 || r == -EIO))\n"
} |
| 5/2 | 2026/08/14 13:08 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6173 output: 25 thoughts: 64 Thoughts: Here's my take on the thought process, summarized as you requested: **Deconstructing the KVM Memory Fault Path** Okay, so I'm digging into how KVM handles memory faults before they actually happen – that pre-fault stage. Right, I remember seeing `kvm_arch_vcpu_pre_fault_memory` in the code, and I know that it's called from `kvm_vcpu_pre_fault_memory`. It's a standard pattern, right? Low-level architecture specific stuff being handled downstream from the more general KVM core function. Let's quickly refresh my memory on `kvm_vcpu_pre_fault_memory`. I need to figure out what that's actually doing and where exactly it fits in the fault handling pipeline. What are the key things it's responsible for? Gotta trace that code path and understand the conditions under which this pre-fault routine is invoked. Is it related to specific guest memory types, or page table operations? Gotta zoom in and get the details. |
| 6/2 | 2026/08/14 13:08 | tool | grepper |
0mArgs:{
"Expression": "kvm_vcpu_pre_fault_memory"
}Results: {
"Output": "virt/kvm/kvm_main.c=4308=static int kvm_vcpu_ioctl_get_stats_fd(struct kvm_vcpu *vcpu)\n--\nvirt/kvm/kvm_main.c-4333-#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY\nvirt/kvm/kvm_main.c:4334:static int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\nvirt/kvm/kvm_main.c-4335-\t\t\t\t struct kvm_pre_fault_memory *range)\n--\nvirt/kvm/kvm_main.c=4406=static long kvm_vcpu_ioctl(struct file *filp,\n--\nvirt/kvm/kvm_main.c-4638-\t\t\tbreak;\nvirt/kvm/kvm_main.c:4639:\t\tr = kvm_vcpu_pre_fault_memory(vcpu, \u0026range);\nvirt/kvm/kvm_main.c-4640-\t\t/* Pass back leftover range. */\n"
} |
| 7/2 | 2026/08/14 13:08 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6772 output: 26 thoughts: 114 Thoughts: **Analyzing the KVM Pre-Fault Memory Patch** Okay, so this is called from `kvm_vcpu_ioctl` specifically for `KVM_PRE_FAULT_MEMORY`. Interesting. The core of this patch seems to be introducing `kvm_arch_vcpu_pre_fault_memory` within `arch/riscv/kvm/mmu.c`. Right, that's where the architectural specifics are handled. And then, there's a modification to `kvm_riscv_gstage_map_page` in `arch/riscv/kvm/gstage.c`. That's where things get interesting in RISC-V KVM. It's likely involved in the guest page table management, especially given "gstage" and the context of pre-faulting. Let's dig into `kvm_riscv_gstage_map_page` now; that's where the mechanics of this pre-faulting likely come together. |
| 8/2 | 2026/08/14 13:08 | tool | grepper |
0mArgs:{
"Expression": "kvm_riscv_gstage_map_page"
}Results: {
"Output": "arch/riscv/include/asm/kvm_gstage.h=57=bool kvm_riscv_gstage_try_update_pte(struct kvm_gstage *gstage, u32 level,\n--\narch/riscv/include/asm/kvm_gstage.h-60-\narch/riscv/include/asm/kvm_gstage.h:61:int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\narch/riscv/include/asm/kvm_gstage.h-62-\t\t\t struct kvm_mmu_memory_cache *pcache,\n--\narch/riscv/kvm/gstage.c=186=static void kvm_riscv_gstage_update_pte_prot(struct kvm_gstage *gstage, u32 level,\n--\narch/riscv/kvm/gstage.c-206-\narch/riscv/kvm/gstage.c:207:int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\narch/riscv/kvm/gstage.c-208-\t\t\t struct kvm_mmu_memory_cache *pcache,\n--\narch/riscv/kvm/mmu.c=623=int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\n--\narch/riscv/kvm/mmu.c-734-\t\tmark_page_dirty_in_slot(kvm, memslot, gfn);\narch/riscv/kvm/mmu.c:735:\t\tret = kvm_riscv_gstage_map_page(\u0026gstage, pcache, gpa, hfn \u003c\u003c PAGE_SHIFT,\narch/riscv/kvm/mmu.c-736-\t\t\t\t\t\tvma_pagesize, false, true, out_map);\narch/riscv/kvm/mmu.c-737-\t} else {\narch/riscv/kvm/mmu.c:738:\t\tret = kvm_riscv_gstage_map_page(\u0026gstage, pcache, gpa, hfn \u003c\u003c PAGE_SHIFT,\narch/riscv/kvm/mmu.c-739-\t\t\t\t\t\tvma_pagesize, true, true, out_map);\n"
} |
| 9/2 | 2026/08/14 13:08 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7722 output: 24 thoughts: 109 Thoughts: **Tracing the KVM/RISC-V Page Mapping Flow** Okay, so I need to understand this page mapping process within the KVM/RISC-V environment. From the code, it looks like `kvm_riscv_gstage_map_page` is the key function here, and the first place it's called from is `kvm_riscv_mmu_map`. That seems logical, as `kvm_riscv_gstage_map_page` probably handles the actual Guest Stage mapping. Now, where is *`kvm_riscv_mmu_map`* being called from? Ah, looks like `kvm_arch_vcpu_pre_fault_memory`, and also likely `kvm_riscv_vcpu_fault`. That makes sense; we'd need to map pages when there's a memory access fault or during pre-faulting to anticipate future access. To really get a grasp on the complete process, I should probably delve into the `kvm_riscv_mmu_map` function itself. Let's start there to understand the context and how it orchestrates the `kvm_riscv_gstage_map_page` call. |
| 10/2 | 2026/08/14 13:09 | tool | grepper |
0mArgs:{
"Expression": "kvm_riscv_mmu_map"
}Results: {
"Output": "arch/riscv/include/asm/kvm_mmu.h=13=void kvm_riscv_mmu_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size);\narch/riscv/include/asm/kvm_mmu.h:14:int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\narch/riscv/include/asm/kvm_mmu.h-15-\t\t gpa_t gpa, unsigned long hva, bool is_write,\n--\narch/riscv/kvm/mmu.c=552=static bool kvm_riscv_mmu_dirty_log_write_fault_fast(struct kvm *kvm,\n--\narch/riscv/kvm/mmu.c-622-\narch/riscv/kvm/mmu.c:623:int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\narch/riscv/kvm/mmu.c-624-\t\t gpa_t gpa, unsigned long hva, bool is_write,\n--\narch/riscv/kvm/mmu.c=751=long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\n--\narch/riscv/kvm/mmu.c-778-\t\tcond_resched();\narch/riscv/kvm/mmu.c:779:\t\tret = kvm_riscv_mmu_map(vcpu, memslot, range-\u003egpa, hva, false, \u0026out_map);\narch/riscv/kvm/mmu.c-780-\t\tif (ret)\n--\narch/riscv/kvm/vcpu_exit.c=16=static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,\n--\narch/riscv/kvm/vcpu_exit.c-66-\narch/riscv/kvm/vcpu_exit.c:67:\tret = kvm_riscv_mmu_map(vcpu, memslot, fault_addr, hva,\narch/riscv/kvm/vcpu_exit.c-68-\t\t\t\t(trap-\u003escause == EXC_STORE_GUEST_PAGE_FAULT) ? true : false,\n"
} |
| 11/2 | 2026/08/14 13:09 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8734 output: 173 thoughts: 336 Thoughts: **My Initial Assessment of the KVM RISC-V Page Fault Handler Patch** Okay, so I've got this patch for KVM RISC-V, and my initial scan suggests it's centered around a new feature, `kvm_arch_vcpu_pre_fault_memory`. My primary concern is that it seems to be modifying the core page fault handler, `kvm_riscv_mmu_map`, specifically by altering `kvm_riscv_gstage_map_page` to populate `out_map`. This definitely has fuzzing potential written all over it. Any change in the memory mapping logic, especially within a critical path like a page fault handler, warrants immediate and thorough testing. The key symbols I'll be focusing on here are `kvm_arch_vcpu_pre_fault_memory`, `kvm_riscv_gstage_map_page`, and, of course, the main handler `kvm_riscv_mmu_map`. I need to understand exactly how this new pre-fault mechanism integrates into the existing page fault process. I need to quickly figure out the configuration requirements. Looks like `KVM` is the main configuration option I need to ensure is enabled. A quick search within the patch confirmed that `select KVM_GENERIC_PRE_FAULT_MEMORY` is added to `arch/riscv/kvm/Kconfig`. Now, I need to confirm that those crucial functions aren't hidden by being declared as static. I need to be able to hook into them for debugging and analysis. Good, `kvm_arch_vcpu_pre_fault_memory` is indeed a public function with a `long` return type. And `kvm_riscv_gstage_map_page` is an `int` and not static. And, finally, `kvm_riscv_mmu_map` is an `int` as well, so no issues there. Alright, I'll record these findings. This patch demands some serious scrutiny. |
| 12/2 | 2026/08/14 13:09 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_arch_vcpu_pre_fault_memory",
"kvm_riscv_gstage_map_page",
"kvm_riscv_mmu_map"
],
"Reasoning": "The patch implements KVM_CAP_PRE_FAULT_MEMORY for RISC-V by adding kvm_arch_vcpu_pre_fault_memory and modifying the page mapping logic in kvm_riscv_gstage_map_page and kvm_riscv_mmu_map. This introduces new reachable ioctl code paths and modifies the core KVM MMU logic on RISC-V, which is highly relevant for fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"KVM"
],
"FocusSymbols": [
"kvm_arch_vcpu_pre_fault_memory",
"kvm_riscv_gstage_map_page",
"kvm_riscv_mmu_map"
],
"Reasoning": "The patch implements KVM_CAP_PRE_FAULT_MEMORY for RISC-V by adding kvm_arch_vcpu_pre_fault_memory and modifying the page mapping logic in kvm_riscv_gstage_map_page and kvm_riscv_mmu_map. This introduces new reachable ioctl code paths and modifies the core KVM MMU logic on RISC-V, which is highly relevant for fuzzing.",
"WorthFuzzing": true
} |
| 13/1 | 2026/08/14 13:09 | action | If |
2mArgs:{
"WorthFuzzing": true
}Results: null |
| 14/2 | 2026/08/14 13:09 | agent | kmsan-evaluator |
2mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch introduces support for KVM_CAP_PRE_FAULT_MEMORY on RISC-V. It adds `kvm_arch_vcpu_pre_fault_memory` which uses a local `struct kvm_gstage_mapping out_map` initialized to `{ 0 }`. The `out_map` is further zeroed out via `memset` in `kvm_riscv_mmu_map` before being populated. The `struct kvm_pre_fault_memory` passed from user space is fully initialized via `copy_from_user` (including padding) before being modified and copied back, so there is no kernel info-leak. All variables are properly initialized before use. Any potential bugs introduced by this patch (such as out-of-bounds accesses or locking issues) would be caught by KASAN and LOCKDEP. There are no uninitialized memory risks that require a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
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: For your convenience, here is the diff of the changes:
commit 7fb8cf2c5d8c084cacad1933dc29a06ef91e5e3d
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 14 13:08:14 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a39e01..8ac209e8ac870 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -28,6 +28,7 @@ config KVM
select KVM_COMMON
select KVM_GENERIC_DIRTYLOG_READ_PROTECT
select KVM_GENERIC_HARDWARE_ENABLING
+ select KVM_GENERIC_PRE_FAULT_MEMORY
select KVM_MMIO
select VIRT_XFER_TO_GUEST_WORK
select SCHED_INFO
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index e5002cb9cbef1..6bd8b8fd6ceb2 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -280,6 +280,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
out_map->level, true);
} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {
kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);
+ out_map->addr = ALIGN_DOWN(gpa, page_size);
+ out_map->level = ptep_level;
+ out_map->pte = ptep_get(ptep);
return 0;
}
}
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 6035b5ec95039..33d4ba406b0de 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -748,6 +748,48 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
return ret;
}
+long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
+ struct kvm_pre_fault_memory *range)
+{
+ struct kvm_gstage_mapping out_map = { 0 };
+ struct kvm_memory_slot *memslot;
+ unsigned long map_size;
+ unsigned long hva;
+ gpa_t end;
+ gfn_t gfn;
+ int ret;
+
+ gfn = gpa_to_gfn(range->gpa);
+ memslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+ if (!memslot)
+ return -ENOENT;
+
+ hva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);
+ if (kvm_is_error_hva(hva))
+ return -ENOENT;
+
+ for (;;) {
+ if (signal_pending(current))
+ return -EINTR;
+
+ if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
+ return -EIO;
+
+ cond_resched();
+ ret = kvm_riscv_mmu_map(vcpu, memslot, range->gpa, hva, false, &out_map);
+ if (ret)
+ return ret;
+
+ if (pte_val(out_map.pte))
+ break;
+ }
+
+ map_size = PAGE_SIZE << (out_map.level * kvm_riscv_gstage_index_bits);
+ end = ALIGN_DOWN(range->gpa, map_size) + map_size;
+
+ return min_t(u64, range->size, end - range->gpa);
+}
+
int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
{
struct page *pgd_page;
diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
index a9f083feeb767..58500a19b33bd 100644
--- a/arch/riscv/kvm/vm.c
+++ b/arch/riscv/kvm/vm.c
@@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_MP_STATE:
case KVM_CAP_IMMEDIATE_EXIT:
case KVM_CAP_SET_GUEST_DEBUG:
+ case KVM_CAP_PRE_FAULT_MEMORY:
r = 1;
break;
case KVM_CAP_NR_VCPUS:
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 6fc34e9bf8e1b..ac64ac92fd4b0 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -225,6 +225,7 @@ TEST_GEN_PROGS_riscv += coalesced_io_test
TEST_GEN_PROGS_riscv += dirty_log_perf_test
TEST_GEN_PROGS_riscv += get-reg-list
TEST_GEN_PROGS_riscv += mmu_stress_test
+TEST_GEN_PROGS_riscv += pre_fault_memory_test
TEST_GEN_PROGS_riscv += rseq_test
TEST_GEN_PROGS_riscv += steal_time
diff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h
index e3acf2ae9881e..70487c8ed1553 100644
--- a/tools/testing/selftests/kvm/include/riscv/processor.h
+++ b/tools/testing/selftests/kvm/include/riscv/processor.h
@@ -12,6 +12,9 @@
#include <asm/vdso/processor.h>
#include "kvm_util.h"
+#define PAGE_SHIFT 12
+#define PAGE_SIZE BIT_ULL(PAGE_SHIFT)
+
#define INSN_OPCODE_MASK 0x007c
#define INSN_OPCODE_SHIFT 2
#define INSN_OPCODE_SYSTEM 28
@@ -127,6 +130,9 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, exception_handl
void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handler);
+/* L4 index Bit[56:48] */
+#define PGTBL_L4_INDEX_MASK 0x01FF000000000000ULL
+#define PGTBL_L4_INDEX_SHIFT 48
/* L3 index Bit[47:39] */
#define PGTBL_L3_INDEX_MASK 0x0000FF8000000000ULL
#define PGTBL_L3_INDEX_SHIFT 39
diff --git a/tools/testing/selftests/kvm/lib/riscv/processor.c b/tools/testing/selftests/kvm/lib/riscv/processor.c
index ded5429f34483..b4d41a4075536 100644
--- a/tools/testing/selftests/kvm/lib/riscv/processor.c
+++ b/tools/testing/selftests/kvm/lib/riscv/processor.c
@@ -43,6 +43,7 @@ static u64 pte_index_mask[] = {
PGTBL_L1_INDEX_MASK,
PGTBL_L2_INDEX_MASK,
PGTBL_L3_INDEX_MASK,
+ PGTBL_L4_INDEX_MASK,
};
static u32 pte_index_shift[] = {
@@ -50,6 +51,7 @@ static u32 pte_index_shift[] = {
PGTBL_L1_INDEX_SHIFT,
PGTBL_L2_INDEX_SHIFT,
PGTBL_L3_INDEX_SHIFT,
+ PGTBL_L4_INDEX_SHIFT,
};
static u64 pte_index(struct kvm_vm *vm, gva_t gva, int level)
|
| 15/3 | 2026/08/14 13:09 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 4138 output: 42 thoughts: 673 Thoughts: **KMSAN Fuzzing Session Justification** Okay, so this patch introduces support for `KVM_CAP_PRE_FAULT_MEMORY` on RISC-V. My immediate thought is, does this warrant a dedicated KMSAN fuzzing session? Let's break it down. It touches a few files: `Kconfig`, `gstage.c`, `mmu.c`, `vm.c` – the usual suspects for this kind of feature – and some selftests. Seems like a reasonable scope, nothing obviously huge that jumps out. Let's dive into the core logic, specifically `arch/riscv/kvm/mmu.c`. The new function `kvm_arch_vcpu_pre_fault_memory` is the likely entry point. Looks like it's handling the pre-faulting of memory for a virtual CPU. The important bit here is that `kvm_riscv_mmu_map` is called and seems to be the function that actually does the work. Inside the new function, `out_map` is initialized to zero, and passed as a pointer to `kvm_riscv_mmu_map`. I need to ensure that the initialization is correct. If the initialization is incorrect, then fuzzing becomes much more important, because the new function is a new code path. This could be exploitable, if the new variable `out_map` isn't initialized, if it is not handled by the `kvm_riscv_mmu_map` function, this could be a security vulnerability that would be discovered with a fuzzing session. I need to understand what `kvm_riscv_mmu_map` does with that zeroed `out_map` struct. I'll need to scrutinize the `kvm_riscv_mmu_map` implementation. If the initialization is critical for proper operation, KMSAN would likely pick up any use-before-initialization issues in this function. I'll check that function now. |
| 16/3 | 2026/08/14 13:10 | tool | grepper |
0mArgs:{
"Expression": "kvm_riscv_mmu_map",
"PathPrefix": "arch/riscv/kvm/mmu.c"
}Results: {
"Output": "arch/riscv/kvm/mmu.c=552=static bool kvm_riscv_mmu_dirty_log_write_fault_fast(struct kvm *kvm,\n--\narch/riscv/kvm/mmu.c-622-\narch/riscv/kvm/mmu.c:623:int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\narch/riscv/kvm/mmu.c-624-\t\t gpa_t gpa, unsigned long hva, bool is_write,\n--\narch/riscv/kvm/mmu.c=751=long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\n--\narch/riscv/kvm/mmu.c-778-\t\tcond_resched();\narch/riscv/kvm/mmu.c:779:\t\tret = kvm_riscv_mmu_map(vcpu, memslot, range-\u003egpa, hva, false, \u0026out_map);\narch/riscv/kvm/mmu.c-780-\t\tif (ret)\n"
} |
| 17/3 | 2026/08/14 13:10 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 5593 output: 41 thoughts: 19 Thoughts: Here's a summary of my thought process, as if I were analyzing the `kvm_riscv_mmu_map` implementation: **Diving into kvm_riscv_mmu_map** Okay, time to dissect the `kvm_riscv_mmu_map` implementation. Let's see what's happening under the hood. I need to understand how KVM interacts with the RISC-V MMU for memory mapping. I'm expecting this function to be the core of translating guest virtual addresses to host physical addresses, handling page table walks, and setting up the necessary hardware mappings within the virtual machine. I'll be looking for where it interacts with the RISC-V specific instruction set extensions and how it might handle different memory access types and protection levels. I'll need to pay close attention to the potential performance bottlenecks, any locking mechanisms used for concurrency, and how it handles potential TLB invalidations to make sure everything is coherent. Time to get into the details! |
| 18/3 | 2026/08/14 13:10 | tool | read-file |
0mArgs:{
"File": "arch/riscv/kvm/mmu.c",
"FirstLine": 623,
"LineCount": 100
}Results: {
"Contents": " 623:\tint kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\n 624:\t\t\t gpa_t gpa, unsigned long hva, bool is_write,\n 625:\t\t\t struct kvm_gstage_mapping *out_map)\n 626:\t{\n 627:\t\tint ret;\n 628:\t\tkvm_pfn_t hfn;\n 629:\t\tbool is_hugetlb;\n 630:\t\tbool writable;\n 631:\t\tunsigned int vma_pageshift;\n 632:\t\tgfn_t gfn = gpa \u003e\u003e PAGE_SHIFT;\n 633:\t\tstruct vm_area_struct *vma;\n 634:\t\tstruct kvm *kvm = vcpu-\u003ekvm;\n 635:\t\tstruct kvm_mmu_memory_cache *pcache = \u0026vcpu-\u003earch.mmu_page_cache;\n 636:\t\tbool logging = kvm_slot_dirty_track_enabled(memslot) \u0026\u0026\n 637:\t\t\t !(memslot-\u003eflags \u0026 KVM_MEM_READONLY);\n 638:\t\tunsigned long vma_pagesize, mmu_seq;\n 639:\t\tstruct kvm_gstage gstage;\n 640:\t\tstruct page *page;\n 641:\t\n 642:\t\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n 643:\t\n 644:\t\t/* Setup initial state of output mapping */\n 645:\t\tmemset(out_map, 0, sizeof(*out_map));\n 646:\t\n 647:\t\tif (is_write \u0026\u0026 logging \u0026\u0026\n 648:\t\t kvm_riscv_mmu_dirty_log_write_fault_fast(kvm, memslot, gpa, out_map))\n 649:\t\t\treturn 0;\n 650:\t\n 651:\t\t/* We need minimum second+third level pages */\n 652:\t\tret = kvm_mmu_topup_memory_cache(pcache, kvm-\u003earch.pgd_levels);\n 653:\t\tif (ret) {\n 654:\t\t\tkvm_err(\"Failed to topup G-stage cache\\n\");\n 655:\t\t\treturn ret;\n 656:\t\t}\n 657:\t\n 658:\t\tmmap_read_lock(current-\u003emm);\n 659:\t\n 660:\t\tvma = vma_lookup(current-\u003emm, hva);\n 661:\t\tif (unlikely(!vma)) {\n 662:\t\t\tkvm_err(\"Failed to find VMA for hva 0x%lx\\n\", hva);\n 663:\t\t\tmmap_read_unlock(current-\u003emm);\n 664:\t\t\treturn -EFAULT;\n 665:\t\t}\n 666:\t\n 667:\t\tis_hugetlb = is_vm_hugetlb_page(vma);\n 668:\t\tif (is_hugetlb)\n 669:\t\t\tvma_pageshift = huge_page_shift(hstate_vma(vma));\n 670:\t\telse\n 671:\t\t\tvma_pageshift = PAGE_SHIFT;\n 672:\t\tvma_pagesize = 1ULL \u003c\u003c vma_pageshift;\n 673:\t\tif (logging || (vma-\u003evm_flags \u0026 VM_PFNMAP))\n 674:\t\t\tvma_pagesize = PAGE_SIZE;\n 675:\t\telse if (is_hugetlb)\n 676:\t\t\tvma_pagesize = hugetlb_mapping_size(memslot, hva, vma_pagesize);\n 677:\t\n 678:\t\t/*\n 679:\t\t * For hugetlb mappings, vma_pagesize might have been reduced from the\n 680:\t\t * VMA size to a smaller safe mapping size.\n 681:\t\t */\n 682:\t\tif (vma_pagesize == PMD_SIZE || vma_pagesize == PUD_SIZE)\n 683:\t\t\tgfn = ALIGN_DOWN(gpa, vma_pagesize) \u003e\u003e PAGE_SHIFT;\n 684:\t\n 685:\t\t/*\n 686:\t\t * Read mmu_invalidate_seq so that KVM can detect if the results of\n 687:\t\t * vma_lookup() or __kvm_faultin_pfn() become stale prior to acquiring\n 688:\t\t * kvm-\u003emmu_lock.\n 689:\t\t *\n 690:\t\t * Rely on mmap_read_unlock() for an implicit smp_rmb(), which pairs\n 691:\t\t * with the smp_wmb() in kvm_mmu_invalidate_end().\n 692:\t\t */\n 693:\t\tmmu_seq = kvm-\u003emmu_invalidate_seq;\n 694:\t\tmmap_read_unlock(current-\u003emm);\n 695:\t\n 696:\t\tif (vma_pagesize != PUD_SIZE \u0026\u0026\n 697:\t\t vma_pagesize != PMD_SIZE \u0026\u0026\n 698:\t\t vma_pagesize != PAGE_SIZE) {\n 699:\t\t\tkvm_err(\"Invalid VMA page size 0x%lx\\n\", vma_pagesize);\n 700:\t\t\treturn -EFAULT;\n 701:\t\t}\n 702:\t\n 703:\t\thfn = __kvm_faultin_pfn(memslot, gfn, is_write ? FOLL_WRITE : 0,\n 704:\t\t\t\t\t\u0026writable, \u0026page);\n 705:\t\tif (hfn == KVM_PFN_ERR_HWPOISON) {\n 706:\t\t\tsend_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva,\n 707:\t\t\t\t\tvma_pageshift, current);\n 708:\t\t\treturn 0;\n 709:\t\t}\n 710:\t\tif (is_error_noslot_pfn(hfn))\n 711:\t\t\treturn -EFAULT;\n 712:\t\n 713:\t\t/*\n 714:\t\t * If logging is active then we allow writable pages only\n 715:\t\t * for write faults.\n 716:\t\t */\n 717:\t\tif (logging \u0026\u0026 !is_write)\n 718:\t\t\twritable = false;\n 719:\t\n 720:\t\twrite_lock(\u0026kvm-\u003emmu_lock);\n 721:\t\n 722:\t\tif (mmu_invalidate_retry(kvm, mmu_seq))\n"
} |
| 19/3 | 2026/08/14 13:10 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7596 output: 41 thoughts: 103 Thoughts: **My Initial Thoughts on `kvm_riscv_mmu_map`** Okay, so I'm looking at this `kvm_riscv_mmu_map` function, and the first thing I see is a `memset(out_map, 0, sizeof(*out_map));`. Alright, so the output map, `out_map`, is getting zeroed out right at the start. That's a pretty standard initialization, but it makes me think... if `out_map` is being *passed* into the function, it's being overwritten. This could be important for caching and sharing existing MMU state. Now, I see `kvm_riscv_gstage_map_page` being called somewhere inside `kvm_riscv_mmu_map`. That's where the real MMU translation work is probably happening. I need to understand how the zeroed-out `out_map` interacts with these page mappings. Let's dig deeper into the rest of `kvm_riscv_mmu_map` to see the full picture. I need to understand what's happening with that `out_map` variable! |
| 20/3 | 2026/08/14 13:10 | tool | read-file |
0mArgs:{
"File": "arch/riscv/kvm/mmu.c",
"FirstLine": 723,
"LineCount": 100
}Results: {
"Contents": " 723:\t\t\tgoto out_unlock;\n 724:\t\n 725:\t\t/*\n 726:\t\t * Check if we are backed by a THP and thus use block mapping if\n 727:\t\t * possible. Hugetlb mappings already selected their target size above,\n 728:\t\t * so do not promote them through the THP helper.\n 729:\t\t */\n 730:\t\tif (!logging \u0026\u0026 !is_hugetlb \u0026\u0026 vma_pagesize == PAGE_SIZE)\n 731:\t\t\tvma_pagesize = transparent_hugepage_adjust(kvm, memslot, hva, \u0026hfn, \u0026gpa);\n 732:\t\n 733:\t\tif (writable) {\n 734:\t\t\tmark_page_dirty_in_slot(kvm, memslot, gfn);\n 735:\t\t\tret = kvm_riscv_gstage_map_page(\u0026gstage, pcache, gpa, hfn \u003c\u003c PAGE_SHIFT,\n 736:\t\t\t\t\t\t\tvma_pagesize, false, true, out_map);\n 737:\t\t} else {\n 738:\t\t\tret = kvm_riscv_gstage_map_page(\u0026gstage, pcache, gpa, hfn \u003c\u003c PAGE_SHIFT,\n 739:\t\t\t\t\t\t\tvma_pagesize, true, true, out_map);\n 740:\t\t}\n 741:\t\n 742:\t\tif (ret)\n 743:\t\t\tkvm_err(\"Failed to map in G-stage\\n\");\n 744:\t\n 745:\tout_unlock:\n 746:\t\tkvm_release_faultin_page(kvm, page, ret \u0026\u0026 ret != -EEXIST, writable);\n 747:\t\twrite_unlock(\u0026kvm-\u003emmu_lock);\n 748:\t\treturn ret;\n 749:\t}\n 750:\t\n 751:\tlong kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\n 752:\t\t\t\t\t struct kvm_pre_fault_memory *range)\n 753:\t{\n 754:\t\tstruct kvm_gstage_mapping out_map = { 0 };\n 755:\t\tstruct kvm_memory_slot *memslot;\n 756:\t\tunsigned long map_size;\n 757:\t\tunsigned long hva;\n 758:\t\tgpa_t end;\n 759:\t\tgfn_t gfn;\n 760:\t\tint ret;\n 761:\t\n 762:\t\tgfn = gpa_to_gfn(range-\u003egpa);\n 763:\t\tmemslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);\n 764:\t\tif (!memslot)\n 765:\t\t\treturn -ENOENT;\n 766:\t\n 767:\t\thva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);\n 768:\t\tif (kvm_is_error_hva(hva))\n 769:\t\t\treturn -ENOENT;\n 770:\t\n 771:\t\tfor (;;) {\n 772:\t\t\tif (signal_pending(current))\n 773:\t\t\t\treturn -EINTR;\n 774:\t\n 775:\t\t\tif (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))\n 776:\t\t\t\treturn -EIO;\n 777:\t\n 778:\t\t\tcond_resched();\n 779:\t\t\tret = kvm_riscv_mmu_map(vcpu, memslot, range-\u003egpa, hva, false, \u0026out_map);\n 780:\t\t\tif (ret)\n 781:\t\t\t\treturn ret;\n 782:\t\n 783:\t\t\tif (pte_val(out_map.pte))\n 784:\t\t\t\tbreak;\n 785:\t\t}\n 786:\t\n 787:\t\tmap_size = PAGE_SIZE \u003c\u003c (out_map.level * kvm_riscv_gstage_index_bits);\n 788:\t\tend = ALIGN_DOWN(range-\u003egpa, map_size) + map_size;\n 789:\t\n 790:\t\treturn min_t(u64, range-\u003esize, end - range-\u003egpa);\n 791:\t}\n 792:\t\n 793:\tint kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)\n 794:\t{\n 795:\t\tstruct page *pgd_page;\n 796:\t\n 797:\t\tif (kvm-\u003earch.pgd != NULL) {\n 798:\t\t\tkvm_err(\"kvm_arch already initialized?\\n\");\n 799:\t\t\treturn -EINVAL;\n 800:\t\t}\n 801:\t\n 802:\t\tpgd_page = alloc_pages(GFP_KERNEL_ACCOUNT | __GFP_ZERO,\n 803:\t\t\t\t get_order(kvm_riscv_gstage_pgd_size));\n 804:\t\tif (!pgd_page)\n 805:\t\t\treturn -ENOMEM;\n 806:\t\tkvm-\u003earch.pgd = page_to_virt(pgd_page);\n 807:\t\tkvm-\u003earch.pgd_phys = page_to_phys(pgd_page);\n 808:\t\tkvm-\u003earch.pgd_levels = kvm_riscv_gstage_max_pgd_levels;\n 809:\t\tkvm-\u003earch.pgd_split_page_cache.gfp_zero = __GFP_ZERO;\n 810:\t\n 811:\t\treturn 0;\n 812:\t}\n 813:\t\n 814:\tvoid kvm_riscv_mmu_free_pgd(struct kvm *kvm)\n 815:\t{\n 816:\t\tstruct kvm_gstage gstage;\n 817:\t\tvoid *pgd = NULL;\n 818:\t\tbool flush = false;\n 819:\t\n 820:\t\twrite_lock(\u0026kvm-\u003emmu_lock);\n 821:\t\tif (kvm-\u003earch.pgd) {\n 822:\t\t\tkvm_riscv_gstage_init(\u0026gstage, kvm);\n"
} |
| 21/3 | 2026/08/14 13:10 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9648 output: 33 thoughts: 306 Thoughts: **Analyzing the kvm_riscv_gstage_map_page Modification** Okay, so I'm looking at this patch for `kvm_riscv_gstage_map_page`, and it seems to be addressing a case where the page is *already* mapped. The original code didn't handle this scenario, but the added lines indicate that if the physical address (hpa) of the page aligned down to the page size matches the aligned-down physical address derived from the page table entry, we now populate `out_map`. Specifically, it sets `out_map->addr`, `out_map->level`, and `out_map->pte`. This is important because it avoids unnecessary re-mappings or, possibly, incorrect mappings. Now, the question is, *does* `out_map` get sent to userspace? That's what I need to figure out. My intuition says that if we're dealing with a pre-fault situation, then `out_map` probably is not sent directly to userspace; however, the information in `out_map` may be used to respond to a userspace request, maybe in a manner that's transparent to the userspace caller. I know that `kvm_arch_vcpu_pre_fault_memory` is involved in a pre-fault, which gets called from `kvm_vcpu_ioctl_pre_fault_memory`, or something like it. I should verify the call chain and understand how the data in `out_map` is used downstream. Time to trace the calls to `kvm_arch_vcpu_pre_fault_memory` and see how this data is handled. |
| 22/3 | 2026/08/14 13:10 | tool | grepper |
0mArgs:{
"Expression": "kvm_arch_vcpu_pre_fault_memory",
"PathPrefix": ""
}Results: {
"Output": "arch/riscv/kvm/mmu.c=623=int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\n--\narch/riscv/kvm/mmu.c-750-\narch/riscv/kvm/mmu.c:751:long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\narch/riscv/kvm/mmu.c-752-\t\t\t\t struct kvm_pre_fault_memory *range)\n--\narch/s390/kvm/kvm-s390.c=5797=void kvm_arch_commit_memory_region(struct kvm *kvm,\n--\narch/s390/kvm/kvm-s390.c-5840-/**\narch/s390/kvm/kvm-s390.c:5841: * kvm_arch_vcpu_pre_fault_memory() -- pre-fault and link gmap dat tables\narch/s390/kvm/kvm-s390.c-5842- * @vcpu: the vcpu that shall appear to have generated the fault-in.\n--\narch/s390/kvm/kvm-s390.c-5859- */\narch/s390/kvm/kvm-s390.c:5860:long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, struct kvm_pre_fault_memory *range)\narch/s390/kvm/kvm-s390.c-5861-{\n--\narch/x86/kvm/mmu/mmu.c=4973=static int kvm_tdp_page_prefault(struct kvm_vcpu *vcpu, gpa_t gpa,\n--\narch/x86/kvm/mmu/mmu.c-5016-\narch/x86/kvm/mmu/mmu.c:5017:long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\narch/x86/kvm/mmu/mmu.c-5018-\t\t\t\t struct kvm_pre_fault_memory *range)\n--\narch/x86/kvm/vmx/tdx.c=1701=static int tdx_sept_map_leaf_spte(struct kvm *kvm, gfn_t gfn, enum pg_level level,\n--\narch/x86/kvm/vmx/tdx.c-1713-\t/*\narch/x86/kvm/vmx/tdx.c:1714:\t * Ensure pre_fault_allowed is read by kvm_arch_vcpu_pre_fault_memory()\narch/x86/kvm/vmx/tdx.c-1715-\t * before kvm_tdx-\u003estate. Userspace must not be allowed to pre-fault\n--\ninclude/linux/kvm_host.h=2610=void kvm_arch_gmem_invalidate(kvm_pfn_t start, kvm_pfn_t end);\n--\ninclude/linux/kvm_host.h-2613-#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY\ninclude/linux/kvm_host.h:2614:long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\ninclude/linux/kvm_host.h-2615-\t\t\t\t struct kvm_pre_fault_memory *range);\n--\nvirt/kvm/kvm_main.c=4334=static int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\n--\nvirt/kvm/kvm_main.c-4358-\nvirt/kvm/kvm_main.c:4359:\t\tr = kvm_arch_vcpu_pre_fault_memory(vcpu, range);\nvirt/kvm/kvm_main.c-4360-\t\tif (WARN_ON_ONCE(r == 0 || r == -EIO))\n"
} |
| 23/3 | 2026/08/14 13:10 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11432 output: 40 thoughts: 25 Thoughts: Here's my take on the thought process, as requested: **Diving into `kvm_main.c`** Okay, so I need to get to the bottom of this. Let's start by looking at `virt/kvm/kvm_main.c`, specifically around line 4334. That area usually holds crucial parts of the KVM architecture, it could be the place to find what's going on here. I'm hoping I can find the problem by digging into that section directly. It's a good starting point, and I have a feeling the issue is probably located in a region of code near line 4334. |
| 24/3 | 2026/08/14 13:10 | tool | read-file |
0mArgs:{
"File": "virt/kvm/kvm_main.c",
"FirstLine": 4334,
"LineCount": 50
}Results: {
"Contents": "4334:\tstatic int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\n4335:\t\t\t\t\t struct kvm_pre_fault_memory *range)\n4336:\t{\n4337:\t\tint idx;\n4338:\t\tlong r;\n4339:\t\tu64 full_size;\n4340:\t\n4341:\t\tif (range-\u003eflags)\n4342:\t\t\treturn -EINVAL;\n4343:\t\n4344:\t\tif (!PAGE_ALIGNED(range-\u003egpa) ||\n4345:\t\t !PAGE_ALIGNED(range-\u003esize) ||\n4346:\t\t range-\u003egpa + range-\u003esize \u003c= range-\u003egpa)\n4347:\t\t\treturn -EINVAL;\n4348:\t\n4349:\t\tvcpu_load(vcpu);\n4350:\t\tidx = srcu_read_lock(\u0026vcpu-\u003ekvm-\u003esrcu);\n4351:\t\n4352:\t\tfull_size = range-\u003esize;\n4353:\t\tdo {\n4354:\t\t\tif (signal_pending(current)) {\n4355:\t\t\t\tr = -EINTR;\n4356:\t\t\t\tbreak;\n4357:\t\t\t}\n4358:\t\n4359:\t\t\tr = kvm_arch_vcpu_pre_fault_memory(vcpu, range);\n4360:\t\t\tif (WARN_ON_ONCE(r == 0 || r == -EIO))\n4361:\t\t\t\tbreak;\n4362:\t\n4363:\t\t\tif (r \u003c 0)\n4364:\t\t\t\tbreak;\n4365:\t\n4366:\t\t\trange-\u003esize -= r;\n4367:\t\t\trange-\u003egpa += r;\n4368:\t\t\tcond_resched();\n4369:\t\t} while (range-\u003esize);\n4370:\t\n4371:\t\tsrcu_read_unlock(\u0026vcpu-\u003ekvm-\u003esrcu, idx);\n4372:\t\tvcpu_put(vcpu);\n4373:\t\n4374:\t\t/* Return success if at least one page was mapped successfully. */\n4375:\t\treturn full_size == range-\u003esize ? r : 0;\n4376:\t}\n4377:\t#endif\n4378:\t\n4379:\tstatic int kvm_wait_for_vcpu_online(struct kvm_vcpu *vcpu)\n4380:\t{\n4381:\t\tstruct kvm *kvm = vcpu-\u003ekvm;\n4382:\t\n4383:\t\t/*\n"
} |
| 25/3 | 2026/08/14 13:10 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12338 output: 42 thoughts: 25 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **My Line of Reasoning on `kvm_vcpu_pre_fault_memory`** Alright, so `kvm_vcpu_pre_fault_memory`... that immediately triggers my mental alarm bells. I know where that function sits in the KVM codebase; it's called from `kvm_vcpu_ioctl`. My brain immediately starts connecting the dots. `kvm_vcpu_ioctl` is the entry point for a whole raft of hypervisor requests, essentially the interface between the guest OS's userspace and the KVM core's interaction with the hardware. So, this means `kvm_vcpu_pre_fault_memory` is being invoked *during* an IOCTL call. The implications are fairly clear. It's likely handling memory faults *before* they are truly raised by the hardware MMU. This suggests it's part of a pre-emptive fault handling mechanism, potentially aiming at optimizing page faults, dealing with memory ballooning operations, or implementing some form of memory protection/virtualization. I’d guess it is working with some form of pre-emptive memory fault, likely to improve the overall performance of the guest in some way. I'd want to quickly check what parameters are being passed to that function, to know what specifically is the type of fault it's anticipating. Also, it’d be critical to understand *why* the fault is being pre-empted and how KVM is handling this potential guest-level fault. I'll need to dig into the surrounding code to determine the context and determine any associated optimizations. I'd definitely want to look at the surrounding control flow to see when it's called and the typical workload. A quick grep of the code base will certainly illuminate things more quickly. This is where the fun begins. |
| 26/3 | 2026/08/14 13:10 | tool | grepper |
0mArgs:{
"Expression": "kvm_vcpu_pre_fault_memory",
"PathPrefix": "virt/kvm/kvm_main.c"
}Results: {
"Output": "virt/kvm/kvm_main.c=4308=static int kvm_vcpu_ioctl_get_stats_fd(struct kvm_vcpu *vcpu)\n--\nvirt/kvm/kvm_main.c-4333-#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY\nvirt/kvm/kvm_main.c:4334:static int kvm_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\nvirt/kvm/kvm_main.c-4335-\t\t\t\t struct kvm_pre_fault_memory *range)\n--\nvirt/kvm/kvm_main.c=4406=static long kvm_vcpu_ioctl(struct file *filp,\n--\nvirt/kvm/kvm_main.c-4638-\t\t\tbreak;\nvirt/kvm/kvm_main.c:4639:\t\tr = kvm_vcpu_pre_fault_memory(vcpu, \u0026range);\nvirt/kvm/kvm_main.c-4640-\t\t/* Pass back leftover range. */\n"
} |
| 27/3 | 2026/08/14 13:10 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13099 output: 40 thoughts: 21 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into `kvm_vcpu_ioctl` (circa line 4638)** Okay, time to zero in on `kvm_vcpu_ioctl`, specifically around line 4638 in the source. Given my current understanding of the potential issue, I suspect that the critical data is processed in or around that line. I'm thinking, based on the previous context, that this is the point where a certain instruction or series of instructions related to the virtual CPU are handled. I need to refresh my memory on what's handled at that point in the function to confirm that my current understanding is correct. More specifically, I should check the relevant error codes and the data being passed in the ioctl commands. Let me double check if I'm thinking of the right call. |
| 28/3 | 2026/08/14 13:10 | tool | read-file |
0mArgs:{
"File": "virt/kvm/kvm_main.c",
"FirstLine": 4630,
"LineCount": 20
}Results: {
"Contents": "4630:\t\t\tbreak;\n4631:\t\t}\n4632:\t#ifdef CONFIG_KVM_GENERIC_PRE_FAULT_MEMORY\n4633:\t\tcase KVM_PRE_FAULT_MEMORY: {\n4634:\t\t\tstruct kvm_pre_fault_memory range;\n4635:\t\n4636:\t\t\tr = -EFAULT;\n4637:\t\t\tif (copy_from_user(\u0026range, argp, sizeof(range)))\n4638:\t\t\t\tbreak;\n4639:\t\t\tr = kvm_vcpu_pre_fault_memory(vcpu, \u0026range);\n4640:\t\t\t/* Pass back leftover range. */\n4641:\t\t\tif (copy_to_user(argp, \u0026range, sizeof(range)))\n4642:\t\t\t\tr = -EFAULT;\n4643:\t\t\tbreak;\n4644:\t\t}\n4645:\t#endif\n4646:\t\tdefault:\n4647:\t\t\tr = kvm_arch_vcpu_ioctl(filp, ioctl, arg);\n4648:\t\t}\n4649:\tout:\n"
} |
| 29/3 | 2026/08/14 13:10 | llm | kmsan-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13645 output: 43 thoughts: 972 Thoughts: **Analyzing Potential Information Leaks in KVM Pre-Fault Memory Handling**
Okay, so I'm looking at this `KVM_PRE_FAULT_MEMORY` case and the potential for information leakage. The initial concern was that the `range` struct, after being processed by `kvm_vcpu_pre_fault_memory`, might contain uninitialized memory when copied back to user space. However, on closer inspection, it's clear the `range` struct is fully initialized from user space with `copy_from_user(&range, argp, sizeof(range))` *before* it's even passed to the internal function. So, the only data returned to user space in `range` is the updated 'leftover' part of the original data. That's good; the initial worry is addressed.
My next line of investigation centered on `out_map` within `kvm_arch_vcpu_pre_fault_memory`. This struct is passed around and could potentially be a source of uninitialized data being returned. The code shows that `out_map` is initialized to zero (`struct kvm_gstage_mapping out_map = { 0 };`). Then it is again zeroed in `kvm_riscv_mmu_map` with `memset(out_map, 0, sizeof(*out_map))`, *before* `kvm_riscv_gstage_map_page` populates some of its members.
The logic within `kvm_riscv_mmu_map` either sets `out_map.pte`, along with other members, or returns without setting any of those values. The loop in `kvm_arch_vcpu_pre_fault_memory` continues if `out_map.pte` is not set; otherwise, it breaks. Importantly, `out_map.level` is used *after* this. Since `out_map` is initialized to zero, any uninitialized reads in `out_map` will read zero. No information leaks here.
The important point is that the `out_map` is explicitly zeroed before any fields are conditionally set based on the mapping process. This clear initialization strategy is key. I'm now confident that there's no path for uninitialized memory from `out_map` to leak back to user space.
|
| 30/3 | 2026/08/14 13:11 | tool | grepper |
0mError:no matches Args: {
"Expression": "struct kvm_gstage_mapping",
"PathPrefix": "arch/riscv/include/asm/kvm_host.h"
}Results: {
"error": "no matches"
} |
| 31/3 | 2026/08/14 13:11 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15169 output: 28 thoughts: 18 Thoughts: Here's my summary, as though I'm thinking it: **Digging into `kvm_gstage_mapping`** Okay, first things first, I need to get a clear picture of what `struct kvm_gstage_mapping` actually *is*. This sounds like a key data structure within the KVM virtualization layer, probably related to the guest stage table. It's time to check its definition. I need to pinpoint its exact location in the KVM source code. Knowing its structure will inform how the virtualization system handles memory. Let's see if it’s in a header file, and I expect it is, probably nested within a core KVM include directory. Let's start with a quick search to locate the declaration and refresh my understanding of how it's used. |
| 32/3 | 2026/08/14 13:12 | tool | grepper |
0mArgs:{
"Expression": "struct kvm_gstage_mapping",
"PathPrefix": ""
}Results: {
"Output": "arch/riscv/include/asm/kvm_gstage.h=12=struct kvm_gstage {\n--\narch/riscv/include/asm/kvm_gstage.h-20-\narch/riscv/include/asm/kvm_gstage.h:21:struct kvm_gstage_mapping {\narch/riscv/include/asm/kvm_gstage.h-22-\tgpa_t addr;\n--\narch/riscv/include/asm/kvm_gstage.h=53=int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage,\narch/riscv/include/asm/kvm_gstage.h-54-\t\t\t struct kvm_mmu_memory_cache *pcache,\narch/riscv/include/asm/kvm_gstage.h:55:\t\t\t const struct kvm_gstage_mapping *map);\narch/riscv/include/asm/kvm_gstage.h-56-\n--\narch/riscv/include/asm/kvm_gstage.h=61=int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\n--\narch/riscv/include/asm/kvm_gstage.h-64-\t\t\t bool page_rdonly, bool page_exec,\narch/riscv/include/asm/kvm_gstage.h:65:\t\t\t struct kvm_gstage_mapping *out_map);\narch/riscv/include/asm/kvm_gstage.h-66-\n--\narch/riscv/include/asm/kvm_mmu.h=14=int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\narch/riscv/include/asm/kvm_mmu.h-15-\t\t gpa_t gpa, unsigned long hva, bool is_write,\narch/riscv/include/asm/kvm_mmu.h:16:\t\t struct kvm_gstage_mapping *out_map);\narch/riscv/include/asm/kvm_mmu.h-17-int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm);\n--\narch/riscv/kvm/gstage.c=142=int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage,\narch/riscv/kvm/gstage.c-143-\t\t\t struct kvm_mmu_memory_cache *pcache,\narch/riscv/kvm/gstage.c:144:\t\t\t const struct kvm_gstage_mapping *map)\narch/riscv/kvm/gstage.c-145-{\n--\narch/riscv/kvm/gstage.c=207=int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,\n--\narch/riscv/kvm/gstage.c-210-\t\t\t bool page_rdonly, bool page_exec,\narch/riscv/kvm/gstage.c:211:\t\t\t struct kvm_gstage_mapping *out_map)\narch/riscv/kvm/gstage.c-212-{\n--\narch/riscv/kvm/mmu.c=40=int kvm_riscv_mmu_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,\n--\narch/riscv/kvm/mmu.c-51-\t};\narch/riscv/kvm/mmu.c:52:\tstruct kvm_gstage_mapping map;\narch/riscv/kvm/mmu.c-53-\tstruct kvm_gstage gstage;\n--\narch/riscv/kvm/mmu.c=552=static bool kvm_riscv_mmu_dirty_log_write_fault_fast(struct kvm *kvm,\n--\narch/riscv/kvm/mmu.c-554-\t\t\t\t\t\t gpa_t gpa,\narch/riscv/kvm/mmu.c:555:\t\t\t\t\t\t struct kvm_gstage_mapping *out_map)\narch/riscv/kvm/mmu.c-556-{\n--\narch/riscv/kvm/mmu.c=623=int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,\narch/riscv/kvm/mmu.c-624-\t\t gpa_t gpa, unsigned long hva, bool is_write,\narch/riscv/kvm/mmu.c:625:\t\t struct kvm_gstage_mapping *out_map)\narch/riscv/kvm/mmu.c-626-{\n--\narch/riscv/kvm/mmu.c=751=long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,\n--\narch/riscv/kvm/mmu.c-753-{\narch/riscv/kvm/mmu.c:754:\tstruct kvm_gstage_mapping out_map = { 0 };\narch/riscv/kvm/mmu.c-755-\tstruct kvm_memory_slot *memslot;\n--\narch/riscv/kvm/vcpu_exit.c=16=static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run,\n--\narch/riscv/kvm/vcpu_exit.c-18-{\narch/riscv/kvm/vcpu_exit.c:19:\tstruct kvm_gstage_mapping host_map;\narch/riscv/kvm/vcpu_exit.c-20-\tstruct kvm_memory_slot *memslot;\n"
} |
| 33/3 | 2026/08/14 13:12 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16879 output: 43 thoughts: 28 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into KVM G-Stage on RISC-V** Okay, time to dig in. Let's see... I'm thinking I need to refresh my memory on the KVM guest stage on RISC-V. Specifically, I'm going to jump into the `arch/riscv/include/asm/kvm_gstage.h` header. I seem to recall something interesting around line 21, likely some crucial structure definition or macro that I want to review in detail. I'm focusing my attention there to refresh my understanding of how guest physical addresses are translated or handled within the RISC-V KVM architecture. There might be some interesting implementation details or optimization strategies in that specific section that I want to analyze. I'm going in with a targeted approach to quickly parse the relevant parts of that file. |
| 34/3 | 2026/08/14 13:12 | tool | read-file |
0mArgs:{
"File": "arch/riscv/include/asm/kvm_gstage.h",
"FirstLine": 15,
"LineCount": 20
}Results: {
"Contents": " 15:\t#define KVM_GSTAGE_FLAGS_LOCAL\t\tBIT(0)\n 16:\t\tunsigned long vmid;\n 17:\t\tpgd_t *pgd;\n 18:\t\tunsigned long pgd_levels;\n 19:\t};\n 20:\t\n 21:\tstruct kvm_gstage_mapping {\n 22:\t\tgpa_t addr;\n 23:\t\tpte_t pte;\n 24:\t\tu32 level;\n 25:\t};\n 26:\t\n 27:\t#ifdef CONFIG_64BIT\n 28:\t#define kvm_riscv_gstage_index_bits\t9\n 29:\t#else\n 30:\t#define kvm_riscv_gstage_index_bits\t10\n 31:\t#endif\n 32:\t\n 33:\textern unsigned long kvm_riscv_gstage_max_pgd_levels;\n 34:\t\n"
} |
| 35/3 | 2026/08/14 13:12 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17405 output: 40 thoughts: 208 Thoughts: **Analyzing Potential Uninitialized Memory in KVM RISC-V Code**
Okay, so I'm diving into this KVM RISC-V code, specifically looking for potential uninitialized memory issues. My initial focus is on the `struct kvm_gstage_mapping`. I see it has the standard `addr`, `pte`, and `level` fields. Good. I note that it's initialized to `{ 0 }` in `kvm_arch_vcpu_pre_fault_memory`, and also that `kvm_riscv_mmu_map` uses `memset` to zero it out. That's a strong indicator that there shouldn't be any garbage left behind there. Good, we're covering our bases.
Now, let's switch gears to `struct kvm_pre_fault_memory`. This one's a bit more interesting because it's being copied from and to userspace. That's always a potential hotspot for vulnerabilities. I see the struct definition: `gpa`, `size`, `flags`, and some padding. Hmm, the padding. That's where things could get tricky. I need to make sure that the padding is initialized properly, otherwise we might have a potential information leak or some other unexpected behavior. Time to check the kernel headers for `struct kvm_pre_fault_memory`. Let's get the definitive source on that definition from `include/uapi/linux/kvm.h`. I'll confirm how that struct is intended to be used, especially that padding field, before proceeding. Gotta be thorough.
|
| 36/3 | 2026/08/14 13:12 | tool | grepper |
0mArgs:{
"Expression": "struct kvm_pre_fault_memory",
"PathPrefix": "include/uapi/linux/kvm.h"
}Results: {
"Output": "include/uapi/linux/kvm.h=1658=struct kvm_create_guest_memfd {\n--\ninclude/uapi/linux/kvm.h-1663-\ninclude/uapi/linux/kvm.h:1664:#define KVM_PRE_FAULT_MEMORY\t_IOWR(KVMIO, 0xd5, struct kvm_pre_fault_memory)\ninclude/uapi/linux/kvm.h-1665-\ninclude/uapi/linux/kvm.h:1666:struct kvm_pre_fault_memory {\ninclude/uapi/linux/kvm.h-1667-\t__u64 gpa;\n"
} |
| 37/3 | 2026/08/14 13:12 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18174 output: 40 thoughts: 25 Thoughts: Here's my summary, as if I were thinking these thoughts: **Diving into KVM Header Definitions** Alright, time to get my hands dirty. I need to refresh my memory on the KVM interface details, specifically some of the structures and constants used for interacting with the hypervisor. I remember a specific area that might be relevant to the issue I'm debugging, so let's jump straight into the kernel's `include/uapi/linux/kvm.h` file. I'm going to navigate to around line 1666 to start poking around. I'm betting that area probably defines something crucial to the functionality I'm trying to understand. I will see if that is the correct entrypoint and work my way from there. |
| 38/3 | 2026/08/14 13:12 | tool | read-file |
0mArgs:{
"File": "include/uapi/linux/kvm.h",
"FirstLine": 1666,
"LineCount": 10
}Results: {
"Contents": "1666:\tstruct kvm_pre_fault_memory {\n1667:\t\t__u64 gpa;\n1668:\t\t__u64 size;\n1669:\t\t__u64 flags;\n1670:\t\t__u64 padding[5];\n1671:\t};\n1672:\t\n1673:\t#endif /* __LINUX_KVM_H */\n"
} |
| 39/3 | 2026/08/14 13:12 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18520 output: 223 thoughts: 1434 Thoughts: **KMSAN Assessment: KVM Pre-Fault Memory for RISC-V**
Okay, let's break this down. We're looking at a new KVM feature for RISC-V: pre-faulting memory. The primary concern, as always, is whether this introduces any uninitialized memory usage that could lead to information leaks or other vulnerabilities.
First, the `kvm_pre_fault_memory` struct itself is copied directly from userspace using `copy_from_user`. Crucially, this copies the *entire* struct, including the padding. So, all fields, including the padding, are initialized with user-provided data. The kernel then updates `size` and `gpa`. The entire struct, including the potentially updated fields, is copied back to user space using `copy_to_user`. No kernel data is leaked, the padding simply retains its original value.
Next, we dive into `kvm_arch_vcpu_pre_fault_memory`. I see a `struct kvm_gstage_mapping out_map = { 0 };` here. That's good, `out_map` is initialized to zero. The code then calls `kvm_riscv_mmu_map`. We need to make sure this function doesn't leave `out_map` in a partial initialized state. We can see that `kvm_riscv_mmu_map` explicitly `memset`s `out_map` to zero and then calls `kvm_riscv_gstage_map_page`, which sets `out_map.level` and `out_map.pte` together.
Let's check `kvm_riscv_mmu_map` itself, just to be sure. Yes, it explicitly zero-initializes the struct, so that is safe.
Then, are there other places the struct is used? Let's trace it and double-check. Okay, I've confirmed that the local variables `gfn`, `memslot`, `hva`, `ret`, `map_size`, and `end`, and used variables are all assigned before use within the function. `out_map` is initialized, the others are assigned.
Selftests aren't a concern for kernel memory safety in themselves; they're user-space.
So, it seems that there's no path to uninitialized memory being used here. The code consistently initializes or assigns all variables before use, and any user-provided padding is explicitly copied. The patch just adds a new capability, `KVM_CAP_PRE_FAULT_MEMORY`, which maps memory pages ahead of time. I would expect KASAN, LOCKDEP, and the usual bug detectors to catch any memory corruption issues, like out-of-bounds access, wrong page sizes, or locking problems. Given this analysis, I don't see any risk that requires KMSAN. We're good with KASAN.
|
| 40/3 | 2026/08/14 13:12 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch introduces support for KVM_CAP_PRE_FAULT_MEMORY on RISC-V. It adds `kvm_arch_vcpu_pre_fault_memory` which uses a local `struct kvm_gstage_mapping out_map` initialized to `{ 0 }`. The `out_map` is further zeroed out via `memset` in `kvm_riscv_mmu_map` before being populated. The `struct kvm_pre_fault_memory` passed from user space is fully initialized via `copy_from_user` (including padding) before being modified and copied back, so there is no kernel info-leak. All variables are properly initialized before use. Any potential bugs introduced by this patch (such as out-of-bounds accesses or locking issues) would be caught by KASAN and LOCKDEP. There are no uninitialized memory risks that require a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch introduces support for KVM_CAP_PRE_FAULT_MEMORY on RISC-V. It adds `kvm_arch_vcpu_pre_fault_memory` which uses a local `struct kvm_gstage_mapping out_map` initialized to `{ 0 }`. The `out_map` is further zeroed out via `memset` in `kvm_riscv_mmu_map` before being populated. The `struct kvm_pre_fault_memory` passed from user space is fully initialized via `copy_from_user` (including padding) before being modified and copied back, so there is no kernel info-leak. All variables are properly initialized before use. Any potential bugs introduced by this patch (such as out-of-bounds accesses or locking issues) would be caught by KASAN and LOCKDEP. There are no uninitialized memory risks that require a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|