| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/01 22:00 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"KVM",
"KVM_AMD",
"KVM_INTEL"
],
"FocusSymbols": [
"kvm_valid_efer",
"__kvm_valid_efer",
"cpuid_func_emulated",
"kvm_vcpu_after_set_cpuid",
"efer_trap"
],
"KMSANReasoning": "The patch adds support for handling the AMD EFER_LMSLE_MBZ CPUID defeature bit (CPUID 0x80000008:EBX[20]), which dictates whether EFER.LMSLE is allowed to be set in KVM guests.\n\nSpecifically, the patch:\n1. Adds `PASSTHROUGH_F(EFER_LMSLE_MBZ)` in `kvm_initialize_cpu_caps()` and updates vendor CPU capability initialization in `svm.c` and `vmx.c`.\n2. Adds handling for leaf 0x80000008 in `cpuid_func_emulated()`, operating on `struct kvm_cpuid_entry2`, which is explicitly zeroed with `memset(entry, 0, sizeof(*entry))` upon entering the function.\n3. Updates `__kvm_valid_efer()` to reject EFER.LMSLE if the guest has `X86_FEATURE_EFER_LMSLE_MBZ`.\n4. Masks `EFER_LMSLE` in `efer_trap()` for SEV-ES guests.\n5. Updates KVM selftests.\n\nAll affected data are scalar integers, bitmasks, and explicitly initialized/zeroed structs (`memset` in `cpuid_func_emulated`, `kvzalloc_objs` in `kvm_dev_ioctl_get_cpuid`). The changes do not introduce or modify uninitialized memory reads, struct padding leaks to userspace, or uninitialized buffer accesses. Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies KVM's CPUID emulation and EFER validation logic to support the EFER_LMSLE_MBZ defeature bit on x86 (including SVM and VMX). It updates capability initialization, CPUID emulation for function 0x80000008, EFER validation in __kvm_valid_efer/kvm_valid_efer, and SVM EFER trap handling. These paths are directly reachable and fuzzed via KVM ioctls such as KVM_SET_CPUID2, KVM_SET_SREGS, KVM_SET_MSRS, and KVM_RUN.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/01 22:00 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 757af473de3b2aa295eac49c88b809e1f8256f0e\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Oct 1 21:59:59 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst\nindex e0430cc750c9e..2852bf94828be 100644\n--- a/Documentation/virt/kvm/api.rst\n+++ b/Documentation/virt/kvm/api.rst\n@@ -9635,6 +9635,31 @@ On older versions of Linux, CPU[EAX=1]:ECX[24] (TSC_DEADLINE) is not reported by\n is present and the kernel has enabled in-kernel emulation of the local APIC.\n On newer versions, ``KVM_GET_SUPPORTED_CPUID`` does report the bit as available.\n \n+Long mode segment limits\n+~~~~~~~~~~~~~~~~~~~~~~~~\n+\n+CPU[EAX=0x80000008]:EBX[20] (EFER_LMSLE_MBZ) is a \"defeature\" bit: it is set\n+when the CPU does *not* support long mode segment limits, and so requires\n+EFER.LMSLE to be zero. KVM reports the bit via ``KVM_GET_SUPPORTED_CPUID`` if\n+and only if KVM refuses to set EFER.LMSLE, i.e. if the CPU doesn't support long\n+mode segment limits, or if nested SVM is unsupported. KVM therefore reports\n+the bit on all Intel hosts, as KVM allows EFER.LMSLE only when nested SVM is\n+enabled.\n+\n+KVM never reports the bit via ``KVM_GET_EMULATED_CPUID``, but userspace may set\n+it via ``KVM_SET_CPUID2`` even on a host where KVM doesn't report it. KVM\n+honors the guest's enumeration and rejects EFER.LMSLE=1 accordingly. That lets\n+userspace defeature a vCPU on a host that *does* support long mode segment\n+limits, so that the vCPU can later be migrated to a host that doesn't, e.g. so\n+that a vCPU created on AMD Rome can be migrated to Milan and later, which\n+dropped support for long mode segment limits. The opposite direction needs no\n+emulation, as a host that lacks long mode segment limits already enumerates the\n+defeature.\n+\n+Note, ``KVM_SET_MSRS`` is exempt from the check, as host-initiated MSR writes\n+skip guest CPUID checks so that userspace can set MSRs before it sets guest\n+CPUID. ``KVM_SET_SREGS`` and nested VMRUN are not exempt.\n+\n CPU topology\n ~~~~~~~~~~~~\n \ndiff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c\nindex 851f151efb35b..53d205eda3359 100644\n--- a/arch/x86/kvm/cpuid.c\n+++ b/arch/x86/kvm/cpuid.c\n@@ -1171,6 +1171,10 @@ void kvm_initialize_cpu_caps(void)\n \t\tF(AMD_STIBP),\n \t\tF(AMD_STIBP_ALWAYS_ON),\n \t\tF(AMD_IBRS_SAME_MODE),\n+\t\t/*\n+\t\t * Vendor code also sets EFER_LMSLE_MBZ if KVM itself\n+\t\t * can't support EFER.LMSLE, e.g. if nested SVM is disabled.\n+\t\t */\n \t\tPASSTHROUGH_F(EFER_LMSLE_MBZ),\n \t\tF(AMD_PSFD),\n \t\tF(AMD_IBPB_RET),\n@@ -1415,6 +1419,22 @@ static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 ind\n \t\tif (kvm_cpu_cap_has(X86_FEATURE_RDTSCP))\n \t\t\tentry-\u003eecx = feature_bit(RDPID);\n \t\treturn 1;\n+\tcase 0x80000008:\n+\t\t/*\n+\t\t * Honor the guest's EFER_LMSLE_MBZ even if the underlying CPU\n+\t\t * allows setting EFER.LMSLE, e.g. to allow migrating a vCPU\n+\t\t * between hosts with and without EFER.LMSLE support. To avoid\n+\t\t * breaking existing setups that reflect KVM's supported CPUID\n+\t\t * into the guest, KVM doesn't advertise EFER_LMSLE_MBZ unless\n+\t\t * KVM *can't* support EFER.LMSLE=1.\n+\t\t */\n+\t\tif (include_partially_emulated \u0026\u0026\n+\t\t !kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ)) {\n+\t\t\tentry-\u003eebx |= feature_bit(EFER_LMSLE_MBZ);\n+\t\t\treturn 1;\n+\t\t}\n+\t\t/* Nothing in 0x80000008 is fully emulated, don't emit an entry. */\n+\t\treturn 0;\n \tdefault:\n \t\treturn 0;\n \t}\ndiff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c\nindex dd3bb04878ca6..b519fb90776e2 100644\n--- a/arch/x86/kvm/msrs.c\n+++ b/arch/x86/kvm/msrs.c\n@@ -598,9 +598,19 @@ static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\n \tif (efer \u0026 EFER_NX \u0026\u0026 !guest_cpu_cap_has(vcpu, X86_FEATURE_NX))\n \t\treturn false;\n \n-\treturn true;\n+\t/*\n+\t * EFER_LMSLE_MBZ is a \"defeature\" bit, i.e. is set when the CPU does\n+\t * *not* support long mode segment limits, and so is the only EFER\n+\t * check whose polarity is inverted: EFER.LMSLE is legal if and only if\n+\t * the guest does *not* have the defeature.\n+\t */\n+\tif (efer \u0026 EFER_LMSLE \u0026\u0026\n+\t guest_cpu_cap_has(vcpu, X86_FEATURE_EFER_LMSLE_MBZ))\n+\t\treturn false;\n \n+\treturn true;\n }\n+\n bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\n {\n \tif (efer \u0026 ~kvm_caps.supported_efer_bits)\ndiff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c\nindex 7d59d301e1e54..90a80aad672f6 100644\n--- a/arch/x86/kvm/svm/svm.c\n+++ b/arch/x86/kvm/svm/svm.c\n@@ -2759,10 +2759,19 @@ static int efer_trap(struct kvm_vcpu *vcpu)\n \t * bit in svm_set_efer(), but __kvm_valid_efer() checks it against\n \t * whether the guest has X86_FEATURE_SVM - this avoids a failure if\n \t * the guest doesn't have X86_FEATURE_SVM.\n+\t *\n+\t * Clear EFER_LMSLE for a related reason: EFER writes are *trapped*,\n+\t * not intercepted, i.e. hardware has already committed the write by\n+\t * the time KVM gains control, and the trap is enabled if and only if\n+\t * the guest is SEV-ES, whose EFER lives in the encrypted VMSA and so\n+\t * can't be fixed up by KVM. Rejecting EFER.LMSLE=1 would inject a #GP\n+\t * *and* leave EFER.LMSLE set in the guest, which is strictly worse\n+\t * than honoring a write that hardware itself allowed.\n \t */\n \tmsr_info.host_initiated = false;\n \tmsr_info.index = MSR_EFER;\n-\tmsr_info.data = to_svm(vcpu)-\u003evmcb-\u003econtrol.exit_info_1 \u0026 ~EFER_SVME;\n+\tmsr_info.data = to_svm(vcpu)-\u003evmcb-\u003econtrol.exit_info_1 \u0026\n+\t\t\t~(EFER_SVME | EFER_LMSLE);\n \tret = kvm_set_msr_common(vcpu, \u0026msr_info);\n \n \treturn kvm_complete_insn_gp(vcpu, ret);\n@@ -5571,6 +5580,16 @@ static __init void svm_set_cpu_caps(void)\n \t boot_cpu_has(X86_FEATURE_AMD_SSBD))\n \t\tkvm_cpu_cap_set(X86_FEATURE_VIRT_SSBD);\n \n+\t/*\n+\t * Tell userspace that EFER.LMSLE must be zero if nested SVM is\n+\t * disabled, as KVM allows EFER.LMSLE if and only if nested SVM is\n+\t * supported (a historical artifact of commit eec4b140c924 (\"KVM: SVM:\n+\t * Allow EFER.LMSLE to be set with nested svm\"), not an architectural\n+\t * requirement).\n+\t */\n+\tif (!nested)\n+\t\tkvm_cpu_cap_set(X86_FEATURE_EFER_LMSLE_MBZ);\n+\n \tif (enable_pmu) {\n \t\t/*\n \t\t * Enumerate support for PERFCTR_CORE if and only if KVM has\ndiff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c\nindex 612ab07d4100f..f144c1e1c63f2 100644\n--- a/arch/x86/kvm/vmx/vmx.c\n+++ b/arch/x86/kvm/vmx/vmx.c\n@@ -8137,6 +8137,13 @@ static __init void vmx_set_cpu_caps(void)\n \t\tkvm_cpu_cap_clear(X86_FEATURE_IBT);\n \t}\n \n+\t/*\n+\t * CPUID 0x80000008. Tell userspace that EFER.LMSLE must be zero; KVM\n+\t * never allows EFER.LMSLE to be set on Intel CPUs, as KVM supports long\n+\t * mode segment limits only in conjunction with nested SVM.\n+\t */\n+\tkvm_cpu_cap_set(X86_FEATURE_EFER_LMSLE_MBZ);\n+\n \tkvm_setup_xss_caps();\n \tkvm_finalize_cpu_caps();\n }\ndiff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c\nindex 79468ddfe4736..830cb9320d892 100644\n--- a/arch/x86/kvm/x86.c\n+++ b/arch/x86/kvm/x86.c\n@@ -6930,7 +6930,14 @@ static void kvm_setup_efer_caps(void)\n \n \tif (kvm_cpu_cap_has(X86_FEATURE_SVM)) {\n \t\tkvm_caps.supported_efer_bits |= EFER_SVME;\n-\t\tif (!boot_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ))\n+\n+\t\t/*\n+\t\t * Enumerating EFER_LMSLE_MBZ and allowing EFER.LMSLE=1\n+\t\t * would be nonsensical. Note, vendor code sets the defeature\n+\t\t * if KVM can't support EFER.LMSLE for any reason, i.e. this\n+\t\t * needs to consult KVM's capabilities, not just raw CPUID.\n+\t\t */\n+\t\tif (!kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ))\n \t\t\tkvm_caps.supported_efer_bits |= EFER_LMSLE;\n \t}\n }\ndiff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm\nindex 96bab7002d39e..2554464d8b96f 100644\n--- a/tools/testing/selftests/kvm/Makefile.kvm\n+++ b/tools/testing/selftests/kvm/Makefile.kvm\n@@ -117,7 +117,7 @@ TEST_GEN_PROGS_x86 += x86/state_test\n TEST_GEN_PROGS_x86 += x86/vmx_preemption_timer_test\n TEST_GEN_PROGS_x86 += x86/svm_vmcall_test\n TEST_GEN_PROGS_x86 += x86/svm_int_ctl_test\n-TEST_GEN_PROGS_x86 += x86/svm_nested_clear_efer_svme\n+TEST_GEN_PROGS_x86 += x86/svm_nested_efer_test\n TEST_GEN_PROGS_x86 += x86/svm_nested_shutdown_test\n TEST_GEN_PROGS_x86 += x86/svm_nested_soft_inject_test\n TEST_GEN_PROGS_x86 += x86/svm_nested_vmcb12_gpa\ndiff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h\nindex 6e6f70035508a..6f110987c51a7 100644\n--- a/tools/testing/selftests/kvm/include/x86/processor.h\n+++ b/tools/testing/selftests/kvm/include/x86/processor.h\n@@ -216,6 +216,7 @@ struct kvm_x86_cpu_feature {\n #define\tX86_FEATURE_INVTSC\t\tKVM_X86_CPU_FEATURE(0x80000007, 0, EDX, 8)\n #define\tX86_FEATURE_RDPRU\t\tKVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 4)\n #define\tX86_FEATURE_AMD_IBPB\t\tKVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 12)\n+#define\tX86_FEATURE_EFER_LMSLE_MBZ\tKVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 20)\n #define\tX86_FEATURE_NPT\t\t\tKVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 0)\n #define\tX86_FEATURE_LBRV\t\tKVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 1)\n #define\tX86_FEATURE_NRIPS\t\tKVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 3)\n@@ -1472,6 +1473,19 @@ static inline bool kvm_is_lbrv_enabled(void)\n \treturn !!get_kvm_amd_param_integer(\"lbrv\");\n }\n \n+/*\n+ * Do NOT use this to check for nVMX or nSVM support. Querying kvm_cpu_has()\n+ * for either of X86_FEATURE_{VMX,SVM} is the idiomatic way to check for nested\n+ * virtualization support. Use this *only* to validate KVM's own enumeration.\n+ */\n+static inline bool kvm_is_nested_virtualization_enabled(void)\n+{\n+\tif (host_cpu_is_intel)\n+\t\treturn get_kvm_intel_param_bool(\"nested\");\n+\n+\treturn get_kvm_amd_param_integer(\"nested\");\n+}\n+\n u64 *vm_get_pte(struct kvm_vm *vm, gva_t gva);\n \n u64 kvm_hypercall(u64 nr, u64 a0, u64 a1, u64 a2, u64 a3);\ndiff --git a/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c b/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c\ndeleted file mode 100644\nindex 6bc301207cbcb..0000000000000\n--- a/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c\n+++ /dev/null\n@@ -1,50 +0,0 @@\n-// SPDX-License-Identifier: GPL-2.0-only\n-/*\n- * Copyright (C) 2026, Google LLC.\n- */\n-#include \"kvm_util.h\"\n-#include \"vmx.h\"\n-#include \"svm_util.h\"\n-#include \"kselftest.h\"\n-\n-\n-static void l2_guest_code(void)\n-{\n-\tunsigned long efer = rdmsr(MSR_EFER);\n-\n-\t/* generic_svm_setup() initializes EFER_SVME set for L2 */\n-\tGUEST_ASSERT(efer \u0026 EFER_SVME);\n-\twrmsr(MSR_EFER, efer \u0026 ~EFER_SVME);\n-\n-\t/* Unreachable, L1 should be shutdown */\n-\tGUEST_ASSERT(0);\n-}\n-\n-static void l1_guest_code(struct svm_test_data *svm)\n-{\n-\tgeneric_svm_setup(svm, l2_guest_code);\n-\trun_guest(svm-\u003evmcb, svm-\u003evmcb_gpa);\n-\n-\t/* Unreachable, L1 should be shutdown */\n-\tGUEST_ASSERT(0);\n-}\n-\n-int main(int argc, char *argv[])\n-{\n-\tstruct kvm_vcpu *vcpu;\n-\tstruct kvm_vm *vm;\n-\tgva_t nested_gva = 0;\n-\n-\tTEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));\n-\n-\tvm = vm_create_with_one_vcpu(\u0026vcpu, l1_guest_code);\n-\n-\tvcpu_alloc_svm(vm, \u0026nested_gva);\n-\tvcpu_args_set(vcpu, 1, nested_gva);\n-\n-\tvcpu_run(vcpu);\n-\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);\n-\n-\tkvm_vm_free(vm);\n-\treturn 0;\n-}\ndiff --git a/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c b/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c\nnew file mode 100644\nindex 0000000000000..fa0d76a9c390b\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c\n@@ -0,0 +1,266 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/*\n+ * Tests for KVM's handling of EFER bits whose behavior is tied to nested SVM.\n+ *\n+ * Copyright (C) 2026, Google LLC.\n+ */\n+#include \"test_util.h\"\n+#include \"kvm_util.h\"\n+#include \"processor.h\"\n+#include \"svm_util.h\"\n+#include \"kselftest.h\"\n+\n+static bool l2_ran;\n+\n+static void l2_clear_efer_svme(void)\n+{\n+\tu64 efer = rdmsr(MSR_EFER);\n+\n+\t/* generic_svm_setup() initializes EFER_SVME set for L2 */\n+\tGUEST_ASSERT(efer \u0026 EFER_SVME);\n+\twrmsr(MSR_EFER, efer \u0026 ~EFER_SVME);\n+\n+\t/* Unreachable, L1 should be shutdown */\n+\tGUEST_ASSERT(0);\n+}\n+\n+static void l1_clear_efer_svme(struct svm_test_data *svm)\n+{\n+\tgeneric_svm_setup(svm, l2_clear_efer_svme);\n+\trun_guest(svm-\u003evmcb, svm-\u003evmcb_gpa);\n+\n+\t/* Unreachable, L1 should be shutdown */\n+\tGUEST_ASSERT(0);\n+}\n+\n+static void l2_lmsle(void)\n+{\n+\tGUEST_ASSERT(rdmsr(MSR_EFER) \u0026 EFER_LMSLE);\n+\tl2_ran = true;\n+\tvmmcall();\n+}\n+\n+static void l1_lmsle(struct svm_test_data *svm)\n+{\n+\tbool lmsle_mbz = this_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ);\n+\tstruct vmcb *vmcb = svm-\u003evmcb;\n+\tu64 efer = rdmsr(MSR_EFER);\n+\n+\t/*\n+\t * Selftests' vCPUs are created with EFER.LMSLE clear; the sub-tests\n+\t * below need to start from a clean slate.\n+\t */\n+\tGUEST_ASSERT(!(efer \u0026 EFER_LMSLE));\n+\tGUEST_ASSERT(!l2_ran);\n+\n+\t/*\n+\t * Per the APM, if EFER_LMSLE_MBZ is enumerated in CPUID, \"64-bit mode\n+\t * segment limit checking is not supported and attempting to set\n+\t * EFER.LMSLE = 1 causes a #GP exception\".\n+\t */\n+\tif (lmsle_mbz) {\n+\t\tGUEST_ASSERT_EQ(wrmsr_safe(MSR_EFER, efer | EFER_LMSLE), GP_VECTOR);\n+\t\tGUEST_ASSERT(!(rdmsr(MSR_EFER) \u0026 EFER_LMSLE));\n+\t} else {\n+\t\tGUEST_ASSERT_EQ(wrmsr_safe(MSR_EFER, efer | EFER_LMSLE), 0);\n+\t\tGUEST_ASSERT(rdmsr(MSR_EFER) \u0026 EFER_LMSLE);\n+\n+\t\t/*\n+\t\t * Restore EFER so that generic_svm_setup() doesn't propagate\n+\t\t * EFER.LMSLE into vmcb12 on its own, i.e. so that the VMRUN\n+\t\t * sub-test actually tests what it thinks it's testing.\n+\t\t */\n+\t\twrmsr(MSR_EFER, efer);\n+\t}\n+\n+\t/*\n+\t * VMRUN's consistency checks reject \"any MBZ bit of EFER\", i.e. a\n+\t * vmcb12 with EFER.LMSLE set must generate VMEXIT_INVALID when the\n+\t * defeature is enumerated.\n+\t */\n+\tgeneric_svm_setup(svm, l2_lmsle);\n+\tvmcb-\u003esave.efer |= EFER_LMSLE;\n+\trun_guest(vmcb, svm-\u003evmcb_gpa);\n+\n+\tif (lmsle_mbz) {\n+\t\tGUEST_ASSERT_EQ(vmcb-\u003econtrol.exit_code, SVM_EXIT_ERR);\n+\t\tGUEST_ASSERT(!l2_ran);\n+\t} else {\n+\t\tGUEST_ASSERT_EQ(vmcb-\u003econtrol.exit_code, SVM_EXIT_VMMCALL);\n+\t\tGUEST_ASSERT(l2_ran);\n+\t\tGUEST_ASSERT(vmcb-\u003esave.efer \u0026 EFER_LMSLE);\n+\t}\n+\n+\tGUEST_DONE();\n+}\n+\n+static struct kvm_vcpu *create_l1_vcpu(struct kvm_vm **vm, void *l1_guest_code)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tgva_t svm_gva;\n+\n+\t*vm = vm_create_with_one_vcpu(\u0026vcpu, l1_guest_code);\n+\n+\tvcpu_alloc_svm(*vm, \u0026svm_gva);\n+\tvcpu_args_set(vcpu, 1, svm_gva);\n+\n+\treturn vcpu;\n+}\n+\n+static void test_enumeration(void)\n+{\n+\tbool lmsle_mbz;\n+\n+\t/*\n+\t * EFER_LMSLE_MBZ, CPUID.80000008H:EBX[bit 20], is a \"defeature\" bit,\n+\t * i.e. is set when the CPU does *not* support long mode segment\n+\t * limits. KVM enumerates the defeature if and only if KVM refuses to\n+\t * set EFER.LMSLE, i.e. if the CPU doesn't support LMSLE, or if KVM\n+\t * doesn't support nested SVM. Derive the expectation from raw CPUID\n+\t * and kvm_amd's \"nested\" module param rather than from\n+\t * kvm_cpu_has(X86_FEATURE_SVM), so that the assertion doesn't simply\n+\t * compare KVM's enumeration to itself.\n+\t */\n+\tlmsle_mbz = this_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ) ||\n+\t\t !this_cpu_has(X86_FEATURE_SVM) ||\n+\t\t !kvm_is_nested_virtualization_enabled();\n+\n+\tTEST_ASSERT_EQ(kvm_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ), lmsle_mbz);\n+\n+\tksft_test_result_pass(\"KVM enumerates EFER_LMSLE_MBZ=%d\\n\", lmsle_mbz);\n+}\n+\n+static void test_clear_efer_svme(void)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\n+\tvcpu = create_l1_vcpu(\u0026vm, l1_clear_efer_svme);\n+\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);\n+\n+\tkvm_vm_free(vm);\n+\tksft_test_result_pass(\"L2 clearing EFER.SVME shuts down L1\\n\");\n+}\n+\n+static void test_lmsle(bool lmsle_mbz)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tstruct ucall uc;\n+\n+\tvcpu = create_l1_vcpu(\u0026vm, l1_lmsle);\n+\n+\tvcpu_set_or_clear_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ,\n+\t\t\t\t\tlmsle_mbz);\n+\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\n+\tswitch (get_ucall(vcpu, \u0026uc)) {\n+\tcase UCALL_ABORT:\n+\t\tREPORT_GUEST_ASSERT(uc);\n+\tcase UCALL_DONE:\n+\t\tbreak;\n+\tdefault:\n+\t\tTEST_FAIL(\"Unexpected ucall: %lu\", uc.cmd);\n+\t}\n+\n+\tkvm_vm_free(vm);\n+\tksft_test_result_pass(\"Guest EFER_LMSLE_MBZ=%d\\n\", lmsle_mbz);\n+}\n+\n+static void test_host_initiated_lmsle(void)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tu64 efer;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, NULL);\n+\tvcpu_set_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ);\n+\n+\t/*\n+\t * EFER_LMSLE_MBZ is a guest CPUID consistency check, not a host\n+\t * capability, i.e. must not be enforced against host-initiated writes,\n+\t * so that userspace can set MSRs before it sets guest CPUID.\n+\t */\n+\tefer = vcpu_get_msr(vcpu, MSR_EFER);\n+\tTEST_ASSERT(!(efer \u0026 EFER_LMSLE), \"EFER.LMSLE unexpectedly set\");\n+\n+\tvcpu_set_msr(vcpu, MSR_EFER, efer | EFER_LMSLE);\n+\tTEST_ASSERT_EQ(vcpu_get_msr(vcpu, MSR_EFER), efer | EFER_LMSLE);\n+\n+\tkvm_vm_free(vm);\n+\tksft_test_result_pass(\"Host-initiated EFER.LMSLE=1 is allowed\\n\");\n+}\n+\n+static void test_sregs_lmsle(void)\n+{\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_sregs sregs;\n+\tstruct kvm_vm *vm;\n+\tint rc;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, NULL);\n+\tvcpu_set_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ);\n+\n+\t/*\n+\t * Unlike KVM_SET_MSRS, KVM_SET_SREGS runs the full set of guest CPUID\n+\t * checks, i.e. rejects EFER.LMSLE even though it's host-initiated.\n+\t */\n+\tvcpu_sregs_get(vcpu, \u0026sregs);\n+\tTEST_ASSERT(!(sregs.efer \u0026 EFER_LMSLE), \"EFER.LMSLE unexpectedly set\");\n+\n+\tsregs.efer |= EFER_LMSLE;\n+\trc = _vcpu_sregs_set(vcpu, \u0026sregs);\n+\tTEST_ASSERT(rc, \"KVM allowed EFER.LMSLE with EFER_LMSLE_MBZ set\");\n+\n+\tkvm_vm_free(vm);\n+\tksft_test_result_pass(\"KVM_SET_SREGS rejects EFER.LMSLE=1\\n\");\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tbool has_nested_svm, has_lmsle;\n+\n+\tksft_print_header();\n+\tksft_set_plan(6);\n+\n+\ttest_enumeration();\n+\n+\t/*\n+\t * The sub-tests below need to actually run a nested guest, and the\n+\t * EFER.LMSLE sub-tests additionally need KVM to allow EFER.LMSLE.\n+\t * It's KVM's view of the world, not raw CPUID, that dictates whether\n+\t * EFER.LMSLE is allowed, i.e. whether the defeature is emulated.\n+\t */\n+\thas_nested_svm = kvm_cpu_has(X86_FEATURE_SVM);\n+\thas_lmsle = has_nested_svm \u0026\u0026\n+\t\t !kvm_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ);\n+\n+\tif (!has_nested_svm)\n+\t\tksft_print_msg(\"Nested SVM unsupported\\n\");\n+\telse if (!has_lmsle)\n+\t\tksft_print_msg(\"KVM doesn't support EFER.LMSLE\\n\");\n+\n+\tif (has_nested_svm) {\n+\t\ttest_clear_efer_svme();\n+\t\ttest_lmsle(true);\n+\t} else {\n+\t\tksft_test_result_skip(\"L2 clearing EFER.SVME shuts down L1\\n\");\n+\t\tksft_test_result_skip(\"Guest EFER_LMSLE_MBZ=1\\n\");\n+\t}\n+\n+\tif (has_lmsle) {\n+\t\ttest_lmsle(false);\n+\t\ttest_host_initiated_lmsle();\n+\t\ttest_sregs_lmsle();\n+\t} else {\n+\t\tksft_test_result_skip(\"Guest EFER_LMSLE_MBZ=0\\n\");\n+\t\tksft_test_result_skip(\"Host-initiated EFER.LMSLE=1 is allowed\\n\");\n+\t\tksft_test_result_skip(\"KVM_SET_SREGS rejects EFER.LMSLE=1\\n\");\n+\t}\n+\n+\tksft_finished();\n+}\n"
} |
| 2/1 | 2026/10/01 22:00 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": [
"KVM",
"KVM_AMD",
"KVM_INTEL"
],
"FocusSymbols": [
"kvm_valid_efer",
"__kvm_valid_efer",
"cpuid_func_emulated",
"kvm_vcpu_after_set_cpuid",
"efer_trap"
],
"Reasoning": "The patch modifies KVM's CPUID emulation and EFER validation logic to support the EFER_LMSLE_MBZ defeature bit on x86 (including SVM and VMX). It updates capability initialization, CPUID emulation for function 0x80000008, EFER validation in __kvm_valid_efer/kvm_valid_efer, and SVM EFER trap handling. These paths are directly reachable and fuzzed via KVM ioctls such as KVM_SET_CPUID2, KVM_SET_SREGS, KVM_SET_MSRS, and KVM_RUN.",
"WorthFuzzing": true
}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 757af473de3b2aa295eac49c88b809e1f8256f0e
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 1 21:59:59 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index e0430cc750c9e..2852bf94828be 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -9635,6 +9635,31 @@ On older versions of Linux, CPU[EAX=1]:ECX[24] (TSC_DEADLINE) is not reported by
is present and the kernel has enabled in-kernel emulation of the local APIC.
On newer versions, ``KVM_GET_SUPPORTED_CPUID`` does report the bit as available.
+Long mode segment limits
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+CPU[EAX=0x80000008]:EBX[20] (EFER_LMSLE_MBZ) is a "defeature" bit: it is set
+when the CPU does *not* support long mode segment limits, and so requires
+EFER.LMSLE to be zero. KVM reports the bit via ``KVM_GET_SUPPORTED_CPUID`` if
+and only if KVM refuses to set EFER.LMSLE, i.e. if the CPU doesn't support long
+mode segment limits, or if nested SVM is unsupported. KVM therefore reports
+the bit on all Intel hosts, as KVM allows EFER.LMSLE only when nested SVM is
+enabled.
+
+KVM never reports the bit via ``KVM_GET_EMULATED_CPUID``, but userspace may set
+it via ``KVM_SET_CPUID2`` even on a host where KVM doesn't report it. KVM
+honors the guest's enumeration and rejects EFER.LMSLE=1 accordingly. That lets
+userspace defeature a vCPU on a host that *does* support long mode segment
+limits, so that the vCPU can later be migrated to a host that doesn't, e.g. so
+that a vCPU created on AMD Rome can be migrated to Milan and later, which
+dropped support for long mode segment limits. The opposite direction needs no
+emulation, as a host that lacks long mode segment limits already enumerates the
+defeature.
+
+Note, ``KVM_SET_MSRS`` is exempt from the check, as host-initiated MSR writes
+skip guest CPUID checks so that userspace can set MSRs before it sets guest
+CPUID. ``KVM_SET_SREGS`` and nested VMRUN are not exempt.
+
CPU topology
~~~~~~~~~~~~
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 851f151efb35b..53d205eda3359 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1171,6 +1171,10 @@ void kvm_initialize_cpu_caps(void)
F(AMD_STIBP),
F(AMD_STIBP_ALWAYS_ON),
F(AMD_IBRS_SAME_MODE),
+ /*
+ * Vendor code also sets EFER_LMSLE_MBZ if KVM itself
+ * can't support EFER.LMSLE, e.g. if nested SVM is disabled.
+ */
PASSTHROUGH_F(EFER_LMSLE_MBZ),
F(AMD_PSFD),
F(AMD_IBPB_RET),
@@ -1415,6 +1419,22 @@ static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 ind
if (kvm_cpu_cap_has(X86_FEATURE_RDTSCP))
entry->ecx = feature_bit(RDPID);
return 1;
+ case 0x80000008:
+ /*
+ * Honor the guest's EFER_LMSLE_MBZ even if the underlying CPU
+ * allows setting EFER.LMSLE, e.g. to allow migrating a vCPU
+ * between hosts with and without EFER.LMSLE support. To avoid
+ * breaking existing setups that reflect KVM's supported CPUID
+ * into the guest, KVM doesn't advertise EFER_LMSLE_MBZ unless
+ * KVM *can't* support EFER.LMSLE=1.
+ */
+ if (include_partially_emulated &&
+ !kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ)) {
+ entry->ebx |= feature_bit(EFER_LMSLE_MBZ);
+ return 1;
+ }
+ /* Nothing in 0x80000008 is fully emulated, don't emit an entry. */
+ return 0;
default:
return 0;
}
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index dd3bb04878ca6..b519fb90776e2 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -598,9 +598,19 @@ static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
if (efer & EFER_NX && !guest_cpu_cap_has(vcpu, X86_FEATURE_NX))
return false;
- return true;
+ /*
+ * EFER_LMSLE_MBZ is a "defeature" bit, i.e. is set when the CPU does
+ * *not* support long mode segment limits, and so is the only EFER
+ * check whose polarity is inverted: EFER.LMSLE is legal if and only if
+ * the guest does *not* have the defeature.
+ */
+ if (efer & EFER_LMSLE &&
+ guest_cpu_cap_has(vcpu, X86_FEATURE_EFER_LMSLE_MBZ))
+ return false;
+ return true;
}
+
bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
{
if (efer & ~kvm_caps.supported_efer_bits)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7d59d301e1e54..90a80aad672f6 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -2759,10 +2759,19 @@ static int efer_trap(struct kvm_vcpu *vcpu)
* bit in svm_set_efer(), but __kvm_valid_efer() checks it against
* whether the guest has X86_FEATURE_SVM - this avoids a failure if
* the guest doesn't have X86_FEATURE_SVM.
+ *
+ * Clear EFER_LMSLE for a related reason: EFER writes are *trapped*,
+ * not intercepted, i.e. hardware has already committed the write by
+ * the time KVM gains control, and the trap is enabled if and only if
+ * the guest is SEV-ES, whose EFER lives in the encrypted VMSA and so
+ * can't be fixed up by KVM. Rejecting EFER.LMSLE=1 would inject a #GP
+ * *and* leave EFER.LMSLE set in the guest, which is strictly worse
+ * than honoring a write that hardware itself allowed.
*/
msr_info.host_initiated = false;
msr_info.index = MSR_EFER;
- msr_info.data = to_svm(vcpu)->vmcb->control.exit_info_1 & ~EFER_SVME;
+ msr_info.data = to_svm(vcpu)->vmcb->control.exit_info_1 &
+ ~(EFER_SVME | EFER_LMSLE);
ret = kvm_set_msr_common(vcpu, &msr_info);
return kvm_complete_insn_gp(vcpu, ret);
@@ -5571,6 +5580,16 @@ static __init void svm_set_cpu_caps(void)
boot_cpu_has(X86_FEATURE_AMD_SSBD))
kvm_cpu_cap_set(X86_FEATURE_VIRT_SSBD);
+ /*
+ * Tell userspace that EFER.LMSLE must be zero if nested SVM is
+ * disabled, as KVM allows EFER.LMSLE if and only if nested SVM is
+ * supported (a historical artifact of commit eec4b140c924 ("KVM: SVM:
+ * Allow EFER.LMSLE to be set with nested svm"), not an architectural
+ * requirement).
+ */
+ if (!nested)
+ kvm_cpu_cap_set(X86_FEATURE_EFER_LMSLE_MBZ);
+
if (enable_pmu) {
/*
* Enumerate support for PERFCTR_CORE if and only if KVM has
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 612ab07d4100f..f144c1e1c63f2 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8137,6 +8137,13 @@ static __init void vmx_set_cpu_caps(void)
kvm_cpu_cap_clear(X86_FEATURE_IBT);
}
+ /*
+ * CPUID 0x80000008. Tell userspace that EFER.LMSLE must be zero; KVM
+ * never allows EFER.LMSLE to be set on Intel CPUs, as KVM supports long
+ * mode segment limits only in conjunction with nested SVM.
+ */
+ kvm_cpu_cap_set(X86_FEATURE_EFER_LMSLE_MBZ);
+
kvm_setup_xss_caps();
kvm_finalize_cpu_caps();
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 79468ddfe4736..830cb9320d892 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -6930,7 +6930,14 @@ static void kvm_setup_efer_caps(void)
if (kvm_cpu_cap_has(X86_FEATURE_SVM)) {
kvm_caps.supported_efer_bits |= EFER_SVME;
- if (!boot_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ))
+
+ /*
+ * Enumerating EFER_LMSLE_MBZ and allowing EFER.LMSLE=1
+ * would be nonsensical. Note, vendor code sets the defeature
+ * if KVM can't support EFER.LMSLE for any reason, i.e. this
+ * needs to consult KVM's capabilities, not just raw CPUID.
+ */
+ if (!kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ))
kvm_caps.supported_efer_bits |= EFER_LMSLE;
}
}
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 96bab7002d39e..2554464d8b96f 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -117,7 +117,7 @@ TEST_GEN_PROGS_x86 += x86/state_test
TEST_GEN_PROGS_x86 += x86/vmx_preemption_timer_test
TEST_GEN_PROGS_x86 += x86/svm_vmcall_test
TEST_GEN_PROGS_x86 += x86/svm_int_ctl_test
-TEST_GEN_PROGS_x86 += x86/svm_nested_clear_efer_svme
+TEST_GEN_PROGS_x86 += x86/svm_nested_efer_test
TEST_GEN_PROGS_x86 += x86/svm_nested_shutdown_test
TEST_GEN_PROGS_x86 += x86/svm_nested_soft_inject_test
TEST_GEN_PROGS_x86 += x86/svm_nested_vmcb12_gpa
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 6e6f70035508a..6f110987c51a7 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -216,6 +216,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_INVTSC KVM_X86_CPU_FEATURE(0x80000007, 0, EDX, 8)
#define X86_FEATURE_RDPRU KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 4)
#define X86_FEATURE_AMD_IBPB KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 12)
+#define X86_FEATURE_EFER_LMSLE_MBZ KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 20)
#define X86_FEATURE_NPT KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 0)
#define X86_FEATURE_LBRV KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 1)
#define X86_FEATURE_NRIPS KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 3)
@@ -1472,6 +1473,19 @@ static inline bool kvm_is_lbrv_enabled(void)
return !!get_kvm_amd_param_integer("lbrv");
}
+/*
+ * Do NOT use this to check for nVMX or nSVM support. Querying kvm_cpu_has()
+ * for either of X86_FEATURE_{VMX,SVM} is the idiomatic way to check for nested
+ * virtualization support. Use this *only* to validate KVM's own enumeration.
+ */
+static inline bool kvm_is_nested_virtualization_enabled(void)
+{
+ if (host_cpu_is_intel)
+ return get_kvm_intel_param_bool("nested");
+
+ return get_kvm_amd_param_integer("nested");
+}
+
u64 *vm_get_pte(struct kvm_vm *vm, gva_t gva);
u64 kvm_hypercall(u64 nr, u64 a0, u64 a1, u64 a2, u64 a3);
diff --git a/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c b/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c
deleted file mode 100644
index 6bc301207cbcb..0000000000000
--- a/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2026, Google LLC.
- */
-#include "kvm_util.h"
-#include "vmx.h"
-#include "svm_util.h"
-#include "kselftest.h"
-
-
-static void l2_guest_code(void)
-{
- unsigned long efer = rdmsr(MSR_EFER);
-
- /* generic_svm_setup() initializes EFER_SVME set for L2 */
- GUEST_ASSERT(efer & EFER_SVME);
- wrmsr(MSR_EFER, efer & ~EFER_SVME);
-
- /* Unreachable, L1 should be shutdown */
- GUEST_ASSERT(0);
-}
-
-static void l1_guest_code(struct svm_test_data *svm)
-{
- generic_svm_setup(svm, l2_guest_code);
- run_guest(svm->vmcb, svm->vmcb_gpa);
-
- /* Unreachable, L1 should be shutdown */
- GUEST_ASSERT(0);
-}
-
-int main(int argc, char *argv[])
-{
- struct kvm_vcpu *vcpu;
- struct kvm_vm *vm;
- gva_t nested_gva = 0;
-
- TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
-
- vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
-
- vcpu_alloc_svm(vm, &nested_gva);
- vcpu_args_set(vcpu, 1, nested_gva);
-
- vcpu_run(vcpu);
- TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
-
- kvm_vm_free(vm);
- return 0;
-}
diff --git a/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c b/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c
new file mode 100644
index 0000000000000..fa0d76a9c390b
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Tests for KVM's handling of EFER bits whose behavior is tied to nested SVM.
+ *
+ * Copyright (C) 2026, Google LLC.
+ */
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "svm_util.h"
+#include "kselftest.h"
+
+static bool l2_ran;
+
+static void l2_clear_efer_svme(void)
+{
+ u64 efer = rdmsr(MSR_EFER);
+
+ /* generic_svm_setup() initializes EFER_SVME set for L2 */
+ GUEST_ASSERT(efer & EFER_SVME);
+ wrmsr(MSR_EFER, efer & ~EFER_SVME);
+
+ /* Unreachable, L1 should be shutdown */
+ GUEST_ASSERT(0);
+}
+
+static void l1_clear_efer_svme(struct svm_test_data *svm)
+{
+ generic_svm_setup(svm, l2_clear_efer_svme);
+ run_guest(svm->vmcb, svm->vmcb_gpa);
+
+ /* Unreachable, L1 should be shutdown */
+ GUEST_ASSERT(0);
+}
+
+static void l2_lmsle(void)
+{
+ GUEST_ASSERT(rdmsr(MSR_EFER) & EFER_LMSLE);
+ l2_ran = true;
+ vmmcall();
+}
+
+static void l1_lmsle(struct svm_test_data *svm)
+{
+ bool lmsle_mbz = this_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ);
+ struct vmcb *vmcb = svm->vmcb;
+ u64 efer = rdmsr(MSR_EFER);
+
+ /*
+ * Selftests' vCPUs are created with EFER.LMSLE clear; the sub-tests
+ * below need to start from a clean slate.
+ */
+ GUEST_ASSERT(!(efer & EFER_LMSLE));
+ GUEST_ASSERT(!l2_ran);
+
+ /*
+ * Per the APM, if EFER_LMSLE_MBZ is enumerated in CPUID, "64-bit mode
+ * segment limit checking is not supported and attempting to set
+ * EFER.LMSLE = 1 causes a #GP exception".
+ */
+ if (lmsle_mbz) {
+ GUEST_ASSERT_EQ(wrmsr_safe(MSR_EFER, efer | EFER_LMSLE), GP_VECTOR);
+ GUEST_ASSERT(!(rdmsr(MSR_EFER) & EFER_LMSLE));
+ } else {
+ GUEST_ASSERT_EQ(wrmsr_safe(MSR_EFER, efer | EFER_LMSLE), 0);
+ GUEST_ASSERT(rdmsr(MSR_EFER) & EFER_LMSLE);
+
+ /*
+ * Restore EFER so that generic_svm_setup() doesn't propagate
+ * EFER.LMSLE into vmcb12 on its own, i.e. so that the VMRUN
+ * sub-test actually tests what it thinks it's testing.
+ */
+ wrmsr(MSR_EFER, efer);
+ }
+
+ /*
+ * VMRUN's consistency checks reject "any MBZ bit of EFER", i.e. a
+ * vmcb12 with EFER.LMSLE set must generate VMEXIT_INVALID when the
+ * defeature is enumerated.
+ */
+ generic_svm_setup(svm, l2_lmsle);
+ vmcb->save.efer |= EFER_LMSLE;
+ run_guest(vmcb, svm->vmcb_gpa);
+
+ if (lmsle_mbz) {
+ GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_ERR);
+ GUEST_ASSERT(!l2_ran);
+ } else {
+ GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL);
+ GUEST_ASSERT(l2_ran);
+ GUEST_ASSERT(vmcb->save.efer & EFER_LMSLE);
+ }
+
+ GUEST_DONE();
+}
+
+static struct kvm_vcpu *create_l1_vcpu(struct kvm_vm **vm, void *l1_guest_code)
+{
+ struct kvm_vcpu *vcpu;
+ gva_t svm_gva;
+
+ *vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
+
+ vcpu_alloc_svm(*vm, &svm_gva);
+ vcpu_args_set(vcpu, 1, svm_gva);
+
+ return vcpu;
+}
+
+static void test_enumeration(void)
+{
+ bool lmsle_mbz;
+
+ /*
+ * EFER_LMSLE_MBZ, CPUID.80000008H:EBX[bit 20], is a "defeature" bit,
+ * i.e. is set when the CPU does *not* support long mode segment
+ * limits. KVM enumerates the defeature if and only if KVM refuses to
+ * set EFER.LMSLE, i.e. if the CPU doesn't support LMSLE, or if KVM
+ * doesn't support nested SVM. Derive the expectation from raw CPUID
+ * and kvm_amd's "nested" module param rather than from
+ * kvm_cpu_has(X86_FEATURE_SVM), so that the assertion doesn't simply
+ * compare KVM's enumeration to itself.
+ */
+ lmsle_mbz = this_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ) ||
+ !this_cpu_has(X86_FEATURE_SVM) ||
+ !kvm_is_nested_virtualization_enabled();
+
+ TEST_ASSERT_EQ(kvm_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ), lmsle_mbz);
+
+ ksft_test_result_pass("KVM enumerates EFER_LMSLE_MBZ=%d\n", lmsle_mbz);
+}
+
+static void test_clear_efer_svme(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ vcpu = create_l1_vcpu(&vm, l1_clear_efer_svme);
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("L2 clearing EFER.SVME shuts down L1\n");
+}
+
+static void test_lmsle(bool lmsle_mbz)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+
+ vcpu = create_l1_vcpu(&vm, l1_lmsle);
+
+ vcpu_set_or_clear_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ,
+ lmsle_mbz);
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ case UCALL_DONE:
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
+ }
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("Guest EFER_LMSLE_MBZ=%d\n", lmsle_mbz);
+}
+
+static void test_host_initiated_lmsle(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ u64 efer;
+
+ vm = vm_create_with_one_vcpu(&vcpu, NULL);
+ vcpu_set_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ);
+
+ /*
+ * EFER_LMSLE_MBZ is a guest CPUID consistency check, not a host
+ * capability, i.e. must not be enforced against host-initiated writes,
+ * so that userspace can set MSRs before it sets guest CPUID.
+ */
+ efer = vcpu_get_msr(vcpu, MSR_EFER);
+ TEST_ASSERT(!(efer & EFER_LMSLE), "EFER.LMSLE unexpectedly set");
+
+ vcpu_set_msr(vcpu, MSR_EFER, efer | EFER_LMSLE);
+ TEST_ASSERT_EQ(vcpu_get_msr(vcpu, MSR_EFER), efer | EFER_LMSLE);
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("Host-initiated EFER.LMSLE=1 is allowed\n");
+}
+
+static void test_sregs_lmsle(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_sregs sregs;
+ struct kvm_vm *vm;
+ int rc;
+
+ vm = vm_create_with_one_vcpu(&vcpu, NULL);
+ vcpu_set_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ);
+
+ /*
+ * Unlike KVM_SET_MSRS, KVM_SET_SREGS runs the full set of guest CPUID
+ * checks, i.e. rejects EFER.LMSLE even though it's host-initiated.
+ */
+ vcpu_sregs_get(vcpu, &sregs);
+ TEST_ASSERT(!(sregs.efer & EFER_LMSLE), "EFER.LMSLE unexpectedly set");
+
+ sregs.efer |= EFER_LMSLE;
+ rc = _vcpu_sregs_set(vcpu, &sregs);
+ TEST_ASSERT(rc, "KVM allowed EFER.LMSLE with EFER_LMSLE_MBZ set");
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("KVM_SET_SREGS rejects EFER.LMSLE=1\n");
+}
+
+int main(int argc, char *argv[])
+{
+ bool has_nested_svm, has_lmsle;
+
+ ksft_print_header();
+ ksft_set_plan(6);
+
+ test_enumeration();
+
+ /*
+ * The sub-tests below need to actually run a nested guest, and the
+ * EFER.LMSLE sub-tests additionally need KVM to allow EFER.LMSLE.
+ * It's KVM's view of the world, not raw CPUID, that dictates whether
+ * EFER.LMSLE is allowed, i.e. whether the defeature is emulated.
+ */
+ has_nested_svm = kvm_cpu_has(X86_FEATURE_SVM);
+ has_lmsle = has_nested_svm &&
+ !kvm_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ);
+
+ if (!has_nested_svm)
+ ksft_print_msg("Nested SVM unsupported\n");
+ else if (!has_lmsle)
+ ksft_print_msg("KVM doesn't support EFER.LMSLE\n");
+
+ if (has_nested_svm) {
+ test_clear_efer_svme();
+ test_lmsle(true);
+ } else {
+ ksft_test_result_skip("L2 clearing EFER.SVME shuts down L1\n");
+ ksft_test_result_skip("Guest EFER_LMSLE_MBZ=1\n");
+ }
+
+ if (has_lmsle) {
+ test_lmsle(false);
+ test_host_initiated_lmsle();
+ test_sregs_lmsle();
+ } else {
+ ksft_test_result_skip("Guest EFER_LMSLE_MBZ=0\n");
+ ksft_test_result_skip("Host-initiated EFER.LMSLE=1 is allowed\n");
+ ksft_test_result_skip("KVM_SET_SREGS rejects EFER.LMSLE=1\n");
+ }
+
+ ksft_finished();
+}
|
| 3/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 11095 output: 37 thoughts: 1413 Thoughts: **KVM Patch Analysis: Worthy of Fuzzing?** Alright, let's break this down. This patch is knee-deep in KVM on x86, specifically tweaking `cpuid`, MSRs, and SVM/VMX handling, along with associated documentation and selftests. My initial thought is to figure out if these changes are even *reachable* in a standard virtualized environment – and whether they're worth the fuzzing effort. First, I need to look carefully at the diff. I see changes in the documentation (`api.rst`), so that is not directly relevant for fuzzing, although changes in documentation can provide valuable information on functionality and may indirectly identify fuzzing targets. The core interest lies in `cpuid.c`, `msrs.c`, `svm.c`, `vmx.c`, and `x86.c`. I note that `kvm_initialize_cpu_caps` is setting `PASSTHROUGH_F(EFER_LMSLE_MBZ)` and `cpuid_func_emulated` is handling `0x80000008`, while `__kvm_valid_efer` is adding a new check related to `EFER_LMSLE_MBZ`. `efer_trap` is modifying `exit_info_1`, and functions like `svm_set_cpu_caps` and `vmx_set_cpu_caps` are setting `X86_FEATURE_EFER_LMSLE_MBZ`, and `kvm_setup_efer_caps` is manipulating `supported_efer_bits`. The key question is, is this accessible in a typical QEMU or GCE setup? And the answer is likely yes, because KVM is a known target for syzkaller, especially if `/dev/kvm` is available, whether through nested virtualization or software emulation. Syzkaller, with its `syz_kvm_*` pseudo-syscalls, directly interacts with `/dev/kvm` on x86. So, standard QEMU or GCE setups often *do* allow this level of interaction. Now, let's consult the guidelines. The first section emphasizes reachability: bugs can only be found in code that executes in standard virtualized environments. The second details when to *return false* – that is, when the code is not worth fuzzing. I don't see any "negative criteria" here: these aren't non-kernel changes, unreachable hardware, or inaccessible execution paths. The target architecture is amd64. Finally, the third section spells out the positive criteria: KVM is a core virtualization subsystem (`arch/x86/kvm/`), and these modifications are relevant because: - `cpuid_func_emulated` is called when the `KVM_GET_EMULATED_CPUID` ioctl is used. - `__kvm_valid_efer` is called on `KVM_SET_SREGS`, `KVM_SET_SREGS2`, guest MSR writes via `wrmsr` to `MSR_EFER`, or via `kvm_set_msr`. - `efer_trap` in svm - `kvm_setup_efer_caps` and `kvm_initialize_cpu_caps` happen during KVM initialization. Therefore, because this is a core subsystem and these functions are reachable through ioctls and MSR manipulation, it is *definitely* worth fuzzing. I'll now need to check caller functions with `grepper` or by inspecting the source directly to confirm the reachability, the static/non-static status, and the path to trigger these modifications, but the initial assessment is positive. |
| 4/2 | 2026/10/01 22:00 | tool | grepper |
0mArgs:{
"Expression": "__kvm_valid_efer",
"PathPrefix": "arch/x86/kvm/"
}Results: {
"Output": "arch/x86/kvm/msrs.c=577=static int do_get_feature_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)\n--\narch/x86/kvm/msrs.c-582-\narch/x86/kvm/msrs.c:583:static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\narch/x86/kvm/msrs.c-584-{\n--\narch/x86/kvm/msrs.c=614=bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\n--\narch/x86/kvm/msrs.c-618-\narch/x86/kvm/msrs.c:619:\treturn __kvm_valid_efer(vcpu, efer);\narch/x86/kvm/msrs.c-620-}\n--\narch/x86/kvm/msrs.c=623=static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-632-\tif (!msr_info-\u003ehost_initiated) {\narch/x86/kvm/msrs.c:633:\t\tif (!__kvm_valid_efer(vcpu, efer))\narch/x86/kvm/msrs.c-634-\t\t\treturn 1;\n--\narch/x86/kvm/svm/svm.c=2752=static int efer_trap(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/svm.c-2758-\t * Clear the EFER_SVME bit from EFER. The SVM code always sets this\narch/x86/kvm/svm/svm.c:2759:\t * bit in svm_set_efer(), but __kvm_valid_efer() checks it against\narch/x86/kvm/svm/svm.c-2760-\t * whether the guest has X86_FEATURE_SVM - this avoids a failure if\n"
} |
| 5/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13940 output: 36 thoughts: 15 Thoughts: Here's my take on those thoughts: **Investigating the `kvm_valid_efer` Callers** Alright, let's dive in. My immediate thought is to trace the callers of `kvm_valid_efer`. I need to understand *who* is using this function, and more importantly, *why*. Knowing the call sites will be critical to understand how this function is intended to behave. It's the first step in digging deeper. I need to get a handle on the landscape before making any assumptions. Gotta see the context! |
| 6/2 | 2026/10/01 22:00 | tool | grepper |
0mArgs:{
"Expression": "kvm_valid_efer",
"PathPrefix": "arch/x86/kvm/"
}Results: {
"Output": "arch/x86/kvm/msrs.c=577=static int do_get_feature_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)\n--\narch/x86/kvm/msrs.c-582-\narch/x86/kvm/msrs.c:583:static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\narch/x86/kvm/msrs.c-584-{\n--\narch/x86/kvm/msrs.c-613-\narch/x86/kvm/msrs.c:614:bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\narch/x86/kvm/msrs.c-615-{\n--\narch/x86/kvm/msrs.c-618-\narch/x86/kvm/msrs.c:619:\treturn __kvm_valid_efer(vcpu, efer);\narch/x86/kvm/msrs.c-620-}\narch/x86/kvm/msrs.c:621:EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_valid_efer);\narch/x86/kvm/msrs.c-622-\narch/x86/kvm/msrs.c=623=static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-632-\tif (!msr_info-\u003ehost_initiated) {\narch/x86/kvm/msrs.c:633:\t\tif (!__kvm_valid_efer(vcpu, efer))\narch/x86/kvm/msrs.c-634-\t\t\treturn 1;\n--\narch/x86/kvm/msrs.h=58=int kvm_get_reg_list(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/msrs.h-60-\narch/x86/kvm/msrs.h:61:bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer);\narch/x86/kvm/msrs.h-62-int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data);\n--\narch/x86/kvm/regs.c=545=static bool kvm_is_valid_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs)\n--\narch/x86/kvm/regs.c-567-\t kvm_is_valid_cr0(vcpu, sregs-\u003ecr0) \u0026\u0026\narch/x86/kvm/regs.c:568:\t kvm_valid_efer(vcpu, sregs-\u003eefer);\narch/x86/kvm/regs.c-569-}\n--\narch/x86/kvm/svm/nested.c=438=static bool nested_vmcb_check_save(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/svm/nested.c-471-\narch/x86/kvm/svm/nested.c:472:\tif (CC(!kvm_valid_efer(vcpu, save-\u003eefer)))\narch/x86/kvm/svm/nested.c-473-\t\treturn false;\n--\narch/x86/kvm/svm/svm.c=2752=static int efer_trap(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/svm.c-2758-\t * Clear the EFER_SVME bit from EFER. The SVM code always sets this\narch/x86/kvm/svm/svm.c:2759:\t * bit in svm_set_efer(), but __kvm_valid_efer() checks it against\narch/x86/kvm/svm/svm.c-2760-\t * whether the guest has X86_FEATURE_SVM - this avoids a failure if\n--\narch/x86/kvm/vmx/nested.c=3175=static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/vmx/nested.c-3237-\tif (vmcs12-\u003evm_exit_controls \u0026 VM_EXIT_LOAD_IA32_EFER) {\narch/x86/kvm/vmx/nested.c:3238:\t\tif (CC(!kvm_valid_efer(vcpu, vmcs12-\u003ehost_ia32_efer)) ||\narch/x86/kvm/vmx/nested.c-3239-\t\t CC(ia32e != !!(vmcs12-\u003ehost_ia32_efer \u0026 EFER_LMA)) ||\n--\narch/x86/kvm/vmx/nested.c=3311=static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/vmx/nested.c-3376-\t (vmcs12-\u003evm_entry_controls \u0026 VM_ENTRY_LOAD_IA32_EFER)) {\narch/x86/kvm/vmx/nested.c:3377:\t\tif (CC(!kvm_valid_efer(vcpu, vmcs12-\u003eguest_ia32_efer)) ||\narch/x86/kvm/vmx/nested.c-3378-\t\t CC(ia32e != !!(vmcs12-\u003eguest_ia32_efer \u0026 EFER_LMA)) ||\n"
} |
| 7/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 15629 output: 36 thoughts: 159 Thoughts: Here's my analysis of the provided information: **Understanding EFER Validation in KVM** Okay, so I see `kvm_valid_efer` is a key function related to validating the Extended Feature Enable Register (EFER) within KVM. This is pretty standard for virtualization, as you need to carefully control and validate modifications to system registers that can drastically change the behavior of the guest. My analysis begins by considering the contexts where `kvm_valid_efer` is being called. Specifically, I note it’s invoked by `kvm_is_valid_sregs`, which is a central part of both `KVM_SET_SREGS` and `KVM_SET_SREGS2` ioctls. That means these ioctls are using this validation before setting the guest’s system registers. This seems sensible, as you don't want the user space to be able to set illegal EFER values and break things. I also see that it is also called by `nested_vmcb_check_save` during nested SVM VMRUN, as well as by the `nested_vmx_check_host_state` and `nested_vmx_check_guest_state` routines during nested VMX operation. This indicates that these checks are performed for both host and guest states when dealing with nested virtualization. This makes sense; nested virtualization introduces even more complexity in controlling the EFER value. Furthermore, I note the presence of `__kvm_valid_efer` which is called directly by `set_efer` when the guest executes a `WRMSR` instruction to `MSR_EFER` (or via KVM emulation). This makes sense; it ensures the KVM kernel module validates the EFER value before allowing it to be changed. My next step would be to delve into `cpuid_func_emulated`. I need to figure out how EFER is related to what this function does. My guess is that it would probably handle emulating CPUID leaf functions that might be affected by EFER settings, such as those related to paging, long mode, or other architectural features. From my experience, you have to be careful about CPUID emulation and ensure it reflects the effective state of the guest, which is strongly influenced by EFER. |
| 8/2 | 2026/10/01 22:00 | tool | grepper |
0mArgs:{
"Expression": "cpuid_func_emulated",
"PathPrefix": "arch/x86/kvm/"
}Results: {
"Output": "arch/x86/kvm/cpuid.c=356=static u32 cpuid_get_reg_unsafe(struct kvm_cpuid_entry2 *entry, u32 reg)\n--\narch/x86/kvm/cpuid.c-372-\narch/x86/kvm/cpuid.c:373:static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 index,\narch/x86/kvm/cpuid.c-374-\t\t\t bool include_partially_emulated);\n--\narch/x86/kvm/cpuid.c=376=void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/cpuid.c-402-\narch/x86/kvm/cpuid.c:403:\t\tcpuid_func_emulated(\u0026emulated, cpuid.function, cpuid.index, true);\narch/x86/kvm/cpuid.c-404-\n--\narch/x86/kvm/cpuid.c=1343=static struct kvm_cpuid_entry2 *do_host_cpuid(struct kvm_cpuid_array *array,\n--\narch/x86/kvm/cpuid.c-1385-\narch/x86/kvm/cpuid.c:1386:static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 index,\narch/x86/kvm/cpuid.c-1387-\t\t\t bool include_partially_emulated)\n--\narch/x86/kvm/cpuid.c-1442-\narch/x86/kvm/cpuid.c:1443:static int __do_cpuid_func_emulated(struct kvm_cpuid_array *array, u32 func)\narch/x86/kvm/cpuid.c-1444-{\n--\narch/x86/kvm/cpuid.c-1447-\narch/x86/kvm/cpuid.c:1448:\tarray-\u003enent += cpuid_func_emulated(\u0026array-\u003eentries[array-\u003enent], func, 0, false);\narch/x86/kvm/cpuid.c-1449-\treturn 0;\n--\narch/x86/kvm/cpuid.c=1949=static int do_cpuid_func(struct kvm_cpuid_array *array, u32 func,\n--\narch/x86/kvm/cpuid.c-1952-\tif (type == KVM_GET_EMULATED_CPUID)\narch/x86/kvm/cpuid.c:1953:\t\treturn __do_cpuid_func_emulated(array, func);\narch/x86/kvm/cpuid.c-1954-\n"
} |
| 9/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 17086 output: 41 thoughts: 70 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into KVM CPUID Emulation** Alright, let's get down to brass tacks. I'm taking a look at the execution flow *after* the `kvm_vcpu_after_set_cpuid` function call – that's where the CPUID emulation magic happens. My gut tells me this is the point where we're actually *doing* something with the emulated CPUID values. Specifically, I see a call to `cpuid_func_emulated`. I need to figure out how `emulated` is used and where these functions are getting called from. The function's signature suggests it handles the core logic for translating the requested CPUID function, index, and potentially some flags for further manipulation. I'm guessing that 'true' argument at the end signals that this should apply a mask or other type of filter. To really understand what's going on, I need to dig into the implementation of `cpuid_func_emulated`. Looks like the main meat and potatoes of the emulation process itself is happening at around line 400 in `arch/x86/kvm/cpuid.c`. I'm going to start there and trace the logic through, paying close attention to how it handles different CPUID functions and the role of the `emulated` structure. This likely contains the critical details for how we inject the emulated values. |
| 10/2 | 2026/10/01 22:00 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/cpuid.c",
"FirstLine": 395,
"LineCount": 25
}Results: {
"Contents": " 395:\t\n 396:\t\t\tif (!cpuid.function)\n 397:\t\t\t\tcontinue;\n 398:\t\n 399:\t\t\tentry = kvm_find_cpuid_entry_index(vcpu, cpuid.function, cpuid.index);\n 400:\t\t\tif (!entry)\n 401:\t\t\t\tcontinue;\n 402:\t\n 403:\t\t\tcpuid_func_emulated(\u0026emulated, cpuid.function, cpuid.index, true);\n 404:\t\n 405:\t\t\t/*\n 406:\t\t\t * A vCPU has a feature if it's supported by KVM and is enabled\n 407:\t\t\t * in guest CPUID. Note, this includes features that are\n 408:\t\t\t * supported by KVM but aren't advertised to userspace!\n 409:\t\t\t */\n 410:\t\t\tvcpu-\u003earch.cpu_caps[i] = kvm_cpu_caps[i] |\n 411:\t\t\t\t\t\t cpuid_get_reg_unsafe(\u0026emulated, cpuid.reg);\n 412:\t\t\tvcpu-\u003earch.cpu_caps[i] \u0026= cpuid_get_reg_unsafe(entry, cpuid.reg);\n 413:\t\t}\n 414:\t\n 415:\t\tkvm_update_cpuid_runtime(vcpu);\n 416:\t\n 417:\t\t/*\n 418:\t\t * If TDP is enabled, let the guest use GBPAGES if they're supported in\n 419:\t\t * hardware. The hardware page walker doesn't let KVM disable GBPAGES,\n"
} |
| 11/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 17931 output: 41 thoughts: 1923 Thoughts: **Focusing on KVM EFER and CPUID Emulation for Fuzzing** Okay, so `kvm_vcpu_after_set_cpuid` is the function that gets called after CPUID is configured for a vCPU, and it's key because it calls `cpuid_func_emulated`. Specifically, the code within `cpuid_func_emulated` at 0x80000008, when `include_partially_emulated` is true and a specific feature bit isn't already set, it modifies the CPUID data. This modification, in turn, updates `vcpu->arch.cpu_caps[i]`. This looks promising. The critical point is that this change affects how EFER is validated later through `guest_cpu_cap_has` when EFER is interacted with (e.g., via `KVM_SET_SREGS`, `KVM_SET_SREGS2`, or `wrmsr` to `MSR_EFER`). If `EFER_LMSLE` is set, `__kvm_valid_efer` will return false. Given the target architecture is amd64, and we know standard environments like syzkaller heavily fuzz KVM through ioctls and MSR accesses, this looks like a prime target for fuzzing. The patch directly modifies core virtualization logic in `arch/x86/kvm` dealing with state validation and register handling. This definitely warrants further investigation. The criteria for "WorthFuzzing" are met. The patch alters EFER validation and CPUID emulation. It specifically allows the guest to manipulate the `EFER_LMSLE_MBZ` bit to disable `EFER.LMSLE` even on host systems that support it. This enforcement happens via `__kvm_valid_efer` and `kvm_valid_efer` on MSR writes and `KVM_SET_SREGS`, along with KVM capability initialization and SVM EFER trap handling. The fact that these paths are directly reachable and exercised by `KVM_SET_CPUID2`, `KVM_SET_SREGS`, `KVM_RUN` ioctls makes this even more appealing. Now, let's identify the specific functions to focus on. Since we are guiding a fuzzer, we need to pick specific feature entrypoints. `kvm_valid_efer` is the non-static function in `arch/x86/kvm/msrs.c` that validates EFER. `kvm_valid_efer` is called by `kvm_arch_vcpu_ioctl_set_sregs`, nested VMs, which makes it a good focus. `__kvm_valid_efer` is a static function called by `set_efer`, but since the latter is called by non-static `kvm_set_msr_common`, `kvm_valid_efer` is safer and a good place to focus. Since `kvm_vcpu_after_set_cpuid` calls `cpuid_func_emulated`, and it's non-static, it can also be added as a good focus. And static `efer_trap` in `arch/x86/kvm/svm/svm.c` is also a possible target. `kvm_setup_efer_caps`, `svm_set_cpu_caps` and `vmx_set_cpu_caps` will be called during initialization and are worth fuzzing as well. `set_efer` is called from `kvm_set_msr_common`, making it worth fuzzing too. So the FocusSymbols should be: 1. `kvm_valid_efer` 2. `kvm_vcpu_after_set_cpuid` 3. `set_efer` 4. `efer_trap` 5. `kvm_setup_efer_caps` 6. `svm_set_cpu_caps` 7. `vmx_set_cpu_caps` |
| 12/2 | 2026/10/01 22:00 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/msrs.c",
"FirstLine": 610,
"LineCount": 40
}Results: {
"Contents": " 610:\t\n 611:\t\treturn true;\n 612:\t}\n 613:\t\n 614:\tbool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\n 615:\t{\n 616:\t\tif (efer \u0026 ~kvm_caps.supported_efer_bits)\n 617:\t\t\treturn false;\n 618:\t\n 619:\t\treturn __kvm_valid_efer(vcpu, efer);\n 620:\t}\n 621:\tEXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_valid_efer);\n 622:\t\n 623:\tstatic int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n 624:\t{\n 625:\t\tu64 old_efer = vcpu-\u003earch.efer;\n 626:\t\tu64 efer = msr_info-\u003edata;\n 627:\t\tint r;\n 628:\t\n 629:\t\tif (efer \u0026 ~kvm_caps.supported_efer_bits)\n 630:\t\t\treturn 1;\n 631:\t\n 632:\t\tif (!msr_info-\u003ehost_initiated) {\n 633:\t\t\tif (!__kvm_valid_efer(vcpu, efer))\n 634:\t\t\t\treturn 1;\n 635:\t\n 636:\t\t\tif (is_paging(vcpu) \u0026\u0026\n 637:\t\t\t (vcpu-\u003earch.efer \u0026 EFER_LME) != (efer \u0026 EFER_LME))\n 638:\t\t\t\treturn 1;\n 639:\t\t}\n 640:\t\n 641:\t\tefer \u0026= ~EFER_LMA;\n 642:\t\tefer |= vcpu-\u003earch.efer \u0026 EFER_LMA;\n 643:\t\n 644:\t\tr = kvm_x86_call(set_efer)(vcpu, efer);\n 645:\t\tif (r) {\n 646:\t\t\tWARN_ON(r \u003e 0);\n 647:\t\t\treturn r;\n 648:\t\t}\n 649:\t\n"
} |
| 13/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 21397 output: 33 thoughts: 13 Thoughts: Okay, here's my interpretation of what's going on with `kvm_set_msr_common`, presented as if I were thinking it: **Deconstructing `kvm_set_msr_common`** Alright, let's dive into `kvm_set_msr_common`. My initial impression is that this function is the core of setting Model-Specific Registers (MSRs) within a KVM (Kernel-based Virtual Machine) environment. It’s the gatekeeper, handling the low-level interactions required to modify MSR values for a virtual CPU. Given its name, it’s highly likely a function that centralizes the logic for the *common* aspects of MSR writes, potentially abstracting away details that differ between different MSRs or CPU models. I'd expect to see a good deal of error handling here – MSR writes are sensitive operations. Things like checking for write permissions, validating the provided MSR index against the guest's CPUID features, and ensuring the provided value is within allowed ranges would all be critical. Security is paramount here; if this function isn’t robust, it could be a gaping hole. Furthermore, I anticipate some interaction with the hardware virtualization extensions, potentially using `vmwrite` or equivalent instruction to perform the actual MSR write within the guest's virtualized context. I'll need to see how they're handling potential conflicts with other VMM (Virtual Machine Monitor) operations or concurrent access to the same MSR. Lock management might be an important consideration. It's probable that this function handles the translation between the guest-visible MSR index and any host-internal representation. There might be some architecture-specific translation involved, particularly if the host and guest are different architectures, or if there's any emulation of MSRs. I'm curious about the function's parameter passing. How are the MSR index, the value to write, and possibly other context (like the vCPU) being passed? Are they using a structure, individual parameters, or some clever use of the stack? That often gives clues about its design efficiency and how it handles potential future extensions. This is where I'd look to understand how it's designed to handle multiple vCPUs and the potential for concurrent access. |
| 14/2 | 2026/10/01 22:00 | tool | grepper |
0mArgs:{
"Expression": "set_efer",
"PathPrefix": "arch/x86/kvm/"
}Results: {
"Output": "arch/x86/kvm/msrs.c=621=EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_valid_efer);\narch/x86/kvm/msrs.c-622-\narch/x86/kvm/msrs.c:623:static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\narch/x86/kvm/msrs.c-624-{\n--\narch/x86/kvm/msrs.c-643-\narch/x86/kvm/msrs.c:644:\tr = kvm_x86_call(set_efer)(vcpu, efer);\narch/x86/kvm/msrs.c-645-\tif (r) {\n--\narch/x86/kvm/msrs.c=1452=int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-1549-\tcase MSR_EFER:\narch/x86/kvm/msrs.c:1550:\t\treturn set_efer(vcpu, msr_info);\narch/x86/kvm/msrs.c-1551-\tcase MSR_K7_HWCR: {\n--\narch/x86/kvm/regs.c=571=static int __set_sregs_common(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs,\n--\narch/x86/kvm/regs.c-599-\t*mmu_reset_needed |= vcpu-\u003earch.efer != sregs-\u003eefer;\narch/x86/kvm/regs.c:600:\tkvm_x86_call(set_efer)(vcpu, sregs-\u003eefer);\narch/x86/kvm/regs.c-601-\n--\narch/x86/kvm/smm.c=279=void enter_smm(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/smm.c-361-\tif (guest_cpu_cap_has(vcpu, X86_FEATURE_LM))\narch/x86/kvm/smm.c:362:\t\tif (kvm_x86_call(set_efer)(vcpu, 0))\narch/x86/kvm/smm.c-363-\t\t\tgoto error;\n--\narch/x86/kvm/svm/nested.c=744=static void nested_vmcb02_prepare_save(struct vcpu_svm *svm)\n--\narch/x86/kvm/svm/nested.c-791-\narch/x86/kvm/svm/nested.c:792:\tsvm_set_efer(vcpu, svm-\u003enested.save.efer);\narch/x86/kvm/svm/nested.c-793-\n--\narch/x86/kvm/svm/nested.c=1317=void nested_svm_vmexit(struct vcpu_svm *svm)\n--\narch/x86/kvm/svm/nested.c-1422-\tkvm_set_rflags(vcpu, vmcb01-\u003esave.rflags);\narch/x86/kvm/svm/nested.c:1423:\tsvm_set_efer(vcpu, vmcb01-\u003esave.efer);\narch/x86/kvm/svm/nested.c-1424-\tsvm_set_cr0(vcpu, vmcb01-\u003esave.cr0 | X86_CR0_PE);\n--\narch/x86/kvm/svm/svm.c=201=static int get_npt_level(void)\n--\narch/x86/kvm/svm/svm.c-209-\narch/x86/kvm/svm/svm.c:210:int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)\narch/x86/kvm/svm/svm.c-211-{\n--\narch/x86/kvm/svm/svm.c=2752=static int efer_trap(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/svm.c-2758-\t * Clear the EFER_SVME bit from EFER. The SVM code always sets this\narch/x86/kvm/svm/svm.c:2759:\t * bit in svm_set_efer(), but __kvm_valid_efer() checks it against\narch/x86/kvm/svm/svm.c-2760-\t * whether the guest has X86_FEATURE_SVM - this avoids a failure if\n--\narch/x86/kvm/svm/svm.c=5337=struct kvm_x86_ops svm_x86_ops __initdata = {\n--\narch/x86/kvm/svm/svm.c-5378-\t.set_cr4 = svm_set_cr4,\narch/x86/kvm/svm/svm.c:5379:\t.set_efer = svm_set_efer,\narch/x86/kvm/svm/svm.c-5380-\t.get_idt = svm_get_idt,\n--\narch/x86/kvm/svm/svm.h=811=void svm_update_lbrv(struct kvm_vcpu *vcpu);\narch/x86/kvm/svm/svm.h-812-\narch/x86/kvm/svm/svm.h:813:int svm_set_efer(struct kvm_vcpu *vcpu, u64 efer);\narch/x86/kvm/svm/svm.h-814-void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);\n--\narch/x86/kvm/vmx/main.c=421=static void vt_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)\n--\narch/x86/kvm/vmx/main.c-428-\narch/x86/kvm/vmx/main.c:429:static int vt_set_efer(struct kvm_vcpu *vcpu, u64 efer)\narch/x86/kvm/vmx/main.c-430-{\n--\narch/x86/kvm/vmx/main.c-433-\narch/x86/kvm/vmx/main.c:434:\treturn vmx_set_efer(vcpu, efer);\narch/x86/kvm/vmx/main.c-435-}\n--\narch/x86/kvm/vmx/main.c=888=struct kvm_x86_ops vt_x86_ops __initdata = {\n--\narch/x86/kvm/vmx/main.c-932-\t.set_cr4 = vt_op(set_cr4),\narch/x86/kvm/vmx/main.c:933:\t.set_efer = vt_op(set_efer),\narch/x86/kvm/vmx/main.c-934-\t.get_idt = vt_op(get_idt),\n--\narch/x86/kvm/vmx/nested.c=2431=static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs01,\n--\narch/x86/kvm/vmx/nested.c-2544-\t * vmcs12's VM_{ENTRY,EXIT}_LOAD_IA32_EFER and VM_ENTRY_IA32E_MODE\narch/x86/kvm/vmx/nested.c:2545:\t * are emulated by vmx_set_efer() in prepare_vmcs02(), but speculate\narch/x86/kvm/vmx/nested.c-2546-\t * on the related bits (if supported by the CPU) in the hope that\narch/x86/kvm/vmx/nested.c:2547:\t * we can avoid VMWrites during vmx_set_efer().\narch/x86/kvm/vmx/nested.c-2548-\t *\n--\narch/x86/kvm/vmx/nested.c-2569-\t * we should use its exit controls. Note that VM_EXIT_LOAD_IA32_EFER\narch/x86/kvm/vmx/nested.c:2570:\t * bits may be modified by vmx_set_efer() in prepare_vmcs02().\narch/x86/kvm/vmx/nested.c-2571-\t */\n--\narch/x86/kvm/vmx/nested.c=2760=static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,\n--\narch/x86/kvm/vmx/nested.c-2843-\t/* Note: may modify VM_ENTRY/EXIT_CONTROLS and GUEST/HOST_IA32_EFER */\narch/x86/kvm/vmx/nested.c:2844:\tvmx_set_efer(vcpu, vcpu-\u003earch.efer);\narch/x86/kvm/vmx/nested.c-2845-\n--\narch/x86/kvm/vmx/nested.c=4800=static void load_vmcs12_host_state(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/vmx/nested.c-4811-\t\tvcpu-\u003earch.efer \u0026= ~(EFER_LMA | EFER_LME);\narch/x86/kvm/vmx/nested.c:4812:\tvmx_set_efer(vcpu, vcpu-\u003earch.efer);\narch/x86/kvm/vmx/nested.c-4813-\n--\narch/x86/kvm/vmx/nested.c=4960=static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/vmx/nested.c-4989-\t */\narch/x86/kvm/vmx/nested.c:4990:\tvmx_set_efer(vcpu, nested_vmx_get_vmcs01_guest_efer(vmx));\narch/x86/kvm/vmx/nested.c-4991-\n--\narch/x86/kvm/vmx/vmx.c=3206=static void enter_rmode(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/vmx/vmx.c-3253-\narch/x86/kvm/vmx/vmx.c:3254:int vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer)\narch/x86/kvm/vmx/vmx.c-3255-{\n--\narch/x86/kvm/vmx/vmx.c=3279=static void enter_lmode(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/vmx/vmx.c-3292-\t}\narch/x86/kvm/vmx/vmx.c:3293:\tvmx_set_efer(vcpu, vcpu-\u003earch.efer | EFER_LMA);\narch/x86/kvm/vmx/vmx.c-3294-}\n--\narch/x86/kvm/vmx/vmx.c=3296=static void exit_lmode(struct kvm_vcpu *vcpu)\narch/x86/kvm/vmx/vmx.c-3297-{\narch/x86/kvm/vmx/vmx.c:3298:\tvmx_set_efer(vcpu, vcpu-\u003earch.efer \u0026 ~EFER_LMA);\narch/x86/kvm/vmx/vmx.c-3299-}\n--\narch/x86/kvm/vmx/vmx.h=357=void vmx_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask);\narch/x86/kvm/vmx/vmx.h:358:int vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer);\narch/x86/kvm/vmx/vmx.h-359-void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);\n--\narch/x86/kvm/vmx/x86_ops.h=71=bool vmx_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4);\narch/x86/kvm/vmx/x86_ops.h:72:int vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer);\narch/x86/kvm/vmx/x86_ops.h-73-void vmx_get_idt(struct kvm_vcpu *vcpu, struct desc_ptr *dt);\n--\narch/x86/kvm/x86.c=9536=void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)\n--\narch/x86/kvm/x86.c-9642-\tkvm_x86_call(set_cr4)(vcpu, 0);\narch/x86/kvm/x86.c:9643:\tkvm_x86_call(set_efer)(vcpu, 0);\narch/x86/kvm/x86.c-9644-\tkvm_x86_call(update_exception_bitmap)(vcpu);\n"
} |
| 15/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25589 output: 32 thoughts: 44 Thoughts: Okay, let's dive into this. I need to get a handle on the KVM configuration. My immediate thought is, "Right, Kconfig, where else would it be?" Specifically, I want to trace the dependencies for `arch/x86/kvm`. So, my first step is to figure out which Kconfig options actually enable the `arch/x86/kvm` code. I'll need to check the Kconfig files themselves to see the relationships. Time to crack open `arch/x86/kvm/Kconfig` and trace back the relevant dependencies to figure out exactly what drives the inclusion of that module. |
| 16/2 | 2026/10/01 22:00 | tool | grepper |
0mArgs:{
"Expression": "config KVM",
"PathPrefix": "arch/x86/kvm/"
}Results: {
"Output": "arch/x86/kvm/Kconfig=18=if VIRTUALIZATION\narch/x86/kvm/Kconfig-19-\narch/x86/kvm/Kconfig:20:config KVM_X86\narch/x86/kvm/Kconfig-21-\tdef_tristate KVM if (KVM_INTEL != n || KVM_AMD != n)\n--\narch/x86/kvm/Kconfig-51-\narch/x86/kvm/Kconfig:52:config KVM\narch/x86/kvm/Kconfig-53-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n--\narch/x86/kvm/Kconfig-68-\narch/x86/kvm/Kconfig:69:config KVM_WERROR\narch/x86/kvm/Kconfig-70-\tbool \"Compile KVM with -Werror\"\n--\narch/x86/kvm/Kconfig-82-\narch/x86/kvm/Kconfig:83:config KVM_SW_PROTECTED_VM\narch/x86/kvm/Kconfig-84-\tbool \"Enable support for KVM software-protected VMs\"\n--\narch/x86/kvm/Kconfig-95-\narch/x86/kvm/Kconfig:96:config KVM_INTEL\narch/x86/kvm/Kconfig-97-\ttristate \"KVM for Intel (and compatible) processors support\"\n--\narch/x86/kvm/Kconfig-106-\narch/x86/kvm/Kconfig:107:config KVM_INTEL_PROVE_VE\narch/x86/kvm/Kconfig-108- bool \"Check that guests do not receive #VE exceptions\"\n--\narch/x86/kvm/Kconfig=122=config X86_SGX_KVM\n--\narch/x86/kvm/Kconfig-133-\narch/x86/kvm/Kconfig:134:config KVM_INTEL_TDX\narch/x86/kvm/Kconfig-135-\tbool \"Intel Trust Domain Extensions (TDX) support\"\n--\narch/x86/kvm/Kconfig-145-\narch/x86/kvm/Kconfig:146:config KVM_AMD\narch/x86/kvm/Kconfig-147-\ttristate \"KVM for AMD processors support\"\n--\narch/x86/kvm/Kconfig-155-\narch/x86/kvm/Kconfig:156:config KVM_AMD_SEV\narch/x86/kvm/Kconfig-157-\tbool \"AMD Secure Encrypted Virtualization (SEV) support\"\n--\narch/x86/kvm/Kconfig-172-\narch/x86/kvm/Kconfig:173:config KVM_IOAPIC\narch/x86/kvm/Kconfig-174-\tbool \"I/O APIC, PIC, and PIT emulation\"\n--\narch/x86/kvm/Kconfig-182-\narch/x86/kvm/Kconfig:183:config KVM_SMM\narch/x86/kvm/Kconfig-184-\tbool \"System Management Mode emulation\"\n--\narch/x86/kvm/Kconfig-193-\narch/x86/kvm/Kconfig:194:config KVM_HYPERV\narch/x86/kvm/Kconfig-195-\tbool \"Support for Microsoft Hyper-V emulation\"\n--\narch/x86/kvm/Kconfig-207-\narch/x86/kvm/Kconfig:208:config KVM_XEN\narch/x86/kvm/Kconfig-209-\tbool \"Support for Xen hypercall interface\"\n--\narch/x86/kvm/Kconfig-216-\narch/x86/kvm/Kconfig:217:config KVM_PROVE_MMU\narch/x86/kvm/Kconfig-218-\tbool \"Prove KVM MMU correctness\"\n--\narch/x86/kvm/Kconfig-229-\narch/x86/kvm/Kconfig:230:config KVM_EXTERNAL_WRITE_TRACKING\narch/x86/kvm/Kconfig-231-\tbool\narch/x86/kvm/Kconfig-232-\narch/x86/kvm/Kconfig:233:config KVM_MAX_NR_VCPUS\narch/x86/kvm/Kconfig-234-\tint \"Maximum number of vCPUs per KVM guest\"\n"
} |
| 17/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27018 output: 36 thoughts: 18 Thoughts: Here's a summary of my immediate thought process: **Diving into the KVM x86 Makefile** Alright, time to get my hands dirty. I need to understand how the KVM modules are being built for the x86 architecture. The first logical step is to examine the `Makefile` located in `arch/x86/kvm/`. This file is the central point for orchestrating the build process within that specific context. I'll be looking for crucial things like: the dependencies, compilation flags being used, how object files are linked, and importantly, which parts of the KVM infrastructure are being compiled into the kernel directly versus being loaded as modules. I'll pay close attention to any conditional logic based on kernel configuration options or specific hardware features, as these are critical for understanding the build’s flexibility and compatibility. I expect to see includes for header files, definitions of object files, build rules with `cc` or `ld`, and possibly some clever use of make variables or functions to handle different scenarios. Understanding this `Makefile` is key to grasping the build flow for the KVM x86 component, and from there I can start to piece together how all the different parts of the virtualization system are assembled. |
| 18/2 | 2026/10/01 22:00 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/Makefile",
"FirstLine": 1,
"LineCount": 40
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t\n 3:\tccflags-y += -I $(srctree)/arch/x86/kvm\n 4:\tccflags-$(CONFIG_KVM_WERROR) += -Werror\n 5:\t\n 6:\tinclude $(srctree)/virt/kvm/Makefile.kvm\n 7:\t\n 8:\tkvm-y\t\t\t+= x86.o emulate.o irq.o lapic.o cpuid.o msrs.o pmu.o regs.o \\\n 9:\t\t\t\t mtrr.o debugfs.o mmu/mmu.o mmu/page_track.o mmu/spte.o\n 10:\t\n 11:\tkvm-$(CONFIG_X86_64) += mmu/tdp_iter.o mmu/tdp_mmu.o\n 12:\tkvm-$(CONFIG_KVM_IOAPIC) += i8259.o i8254.o ioapic.o\n 13:\tkvm-$(CONFIG_KVM_HYPERV) += hyperv.o\n 14:\tkvm-$(CONFIG_KVM_XEN)\t+= xen.o\n 15:\tkvm-$(CONFIG_KVM_SMM)\t+= smm.o\n 16:\t\n 17:\tkvm-intel-y\t\t+= vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o \\\n 18:\t\t\t\t vmx/nested.o vmx/posted_intr.o vmx/main.o\n 19:\t\n 20:\tkvm-intel-$(CONFIG_X86_SGX_KVM)\t+= vmx/sgx.o\n 21:\tkvm-intel-$(CONFIG_KVM_HYPERV)\t+= vmx/hyperv.o vmx/hyperv_evmcs.o\n 22:\tkvm-intel-$(CONFIG_KVM_INTEL_TDX)\t+= vmx/tdx.o\n 23:\t\n 24:\tkvm-amd-y\t\t+= svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o\n 25:\t\n 26:\tkvm-amd-$(CONFIG_KVM_AMD_SEV)\t+= svm/sev.o\n 27:\tkvm-amd-$(CONFIG_KVM_HYPERV)\t+= svm/hyperv.o\n 28:\t\n 29:\tifdef CONFIG_HYPERV\n 30:\tkvm-y\t\t\t+= kvm_onhyperv.o\n 31:\tkvm-intel-y\t\t+= vmx/vmx_onhyperv.o vmx/hyperv_evmcs.o\n 32:\tkvm-amd-y\t\t+= svm/svm_onhyperv.o\n 33:\tendif\n 34:\t\n 35:\tobj-$(CONFIG_KVM_X86)\t+= kvm.o\n 36:\tobj-$(CONFIG_KVM_INTEL)\t+= kvm-intel.o\n 37:\tobj-$(CONFIG_KVM_AMD)\t+= kvm-amd.o\n 38:\t\n 39:\tAFLAGS_svm/vmenter.o := -iquote $(obj)\n 40:\t$(obj)/svm/vmenter.o: $(obj)/kvm-asm-offsets.h\n"
} |
| 19/2 | 2026/10/01 22:00 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 28214 output: 242 thoughts: 738 Thoughts: **KVM Fuzzing Target Identification** Okay, so I've been digging into this KVM code, and here's what I've got. The core KVM functionality is built on a few key configuration options: `CONFIG_KVM`, `CONFIG_KVM_X86` (or more specific ones like `CONFIG_KVM_AMD` and `CONFIG_KVM_INTEL`). These are what ultimately control the inclusion of modules like `kvm.o`, which itself pulls in things like `cpuid.o`, `msrs.o`, and `x86.o`. And then we've got the AMD-specific and Intel-specific modules, controlled by `CONFIG_KVM_AMD` and `CONFIG_KVM_INTEL` respectively. So, clearly, `KVM`, `KVM_AMD`, and `KVM_INTEL` are the settings I'm interested in. Now, the main goal is to identify functions that are *specific* to the KVM context and are also likely to be triggered during runtime, which would make them good fuzzing targets. I've identified a list of functions that seem promising after going through the code. These include: 1. `kvm_valid_efer`: This is an exported internal function called during `sregs` validation and nested entry. 2. `__kvm_valid_efer`: I've seen that this one has been modified to check `guest_cpu_cap_has(vcpu, X86_FEATURE_EFER_LMSLE_MBZ)`. 3. `cpuid_func_emulated`: This one is also modified directly to emulate `0x80000008` `EFER_LMSLE_MBZ`. 4. `efer_trap`: Modified within `arch/x86/kvm/svm/svm.c` to clear `EFER_LMSLE`. 5. `svm_set_cpu_caps` and `vmx_set_cpu_caps`: These are modified within `arch/x86/kvm/svm/svm.c` and `arch/x86/kvm/vmx/vmx.c` respectively, and they are marked `__init`. However, since they run on module init I am not including them in the final list. 6. `kvm_setup_efer_caps`: Modified in `arch/x86/kvm/x86.c`. 7. `kvm_vcpu_after_set_cpuid`: This function, which calls `cpuid_func_emulated`, is called when the guest CPUID is set. I had to pause and think for a moment about whether the `__init` functions are worth considering, but then I remembered they're only executed during kernel/module initialization, not runtime. That means these are *not* fuzzing candidates. The others, though? Those seem perfect. They're all directly tied to critical runtime operations – setting up vCPUs, setting CPUID, setting SREGS, setting MSRs, or trapping EFER in AMD nested/SEV-ES guests. So, I re-checked to make sure that none of those functions were ubiquitous system calls or general tasks. They're all KVM-specific and deal with vCPU and CPUID/MSR manipulations. They look isolated to the right area, which makes them prime targets. All the requirements line up. I'm going to set `WorthFuzzing` to `true`. My reasoning is concise and accurate, and the `FocusSymbols` are: `[ "kvm_valid_efer", "__kvm_valid_efer", "cpuid_func_emulated", "kvm_vcpu_after_set_cpuid", "efer_trap" ]`. The `EnableConfigs` are: `[ "KVM", "KVM_AMD", "KVM_INTEL" ]`. Let's get the tool call made. |
| 20/2 | 2026/10/01 22:00 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"KVM",
"KVM_AMD",
"KVM_INTEL"
],
"FocusSymbols": [
"kvm_valid_efer",
"__kvm_valid_efer",
"cpuid_func_emulated",
"kvm_vcpu_after_set_cpuid",
"efer_trap"
],
"Reasoning": "The patch modifies KVM's CPUID emulation and EFER validation logic to support the EFER_LMSLE_MBZ defeature bit on x86 (including SVM and VMX). It updates capability initialization, CPUID emulation for function 0x80000008, EFER validation in __kvm_valid_efer/kvm_valid_efer, and SVM EFER trap handling. These paths are directly reachable and fuzzed via KVM ioctls such as KVM_SET_CPUID2, KVM_SET_SREGS, KVM_SET_MSRS, and KVM_RUN.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"KVM",
"KVM_AMD",
"KVM_INTEL"
],
"FocusSymbols": [
"kvm_valid_efer",
"__kvm_valid_efer",
"cpuid_func_emulated",
"kvm_vcpu_after_set_cpuid",
"efer_trap"
],
"Reasoning": "The patch modifies KVM's CPUID emulation and EFER validation logic to support the EFER_LMSLE_MBZ defeature bit on x86 (including SVM and VMX). It updates capability initialization, CPUID emulation for function 0x80000008, EFER validation in __kvm_valid_efer/kvm_valid_efer, and SVM EFER trap handling. These paths are directly reachable and fuzzed via KVM ioctls such as KVM_SET_CPUID2, KVM_SET_SREGS, KVM_SET_MSRS, and KVM_RUN.",
"WorthFuzzing": true
} |
| 21/1 | 2026/10/01 22:00 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 22/2 | 2026/10/01 22:00 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch adds support for handling the AMD EFER_LMSLE_MBZ CPUID defeature bit (CPUID 0x80000008:EBX[20]), which dictates whether EFER.LMSLE is allowed to be set in KVM guests.\n\nSpecifically, the patch:\n1. Adds `PASSTHROUGH_F(EFER_LMSLE_MBZ)` in `kvm_initialize_cpu_caps()` and updates vendor CPU capability initialization in `svm.c` and `vmx.c`.\n2. Adds handling for leaf 0x80000008 in `cpuid_func_emulated()`, operating on `struct kvm_cpuid_entry2`, which is explicitly zeroed with `memset(entry, 0, sizeof(*entry))` upon entering the function.\n3. Updates `__kvm_valid_efer()` to reject EFER.LMSLE if the guest has `X86_FEATURE_EFER_LMSLE_MBZ`.\n4. Masks `EFER_LMSLE` in `efer_trap()` for SEV-ES guests.\n5. Updates KVM selftests.\n\nAll affected data are scalar integers, bitmasks, and explicitly initialized/zeroed structs (`memset` in `cpuid_func_emulated`, `kvzalloc_objs` in `kvm_dev_ioctl_get_cpuid`). The changes do not introduce or modify uninitialized memory reads, struct padding leaks to userspace, or uninitialized buffer accesses. Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"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: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 757af473de3b2aa295eac49c88b809e1f8256f0e
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 1 21:59:59 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index e0430cc750c9e..2852bf94828be 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -9635,6 +9635,31 @@ On older versions of Linux, CPU[EAX=1]:ECX[24] (TSC_DEADLINE) is not reported by
is present and the kernel has enabled in-kernel emulation of the local APIC.
On newer versions, ``KVM_GET_SUPPORTED_CPUID`` does report the bit as available.
+Long mode segment limits
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+CPU[EAX=0x80000008]:EBX[20] (EFER_LMSLE_MBZ) is a "defeature" bit: it is set
+when the CPU does *not* support long mode segment limits, and so requires
+EFER.LMSLE to be zero. KVM reports the bit via ``KVM_GET_SUPPORTED_CPUID`` if
+and only if KVM refuses to set EFER.LMSLE, i.e. if the CPU doesn't support long
+mode segment limits, or if nested SVM is unsupported. KVM therefore reports
+the bit on all Intel hosts, as KVM allows EFER.LMSLE only when nested SVM is
+enabled.
+
+KVM never reports the bit via ``KVM_GET_EMULATED_CPUID``, but userspace may set
+it via ``KVM_SET_CPUID2`` even on a host where KVM doesn't report it. KVM
+honors the guest's enumeration and rejects EFER.LMSLE=1 accordingly. That lets
+userspace defeature a vCPU on a host that *does* support long mode segment
+limits, so that the vCPU can later be migrated to a host that doesn't, e.g. so
+that a vCPU created on AMD Rome can be migrated to Milan and later, which
+dropped support for long mode segment limits. The opposite direction needs no
+emulation, as a host that lacks long mode segment limits already enumerates the
+defeature.
+
+Note, ``KVM_SET_MSRS`` is exempt from the check, as host-initiated MSR writes
+skip guest CPUID checks so that userspace can set MSRs before it sets guest
+CPUID. ``KVM_SET_SREGS`` and nested VMRUN are not exempt.
+
CPU topology
~~~~~~~~~~~~
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 851f151efb35b..53d205eda3359 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -1171,6 +1171,10 @@ void kvm_initialize_cpu_caps(void)
F(AMD_STIBP),
F(AMD_STIBP_ALWAYS_ON),
F(AMD_IBRS_SAME_MODE),
+ /*
+ * Vendor code also sets EFER_LMSLE_MBZ if KVM itself
+ * can't support EFER.LMSLE, e.g. if nested SVM is disabled.
+ */
PASSTHROUGH_F(EFER_LMSLE_MBZ),
F(AMD_PSFD),
F(AMD_IBPB_RET),
@@ -1415,6 +1419,22 @@ static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 ind
if (kvm_cpu_cap_has(X86_FEATURE_RDTSCP))
entry->ecx = feature_bit(RDPID);
return 1;
+ case 0x80000008:
+ /*
+ * Honor the guest's EFER_LMSLE_MBZ even if the underlying CPU
+ * allows setting EFER.LMSLE, e.g. to allow migrating a vCPU
+ * between hosts with and without EFER.LMSLE support. To avoid
+ * breaking existing setups that reflect KVM's supported CPUID
+ * into the guest, KVM doesn't advertise EFER_LMSLE_MBZ unless
+ * KVM *can't* support EFER.LMSLE=1.
+ */
+ if (include_partially_emulated &&
+ !kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ)) {
+ entry->ebx |= feature_bit(EFER_LMSLE_MBZ);
+ return 1;
+ }
+ /* Nothing in 0x80000008 is fully emulated, don't emit an entry. */
+ return 0;
default:
return 0;
}
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index dd3bb04878ca6..b519fb90776e2 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -598,9 +598,19 @@ static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
if (efer & EFER_NX && !guest_cpu_cap_has(vcpu, X86_FEATURE_NX))
return false;
- return true;
+ /*
+ * EFER_LMSLE_MBZ is a "defeature" bit, i.e. is set when the CPU does
+ * *not* support long mode segment limits, and so is the only EFER
+ * check whose polarity is inverted: EFER.LMSLE is legal if and only if
+ * the guest does *not* have the defeature.
+ */
+ if (efer & EFER_LMSLE &&
+ guest_cpu_cap_has(vcpu, X86_FEATURE_EFER_LMSLE_MBZ))
+ return false;
+ return true;
}
+
bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)
{
if (efer & ~kvm_caps.supported_efer_bits)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7d59d301e1e54..90a80aad672f6 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -2759,10 +2759,19 @@ static int efer_trap(struct kvm_vcpu *vcpu)
* bit in svm_set_efer(), but __kvm_valid_efer() checks it against
* whether the guest has X86_FEATURE_SVM - this avoids a failure if
* the guest doesn't have X86_FEATURE_SVM.
+ *
+ * Clear EFER_LMSLE for a related reason: EFER writes are *trapped*,
+ * not intercepted, i.e. hardware has already committed the write by
+ * the time KVM gains control, and the trap is enabled if and only if
+ * the guest is SEV-ES, whose EFER lives in the encrypted VMSA and so
+ * can't be fixed up by KVM. Rejecting EFER.LMSLE=1 would inject a #GP
+ * *and* leave EFER.LMSLE set in the guest, which is strictly worse
+ * than honoring a write that hardware itself allowed.
*/
msr_info.host_initiated = false;
msr_info.index = MSR_EFER;
- msr_info.data = to_svm(vcpu)->vmcb->control.exit_info_1 & ~EFER_SVME;
+ msr_info.data = to_svm(vcpu)->vmcb->control.exit_info_1 &
+ ~(EFER_SVME | EFER_LMSLE);
ret = kvm_set_msr_common(vcpu, &msr_info);
return kvm_complete_insn_gp(vcpu, ret);
@@ -5571,6 +5580,16 @@ static __init void svm_set_cpu_caps(void)
boot_cpu_has(X86_FEATURE_AMD_SSBD))
kvm_cpu_cap_set(X86_FEATURE_VIRT_SSBD);
+ /*
+ * Tell userspace that EFER.LMSLE must be zero if nested SVM is
+ * disabled, as KVM allows EFER.LMSLE if and only if nested SVM is
+ * supported (a historical artifact of commit eec4b140c924 ("KVM: SVM:
+ * Allow EFER.LMSLE to be set with nested svm"), not an architectural
+ * requirement).
+ */
+ if (!nested)
+ kvm_cpu_cap_set(X86_FEATURE_EFER_LMSLE_MBZ);
+
if (enable_pmu) {
/*
* Enumerate support for PERFCTR_CORE if and only if KVM has
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 612ab07d4100f..f144c1e1c63f2 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8137,6 +8137,13 @@ static __init void vmx_set_cpu_caps(void)
kvm_cpu_cap_clear(X86_FEATURE_IBT);
}
+ /*
+ * CPUID 0x80000008. Tell userspace that EFER.LMSLE must be zero; KVM
+ * never allows EFER.LMSLE to be set on Intel CPUs, as KVM supports long
+ * mode segment limits only in conjunction with nested SVM.
+ */
+ kvm_cpu_cap_set(X86_FEATURE_EFER_LMSLE_MBZ);
+
kvm_setup_xss_caps();
kvm_finalize_cpu_caps();
}
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 79468ddfe4736..830cb9320d892 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -6930,7 +6930,14 @@ static void kvm_setup_efer_caps(void)
if (kvm_cpu_cap_has(X86_FEATURE_SVM)) {
kvm_caps.supported_efer_bits |= EFER_SVME;
- if (!boot_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ))
+
+ /*
+ * Enumerating EFER_LMSLE_MBZ and allowing EFER.LMSLE=1
+ * would be nonsensical. Note, vendor code sets the defeature
+ * if KVM can't support EFER.LMSLE for any reason, i.e. this
+ * needs to consult KVM's capabilities, not just raw CPUID.
+ */
+ if (!kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ))
kvm_caps.supported_efer_bits |= EFER_LMSLE;
}
}
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 96bab7002d39e..2554464d8b96f 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -117,7 +117,7 @@ TEST_GEN_PROGS_x86 += x86/state_test
TEST_GEN_PROGS_x86 += x86/vmx_preemption_timer_test
TEST_GEN_PROGS_x86 += x86/svm_vmcall_test
TEST_GEN_PROGS_x86 += x86/svm_int_ctl_test
-TEST_GEN_PROGS_x86 += x86/svm_nested_clear_efer_svme
+TEST_GEN_PROGS_x86 += x86/svm_nested_efer_test
TEST_GEN_PROGS_x86 += x86/svm_nested_shutdown_test
TEST_GEN_PROGS_x86 += x86/svm_nested_soft_inject_test
TEST_GEN_PROGS_x86 += x86/svm_nested_vmcb12_gpa
diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h
index 6e6f70035508a..6f110987c51a7 100644
--- a/tools/testing/selftests/kvm/include/x86/processor.h
+++ b/tools/testing/selftests/kvm/include/x86/processor.h
@@ -216,6 +216,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_INVTSC KVM_X86_CPU_FEATURE(0x80000007, 0, EDX, 8)
#define X86_FEATURE_RDPRU KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 4)
#define X86_FEATURE_AMD_IBPB KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 12)
+#define X86_FEATURE_EFER_LMSLE_MBZ KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 20)
#define X86_FEATURE_NPT KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 0)
#define X86_FEATURE_LBRV KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 1)
#define X86_FEATURE_NRIPS KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 3)
@@ -1472,6 +1473,19 @@ static inline bool kvm_is_lbrv_enabled(void)
return !!get_kvm_amd_param_integer("lbrv");
}
+/*
+ * Do NOT use this to check for nVMX or nSVM support. Querying kvm_cpu_has()
+ * for either of X86_FEATURE_{VMX,SVM} is the idiomatic way to check for nested
+ * virtualization support. Use this *only* to validate KVM's own enumeration.
+ */
+static inline bool kvm_is_nested_virtualization_enabled(void)
+{
+ if (host_cpu_is_intel)
+ return get_kvm_intel_param_bool("nested");
+
+ return get_kvm_amd_param_integer("nested");
+}
+
u64 *vm_get_pte(struct kvm_vm *vm, gva_t gva);
u64 kvm_hypercall(u64 nr, u64 a0, u64 a1, u64 a2, u64 a3);
diff --git a/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c b/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c
deleted file mode 100644
index 6bc301207cbcb..0000000000000
--- a/tools/testing/selftests/kvm/x86/svm_nested_clear_efer_svme.c
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2026, Google LLC.
- */
-#include "kvm_util.h"
-#include "vmx.h"
-#include "svm_util.h"
-#include "kselftest.h"
-
-
-static void l2_guest_code(void)
-{
- unsigned long efer = rdmsr(MSR_EFER);
-
- /* generic_svm_setup() initializes EFER_SVME set for L2 */
- GUEST_ASSERT(efer & EFER_SVME);
- wrmsr(MSR_EFER, efer & ~EFER_SVME);
-
- /* Unreachable, L1 should be shutdown */
- GUEST_ASSERT(0);
-}
-
-static void l1_guest_code(struct svm_test_data *svm)
-{
- generic_svm_setup(svm, l2_guest_code);
- run_guest(svm->vmcb, svm->vmcb_gpa);
-
- /* Unreachable, L1 should be shutdown */
- GUEST_ASSERT(0);
-}
-
-int main(int argc, char *argv[])
-{
- struct kvm_vcpu *vcpu;
- struct kvm_vm *vm;
- gva_t nested_gva = 0;
-
- TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM));
-
- vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
-
- vcpu_alloc_svm(vm, &nested_gva);
- vcpu_args_set(vcpu, 1, nested_gva);
-
- vcpu_run(vcpu);
- TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
-
- kvm_vm_free(vm);
- return 0;
-}
diff --git a/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c b/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c
new file mode 100644
index 0000000000000..fa0d76a9c390b
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/svm_nested_efer_test.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Tests for KVM's handling of EFER bits whose behavior is tied to nested SVM.
+ *
+ * Copyright (C) 2026, Google LLC.
+ */
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "svm_util.h"
+#include "kselftest.h"
+
+static bool l2_ran;
+
+static void l2_clear_efer_svme(void)
+{
+ u64 efer = rdmsr(MSR_EFER);
+
+ /* generic_svm_setup() initializes EFER_SVME set for L2 */
+ GUEST_ASSERT(efer & EFER_SVME);
+ wrmsr(MSR_EFER, efer & ~EFER_SVME);
+
+ /* Unreachable, L1 should be shutdown */
+ GUEST_ASSERT(0);
+}
+
+static void l1_clear_efer_svme(struct svm_test_data *svm)
+{
+ generic_svm_setup(svm, l2_clear_efer_svme);
+ run_guest(svm->vmcb, svm->vmcb_gpa);
+
+ /* Unreachable, L1 should be shutdown */
+ GUEST_ASSERT(0);
+}
+
+static void l2_lmsle(void)
+{
+ GUEST_ASSERT(rdmsr(MSR_EFER) & EFER_LMSLE);
+ l2_ran = true;
+ vmmcall();
+}
+
+static void l1_lmsle(struct svm_test_data *svm)
+{
+ bool lmsle_mbz = this_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ);
+ struct vmcb *vmcb = svm->vmcb;
+ u64 efer = rdmsr(MSR_EFER);
+
+ /*
+ * Selftests' vCPUs are created with EFER.LMSLE clear; the sub-tests
+ * below need to start from a clean slate.
+ */
+ GUEST_ASSERT(!(efer & EFER_LMSLE));
+ GUEST_ASSERT(!l2_ran);
+
+ /*
+ * Per the APM, if EFER_LMSLE_MBZ is enumerated in CPUID, "64-bit mode
+ * segment limit checking is not supported and attempting to set
+ * EFER.LMSLE = 1 causes a #GP exception".
+ */
+ if (lmsle_mbz) {
+ GUEST_ASSERT_EQ(wrmsr_safe(MSR_EFER, efer | EFER_LMSLE), GP_VECTOR);
+ GUEST_ASSERT(!(rdmsr(MSR_EFER) & EFER_LMSLE));
+ } else {
+ GUEST_ASSERT_EQ(wrmsr_safe(MSR_EFER, efer | EFER_LMSLE), 0);
+ GUEST_ASSERT(rdmsr(MSR_EFER) & EFER_LMSLE);
+
+ /*
+ * Restore EFER so that generic_svm_setup() doesn't propagate
+ * EFER.LMSLE into vmcb12 on its own, i.e. so that the VMRUN
+ * sub-test actually tests what it thinks it's testing.
+ */
+ wrmsr(MSR_EFER, efer);
+ }
+
+ /*
+ * VMRUN's consistency checks reject "any MBZ bit of EFER", i.e. a
+ * vmcb12 with EFER.LMSLE set must generate VMEXIT_INVALID when the
+ * defeature is enumerated.
+ */
+ generic_svm_setup(svm, l2_lmsle);
+ vmcb->save.efer |= EFER_LMSLE;
+ run_guest(vmcb, svm->vmcb_gpa);
+
+ if (lmsle_mbz) {
+ GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_ERR);
+ GUEST_ASSERT(!l2_ran);
+ } else {
+ GUEST_ASSERT_EQ(vmcb->control.exit_code, SVM_EXIT_VMMCALL);
+ GUEST_ASSERT(l2_ran);
+ GUEST_ASSERT(vmcb->save.efer & EFER_LMSLE);
+ }
+
+ GUEST_DONE();
+}
+
+static struct kvm_vcpu *create_l1_vcpu(struct kvm_vm **vm, void *l1_guest_code)
+{
+ struct kvm_vcpu *vcpu;
+ gva_t svm_gva;
+
+ *vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
+
+ vcpu_alloc_svm(*vm, &svm_gva);
+ vcpu_args_set(vcpu, 1, svm_gva);
+
+ return vcpu;
+}
+
+static void test_enumeration(void)
+{
+ bool lmsle_mbz;
+
+ /*
+ * EFER_LMSLE_MBZ, CPUID.80000008H:EBX[bit 20], is a "defeature" bit,
+ * i.e. is set when the CPU does *not* support long mode segment
+ * limits. KVM enumerates the defeature if and only if KVM refuses to
+ * set EFER.LMSLE, i.e. if the CPU doesn't support LMSLE, or if KVM
+ * doesn't support nested SVM. Derive the expectation from raw CPUID
+ * and kvm_amd's "nested" module param rather than from
+ * kvm_cpu_has(X86_FEATURE_SVM), so that the assertion doesn't simply
+ * compare KVM's enumeration to itself.
+ */
+ lmsle_mbz = this_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ) ||
+ !this_cpu_has(X86_FEATURE_SVM) ||
+ !kvm_is_nested_virtualization_enabled();
+
+ TEST_ASSERT_EQ(kvm_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ), lmsle_mbz);
+
+ ksft_test_result_pass("KVM enumerates EFER_LMSLE_MBZ=%d\n", lmsle_mbz);
+}
+
+static void test_clear_efer_svme(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ vcpu = create_l1_vcpu(&vm, l1_clear_efer_svme);
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_SHUTDOWN);
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("L2 clearing EFER.SVME shuts down L1\n");
+}
+
+static void test_lmsle(bool lmsle_mbz)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+
+ vcpu = create_l1_vcpu(&vm, l1_lmsle);
+
+ vcpu_set_or_clear_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ,
+ lmsle_mbz);
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ case UCALL_DONE:
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
+ }
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("Guest EFER_LMSLE_MBZ=%d\n", lmsle_mbz);
+}
+
+static void test_host_initiated_lmsle(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ u64 efer;
+
+ vm = vm_create_with_one_vcpu(&vcpu, NULL);
+ vcpu_set_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ);
+
+ /*
+ * EFER_LMSLE_MBZ is a guest CPUID consistency check, not a host
+ * capability, i.e. must not be enforced against host-initiated writes,
+ * so that userspace can set MSRs before it sets guest CPUID.
+ */
+ efer = vcpu_get_msr(vcpu, MSR_EFER);
+ TEST_ASSERT(!(efer & EFER_LMSLE), "EFER.LMSLE unexpectedly set");
+
+ vcpu_set_msr(vcpu, MSR_EFER, efer | EFER_LMSLE);
+ TEST_ASSERT_EQ(vcpu_get_msr(vcpu, MSR_EFER), efer | EFER_LMSLE);
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("Host-initiated EFER.LMSLE=1 is allowed\n");
+}
+
+static void test_sregs_lmsle(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_sregs sregs;
+ struct kvm_vm *vm;
+ int rc;
+
+ vm = vm_create_with_one_vcpu(&vcpu, NULL);
+ vcpu_set_cpuid_feature(vcpu, X86_FEATURE_EFER_LMSLE_MBZ);
+
+ /*
+ * Unlike KVM_SET_MSRS, KVM_SET_SREGS runs the full set of guest CPUID
+ * checks, i.e. rejects EFER.LMSLE even though it's host-initiated.
+ */
+ vcpu_sregs_get(vcpu, &sregs);
+ TEST_ASSERT(!(sregs.efer & EFER_LMSLE), "EFER.LMSLE unexpectedly set");
+
+ sregs.efer |= EFER_LMSLE;
+ rc = _vcpu_sregs_set(vcpu, &sregs);
+ TEST_ASSERT(rc, "KVM allowed EFER.LMSLE with EFER_LMSLE_MBZ set");
+
+ kvm_vm_free(vm);
+ ksft_test_result_pass("KVM_SET_SREGS rejects EFER.LMSLE=1\n");
+}
+
+int main(int argc, char *argv[])
+{
+ bool has_nested_svm, has_lmsle;
+
+ ksft_print_header();
+ ksft_set_plan(6);
+
+ test_enumeration();
+
+ /*
+ * The sub-tests below need to actually run a nested guest, and the
+ * EFER.LMSLE sub-tests additionally need KVM to allow EFER.LMSLE.
+ * It's KVM's view of the world, not raw CPUID, that dictates whether
+ * EFER.LMSLE is allowed, i.e. whether the defeature is emulated.
+ */
+ has_nested_svm = kvm_cpu_has(X86_FEATURE_SVM);
+ has_lmsle = has_nested_svm &&
+ !kvm_cpu_has(X86_FEATURE_EFER_LMSLE_MBZ);
+
+ if (!has_nested_svm)
+ ksft_print_msg("Nested SVM unsupported\n");
+ else if (!has_lmsle)
+ ksft_print_msg("KVM doesn't support EFER.LMSLE\n");
+
+ if (has_nested_svm) {
+ test_clear_efer_svme();
+ test_lmsle(true);
+ } else {
+ ksft_test_result_skip("L2 clearing EFER.SVME shuts down L1\n");
+ ksft_test_result_skip("Guest EFER_LMSLE_MBZ=1\n");
+ }
+
+ if (has_lmsle) {
+ test_lmsle(false);
+ test_host_initiated_lmsle();
+ test_sregs_lmsle();
+ } else {
+ ksft_test_result_skip("Guest EFER_LMSLE_MBZ=0\n");
+ ksft_test_result_skip("Host-initiated EFER.LMSLE=1 is allowed\n");
+ ksft_test_result_skip("KVM_SET_SREGS rejects EFER.LMSLE=1\n");
+ }
+
+ ksft_finished();
+}
|
| 23/3 | 2026/10/01 22:00 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 10307 output: 35 thoughts: 831 Thoughts: **KMSAN Fuzzing Session Justification: Analyzing the Patch** Okay, I need to decide if this patch warrants a dedicated KMSAN fuzzing session. Let's break this down. First, there's documentation being added for long mode segment limits (EFER_LMSLE_MBZ) in `Documentation/virt/kvm/api.rst`. That's a good sign for potential interaction with sensitive areas. Then, looking at `arch/x86/kvm/cpuid.c`, `kvm_initialize_cpu_caps()` now includes `PASSTHROUGH_F(EFER_LMSLE_MBZ)`. The more interesting part, though, is in `cpuid_func_emulated()`. It appears to be conditionally setting a bit (EFER_LMSLE_MBZ) in the `entry->ebx` register based on `include_partially_emulated` and if the feature is not already present. Hmm, before I make a call on this, I need to understand how the `entry` is initialized before this function is called. I need to dive into this file, especially the initialization logic for `entry`. Moving on to `arch/x86/kvm/msrs.c`, `__kvm_valid_efer()` now checks if `EFER_LMSLE` is set in the guest EFER and if the guest CPU has the `X86_FEATURE_EFER_LMSLE_MBZ` feature. If both are true, it returns false. This is interesting. Looks like we are trying to prevent the guest from setting this bit, I need to check how the guest tries to set it. Next, `arch/x86/kvm/svm/svm.c` in `efer_trap()` seems to be masking out `EFER_SVME` and `EFER_LMSLE` from something called `exit_info_1`. Then, `svm_set_cpu_caps()` unconditionally sets `X86_FEATURE_EFER_LMSLE_MBZ` if not nested. This looks like we're controlling whether this feature is visible to the guest. I need to understand what `exit_info_1` represents here to see if these masks can lead to an issue. Then, `arch/x86/kvm/vmx/vmx.c` also sets `X86_FEATURE_EFER_LMSLE_MBZ` unconditionally in `vmx_set_cpu_caps()`. This further confirms that KVM is influencing this bit. Finally, in `arch/x86/kvm/x86.c`, `kvm_setup_efer_caps()` seems to be conditionally setting `EFER_LMSLE` based on the presence of `X86_FEATURE_SVM` and whether `X86_FEATURE_EFER_LMSLE_MBZ` is already set. This seems related to the SVM code. And, of course, there are some tests added in the selftests directory. Overall, this patch seems to be about managing the `EFER_LMSLE_MBZ` feature, including exposing it to the guest under certain conditions. The conditional logic and interaction with CPUID, MSRs, and EFER registers suggest that subtle bugs or inconsistencies are possible. The fact that the code is dealing with feature bits related to long mode and how they are exposed to the guest is a red flag, but I need to fully grok `cpuid_func_emulated()` and see if the SVM/VMX interactions can result in any memory corruption. Let's analyze `cpuid_func_emulated()` in detail. I think a dedicated KMSAN fuzzing session is likely justified. |
| 24/3 | 2026/10/01 22:01 | tool | grepper |
0mArgs:{
"Expression": "cpuid_func_emulated",
"PathPrefix": "arch/x86/kvm"
}Results: {
"Output": "arch/x86/kvm/cpuid.c=356=static u32 cpuid_get_reg_unsafe(struct kvm_cpuid_entry2 *entry, u32 reg)\n--\narch/x86/kvm/cpuid.c-372-\narch/x86/kvm/cpuid.c:373:static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 index,\narch/x86/kvm/cpuid.c-374-\t\t\t bool include_partially_emulated);\n--\narch/x86/kvm/cpuid.c=376=void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/cpuid.c-402-\narch/x86/kvm/cpuid.c:403:\t\tcpuid_func_emulated(\u0026emulated, cpuid.function, cpuid.index, true);\narch/x86/kvm/cpuid.c-404-\n--\narch/x86/kvm/cpuid.c=1343=static struct kvm_cpuid_entry2 *do_host_cpuid(struct kvm_cpuid_array *array,\n--\narch/x86/kvm/cpuid.c-1385-\narch/x86/kvm/cpuid.c:1386:static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 index,\narch/x86/kvm/cpuid.c-1387-\t\t\t bool include_partially_emulated)\n--\narch/x86/kvm/cpuid.c-1442-\narch/x86/kvm/cpuid.c:1443:static int __do_cpuid_func_emulated(struct kvm_cpuid_array *array, u32 func)\narch/x86/kvm/cpuid.c-1444-{\n--\narch/x86/kvm/cpuid.c-1447-\narch/x86/kvm/cpuid.c:1448:\tarray-\u003enent += cpuid_func_emulated(\u0026array-\u003eentries[array-\u003enent], func, 0, false);\narch/x86/kvm/cpuid.c-1449-\treturn 0;\n--\narch/x86/kvm/cpuid.c=1949=static int do_cpuid_func(struct kvm_cpuid_array *array, u32 func,\n--\narch/x86/kvm/cpuid.c-1952-\tif (type == KVM_GET_EMULATED_CPUID)\narch/x86/kvm/cpuid.c:1953:\t\treturn __do_cpuid_func_emulated(array, func);\narch/x86/kvm/cpuid.c-1954-\n"
} |
| 25/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 12767 output: 83 thoughts: 42 Thoughts: Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts: **Analyzing KVM CPUID Handling in x86** Alright, I need to get a better handle on the KVM CPUID handling within the x86 architecture. Time to focus on the `arch/x86/kvm/cpuid.c` file. I'm going to zero in on specific sections – lines 380 to 420 and then lines 1380 to 1460. That first section should offer a glimpse into... likely a specific CPUID leaf or perhaps a particular aspect of how KVM filters or modifies CPUID information presented to the guest. I need to understand what's being handled there, perhaps it's related to a security feature, or a specific virtualization extension. The second section seems significantly larger; it's got to be providing some core logic or handling a crucial component of the CPUID functionality, I'm expecting some kind of dispatching, perhaps based on CPUID function input, or maybe conditional handling of feature flags. I'll need to figure out what they are trying to achieve with those lines. Let's see what the code reveals. The context will be crucial; I'll be keeping a sharp eye on any calls to other KVM functions or any interaction with the hardware. Hopefully, these two areas will clarify how KVM interacts with the CPUID instruction. |
| 26/3 | 2026/10/01 22:01 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/cpuid.c",
"FirstLine": 380,
"LineCount": 40
}Results: {
"Contents": " 380:\t\tstruct kvm_cpuid_entry2 *entry;\n 381:\t\tbool allow_gbpages;\n 382:\t\tint i;\n 383:\t\n 384:\t\tmemset(vcpu-\u003earch.cpu_caps, 0, sizeof(vcpu-\u003earch.cpu_caps));\n 385:\t\tBUILD_BUG_ON(ARRAY_SIZE(reverse_cpuid) != NR_KVM_CPU_CAPS);\n 386:\t\n 387:\t\t/*\n 388:\t\t * Reset guest capabilities to userspace's guest CPUID definition, i.e.\n 389:\t\t * honor userspace's definition for features that don't require KVM or\n 390:\t\t * hardware management/support (or that KVM simply doesn't care about).\n 391:\t\t */\n 392:\t\tfor (i = 0; i \u003c NR_KVM_CPU_CAPS; i++) {\n 393:\t\t\tconst struct cpuid_reg cpuid = reverse_cpuid[i];\n 394:\t\t\tstruct kvm_cpuid_entry2 emulated;\n 395:\t\n 396:\t\t\tif (!cpuid.function)\n 397:\t\t\t\tcontinue;\n 398:\t\n 399:\t\t\tentry = kvm_find_cpuid_entry_index(vcpu, cpuid.function, cpuid.index);\n 400:\t\t\tif (!entry)\n 401:\t\t\t\tcontinue;\n 402:\t\n 403:\t\t\tcpuid_func_emulated(\u0026emulated, cpuid.function, cpuid.index, true);\n 404:\t\n 405:\t\t\t/*\n 406:\t\t\t * A vCPU has a feature if it's supported by KVM and is enabled\n 407:\t\t\t * in guest CPUID. Note, this includes features that are\n 408:\t\t\t * supported by KVM but aren't advertised to userspace!\n 409:\t\t\t */\n 410:\t\t\tvcpu-\u003earch.cpu_caps[i] = kvm_cpu_caps[i] |\n 411:\t\t\t\t\t\t cpuid_get_reg_unsafe(\u0026emulated, cpuid.reg);\n 412:\t\t\tvcpu-\u003earch.cpu_caps[i] \u0026= cpuid_get_reg_unsafe(entry, cpuid.reg);\n 413:\t\t}\n 414:\t\n 415:\t\tkvm_update_cpuid_runtime(vcpu);\n 416:\t\n 417:\t\t/*\n 418:\t\t * If TDP is enabled, let the guest use GBPAGES if they're supported in\n 419:\t\t * hardware. The hardware page walker doesn't let KVM disable GBPAGES,\n"
} |
| 27/3 | 2026/10/01 22:01 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/cpuid.c",
"FirstLine": 1385,
"LineCount": 60
}Results: {
"Contents": "1385:\t\n1386:\tstatic int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 index,\n1387:\t\t\t\t bool include_partially_emulated)\n1388:\t{\n1389:\t\tmemset(entry, 0, sizeof(*entry));\n1390:\t\n1391:\t\t/* KVM doesn't currently emulate any non-zero indices. */\n1392:\t\tif (cpuid_function_is_indexed(func) \u0026\u0026 index)\n1393:\t\t\treturn 0;\n1394:\t\n1395:\t\tentry-\u003efunction = func;\n1396:\t\tentry-\u003eindex = 0;\n1397:\t\tentry-\u003eflags = 0;\n1398:\t\n1399:\t\tswitch (func) {\n1400:\t\tcase 0:\n1401:\t\t\tentry-\u003eeax = 7;\n1402:\t\t\treturn 1;\n1403:\t\tcase 1:\n1404:\t\t\tentry-\u003eecx = feature_bit(MOVBE);\n1405:\t\t\t/*\n1406:\t\t\t * KVM allows userspace to enumerate MONITOR+MWAIT support to\n1407:\t\t\t * the guest, but the MWAIT feature flag is never advertised\n1408:\t\t\t * to userspace because MONITOR+MWAIT aren't virtualized by\n1409:\t\t\t * hardware, can't be faithfully emulated in software (KVM\n1410:\t\t\t * emulates them as NOPs), and allowing the guest to execute\n1411:\t\t\t * them natively requires enabling a per-VM capability.\n1412:\t\t\t */\n1413:\t\t\tif (include_partially_emulated)\n1414:\t\t\t\tentry-\u003eecx |= feature_bit(MWAIT);\n1415:\t\t\treturn 1;\n1416:\t\tcase 7:\n1417:\t\t\tentry-\u003eflags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;\n1418:\t\t\tentry-\u003eeax = 0;\n1419:\t\t\tif (kvm_cpu_cap_has(X86_FEATURE_RDTSCP))\n1420:\t\t\t\tentry-\u003eecx = feature_bit(RDPID);\n1421:\t\t\treturn 1;\n1422:\t\tcase 0x80000008:\n1423:\t\t\t/*\n1424:\t\t\t * Honor the guest's EFER_LMSLE_MBZ even if the underlying CPU\n1425:\t\t\t * allows setting EFER.LMSLE, e.g. to allow migrating a vCPU\n1426:\t\t\t * between hosts with and without EFER.LMSLE support. To avoid\n1427:\t\t\t * breaking existing setups that reflect KVM's supported CPUID\n1428:\t\t\t * into the guest, KVM doesn't advertise EFER_LMSLE_MBZ unless\n1429:\t\t\t * KVM *can't* support EFER.LMSLE=1.\n1430:\t\t\t */\n1431:\t\t\tif (include_partially_emulated \u0026\u0026\n1432:\t\t\t !kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ)) {\n1433:\t\t\t\tentry-\u003eebx |= feature_bit(EFER_LMSLE_MBZ);\n1434:\t\t\t\treturn 1;\n1435:\t\t\t}\n1436:\t\t\t/* Nothing in 0x80000008 is fully emulated, don't emit an entry. */\n1437:\t\t\treturn 0;\n1438:\t\tdefault:\n1439:\t\t\treturn 0;\n1440:\t\t}\n1441:\t}\n1442:\t\n1443:\tstatic int __do_cpuid_func_emulated(struct kvm_cpuid_array *array, u32 func)\n1444:\t{\n"
} |
| 28/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14916 output: 36 thoughts: 338 Thoughts: **Analyzing CPUID Emulation in KVM**
Okay, let's break this down. First, I see `cpuid_func_emulated`. Line 1389 immediately grabs my attention: `memset(entry, 0, sizeof(*entry));`. So, `entry` is always explicitly zeroed at the start of `cpuid_func_emulated`. This is important.
Now, looking at `kvm_vcpu_after_set_cpuid`, there's a local `struct kvm_cpuid_entry2 emulated;` being used. Then, `cpuid_func_emulated` is called with this `emulated` struct. Right away, inside that function, `memset` is called to zero out `entry`. This confirms that *regardless* of the subsequent logic, even if the function might return early (e.g. `return 0`), the `emulated` structure passed in is *completely* zeroed out. This is a very predictable state.
Next, I need to check how this function is used elsewhere, specifically `__do_cpuid_func_emulated`. Specifically I want to see how `array->nent` is treated. The code shows:
```c
static int __do_cpuid_func_emulated(struct kvm_cpuid_array *array, u32 func)
{
array->nent += cpuid_func_emulated(&array->entries[array->nent], func, 0, false);
return 0;
}
```
If `include_partially_emulated` is false (as in `__do_cpuid_func_emulated` for `KVM_GET_EMULATED_CPUID`), and a case like 0x80000008 is encountered, it returns 0 and does *not* increment `array->nent`. This means a zeroed entry might be added or might not. I need to figure out which cases result in `array->nent` actually being incremented.
I also need to check `__kvm_valid_efer` and its callers next, since that's another area that might be relevant to this area of the code, so that is my next area of focus.
|
| 29/3 | 2026/10/01 22:01 | tool | grepper |
0mArgs:{
"Expression": "__kvm_valid_efer",
"PathPrefix": "arch/x86/kvm"
}Results: {
"Output": "arch/x86/kvm/msrs.c=577=static int do_get_feature_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data)\n--\narch/x86/kvm/msrs.c-582-\narch/x86/kvm/msrs.c:583:static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\narch/x86/kvm/msrs.c-584-{\n--\narch/x86/kvm/msrs.c=614=bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\n--\narch/x86/kvm/msrs.c-618-\narch/x86/kvm/msrs.c:619:\treturn __kvm_valid_efer(vcpu, efer);\narch/x86/kvm/msrs.c-620-}\n--\narch/x86/kvm/msrs.c=623=static int set_efer(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-632-\tif (!msr_info-\u003ehost_initiated) {\narch/x86/kvm/msrs.c:633:\t\tif (!__kvm_valid_efer(vcpu, efer))\narch/x86/kvm/msrs.c-634-\t\t\treturn 1;\n--\narch/x86/kvm/svm/svm.c=2752=static int efer_trap(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/svm.c-2758-\t * Clear the EFER_SVME bit from EFER. The SVM code always sets this\narch/x86/kvm/svm/svm.c:2759:\t * bit in svm_set_efer(), but __kvm_valid_efer() checks it against\narch/x86/kvm/svm/svm.c-2760-\t * whether the guest has X86_FEATURE_SVM - this avoids a failure if\n"
} |
| 30/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16360 output: 35 thoughts: 27 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person: **Investigating `guest_cpu_cap_has`** Alright, let's dive into this `guest_cpu_cap_has` function or flag or whatever it is. My initial question is always implementation. I need to understand *how* this is actually implemented. What's the code? What's the logic behind it? Is it a simple boolean, a complex bitmask, a lookup table, a dynamic check? Knowing the underlying implementation is *crucial* to understanding its behavior, potential edge cases, and, of course, any potential bugs or performance bottlenecks. I need to see the source. Let's start digging and find the source. |
| 31/3 | 2026/10/01 22:01 | tool | grepper |
0mArgs:{
"Expression": "guest_cpu_cap_has",
"PathPrefix": "arch/x86/kvm"
}Results: {
"Output": "\nFull output is too long, showing 200 out of 805 lines.\nUse more precise expression if possible.\n\narch/x86/kvm/cpuid.c=376=void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/cpuid.c-428-\tallow_gbpages = tdp_enabled ? boot_cpu_has(X86_FEATURE_GBPAGES) :\narch/x86/kvm/cpuid.c:429:\t\t\t\t guest_cpu_cap_has(vcpu, X86_FEATURE_GBPAGES);\narch/x86/kvm/cpuid.c-430-\tguest_cpu_cap_change(vcpu, X86_FEATURE_GBPAGES, allow_gbpages);\n--\narch/x86/kvm/cpuid.c-454-\tvcpu-\u003earch.cr4_guest_rsvd_bits = __cr4_reserved_bits(__kvm_cpu_cap_has, UNUSED_) |\narch/x86/kvm/cpuid.c:455:\t\t\t\t\t __cr4_reserved_bits(guest_cpu_cap_has, vcpu);\narch/x86/kvm/cpuid.c-456-#undef __kvm_cpu_cap_has\n--\narch/x86/kvm/cpuid.h=258=static __always_inline void guest_cpu_cap_change(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/cpuid.h-267-\narch/x86/kvm/cpuid.h:268:static __always_inline bool guest_cpu_cap_has(struct kvm_vcpu *vcpu,\narch/x86/kvm/cpuid.h-269-\t\t\t\t\t unsigned int x86_feature)\n--\narch/x86/kvm/cpuid.h=284=static inline bool kvm_vcpu_is_legal_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)\narch/x86/kvm/cpuid.h-285-{\narch/x86/kvm/cpuid.h:286:\tif (guest_cpu_cap_has(vcpu, X86_FEATURE_LAM))\narch/x86/kvm/cpuid.h-287-\t\tcr3 \u0026= ~(X86_CR3_LAM_U48 | X86_CR3_LAM_U57);\n--\narch/x86/kvm/cpuid.h=292=static inline bool guest_has_spec_ctrl_msr(struct kvm_vcpu *vcpu)\narch/x86/kvm/cpuid.h-293-{\narch/x86/kvm/cpuid.h:294:\treturn (guest_cpu_cap_has(vcpu, X86_FEATURE_SPEC_CTRL) ||\narch/x86/kvm/cpuid.h:295:\t\tguest_cpu_cap_has(vcpu, X86_FEATURE_AMD_STIBP) ||\narch/x86/kvm/cpuid.h:296:\t\tguest_cpu_cap_has(vcpu, X86_FEATURE_AMD_IBRS) ||\narch/x86/kvm/cpuid.h:297:\t\tguest_cpu_cap_has(vcpu, X86_FEATURE_AMD_SSBD));\narch/x86/kvm/cpuid.h-298-}\n--\narch/x86/kvm/cpuid.h=300=static inline bool guest_has_pred_cmd_msr(struct kvm_vcpu *vcpu)\narch/x86/kvm/cpuid.h-301-{\narch/x86/kvm/cpuid.h:302:\treturn (guest_cpu_cap_has(vcpu, X86_FEATURE_SPEC_CTRL) ||\narch/x86/kvm/cpuid.h:303:\t\tguest_cpu_cap_has(vcpu, X86_FEATURE_AMD_IBPB) ||\narch/x86/kvm/cpuid.h:304:\t\tguest_cpu_cap_has(vcpu, X86_FEATURE_SBPB));\narch/x86/kvm/cpuid.h-305-}\n--\narch/x86/kvm/hyperv.c=1358=static void __kvm_hv_xsaves_xsavec_maybe_warn(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/hyperv.c-1377-\tif (guest_cpuid_has(vcpu, X86_FEATURE_XSAVES) ||\narch/x86/kvm/hyperv.c:1378:\t !guest_cpu_cap_has(vcpu, X86_FEATURE_XSAVEC))\narch/x86/kvm/hyperv.c-1379-\t\treturn;\n--\narch/x86/kvm/lapic.c=614=void kvm_apic_set_version(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/lapic.c-624-\narch/x86/kvm/lapic.c:625:\tif (guest_cpu_cap_has(vcpu, X86_FEATURE_X2APIC) \u0026\u0026\narch/x86/kvm/lapic.c-626-\t kvm_lapic_advertise_suppress_eoi_broadcast(vcpu-\u003ekvm))\n--\narch/x86/kvm/lapic.c=2849=int kvm_apic_set_base(struct kvm_vcpu *vcpu, u64 value, bool host_initiated)\n--\narch/x86/kvm/lapic.c-2857-\tu64 reserved_bits = kvm_vcpu_reserved_gpa_bits_raw(vcpu) | 0x2ff |\narch/x86/kvm/lapic.c:2858:\t\t(guest_cpu_cap_has(vcpu, X86_FEATURE_X2APIC) ? 0 : X2APIC_ENABLE);\narch/x86/kvm/lapic.c-2859-\n--\narch/x86/kvm/mmu.h=229=static inline unsigned long kvm_get_active_cr3_lam_bits(struct kvm_vcpu *vcpu)\narch/x86/kvm/mmu.h-230-{\narch/x86/kvm/mmu.h:231:\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_LAM))\narch/x86/kvm/mmu.h-232-\t\treturn 0;\n--\narch/x86/kvm/mmu/mmu.c=5513=static void reset_guest_rsvds_bits_mask(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/mmu/mmu.c-5518-\t\t\t\tw-\u003ecpu_role.base.level, is_efer_nx(w),\narch/x86/kvm/mmu/mmu.c:5519:\t\t\t\tguest_cpu_cap_has(vcpu, X86_FEATURE_GBPAGES),\narch/x86/kvm/mmu/mmu.c-5520-\t\t\t\tis_cr4_pse(w),\n--\narch/x86/kvm/mmu/mmu.c=5580=static void reset_shadow_zero_bits_mask(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/mmu/mmu.c-5595-\t\t\t\tcontext-\u003eroot_role.efer_nx,\narch/x86/kvm/mmu/mmu.c:5596:\t\t\t\tguest_cpu_cap_has(vcpu, X86_FEATURE_GBPAGES),\narch/x86/kvm/mmu/mmu.c-5597-\t\t\t\tis_pse, is_amd);\n--\narch/x86/kvm/msrs.c=583=static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\narch/x86/kvm/msrs.c-584-{\narch/x86/kvm/msrs.c:585:\tif (efer \u0026 EFER_AUTOIBRS \u0026\u0026 !guest_cpu_cap_has(vcpu, X86_FEATURE_AUTOIBRS))\narch/x86/kvm/msrs.c-586-\t\treturn false;\narch/x86/kvm/msrs.c-587-\narch/x86/kvm/msrs.c:588:\tif (efer \u0026 EFER_FFXSR \u0026\u0026 !guest_cpu_cap_has(vcpu, X86_FEATURE_FXSR_OPT))\narch/x86/kvm/msrs.c-589-\t\treturn false;\narch/x86/kvm/msrs.c-590-\narch/x86/kvm/msrs.c:591:\tif (efer \u0026 EFER_SVME \u0026\u0026 !guest_cpu_cap_has(vcpu, X86_FEATURE_SVM))\narch/x86/kvm/msrs.c-592-\t\treturn false;\n--\narch/x86/kvm/msrs.c-594-\tif (efer \u0026 (EFER_LME | EFER_LMA) \u0026\u0026\narch/x86/kvm/msrs.c:595:\t !guest_cpu_cap_has(vcpu, X86_FEATURE_LM))\narch/x86/kvm/msrs.c-596-\t\treturn false;\narch/x86/kvm/msrs.c-597-\narch/x86/kvm/msrs.c:598:\tif (efer \u0026 EFER_NX \u0026\u0026 !guest_cpu_cap_has(vcpu, X86_FEATURE_NX))\narch/x86/kvm/msrs.c-599-\t\treturn false;\n--\narch/x86/kvm/msrs.c-607-\tif (efer \u0026 EFER_LMSLE \u0026\u0026\narch/x86/kvm/msrs.c:608:\t guest_cpu_cap_has(vcpu, X86_FEATURE_EFER_LMSLE_MBZ))\narch/x86/kvm/msrs.c-609-\t\treturn false;\n--\narch/x86/kvm/msrs.c=709=static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data,\n--\narch/x86/kvm/msrs.c-743-\t\tif (!host_initiated \u0026\u0026\narch/x86/kvm/msrs.c:744:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_RDTSCP) \u0026\u0026\narch/x86/kvm/msrs.c:745:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_RDPID))\narch/x86/kvm/msrs.c-746-\t\t\treturn 1;\n--\narch/x86/kvm/msrs.c-763-\tcase MSR_IA32_S_CET:\narch/x86/kvm/msrs.c:764:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) \u0026\u0026\narch/x86/kvm/msrs.c:765:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT))\narch/x86/kvm/msrs.c-766-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c-785-\tcase MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB:\narch/x86/kvm/msrs.c:786:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK))\narch/x86/kvm/msrs.c-787-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c-791-\t\t */\narch/x86/kvm/msrs.c:792:\t\tif (index == MSR_IA32_INT_SSP_TAB \u0026\u0026 !guest_cpu_cap_has(vcpu, X86_FEATURE_LM))\narch/x86/kvm/msrs.c-793-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c=828=static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data,\n--\narch/x86/kvm/msrs.c-839-\t\tif (!host_initiated \u0026\u0026\narch/x86/kvm/msrs.c:840:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_RDTSCP) \u0026\u0026\narch/x86/kvm/msrs.c:841:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_RDPID))\narch/x86/kvm/msrs.c-842-\t\t\treturn 1;\n--\narch/x86/kvm/msrs.c-845-\tcase MSR_IA32_S_CET:\narch/x86/kvm/msrs.c:846:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) \u0026\u0026\narch/x86/kvm/msrs.c:847:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT))\narch/x86/kvm/msrs.c-848-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c-854-\tcase MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB:\narch/x86/kvm/msrs.c:855:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK))\narch/x86/kvm/msrs.c-856-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c=1180=static bool is_xstate_managed_msr(struct kvm_vcpu *vcpu, u32 msr)\n--\narch/x86/kvm/msrs.c-1186-\tcase MSR_IA32_U_CET:\narch/x86/kvm/msrs.c:1187:\t\treturn guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) ||\narch/x86/kvm/msrs.c:1188:\t\t guest_cpu_cap_has(vcpu, X86_FEATURE_IBT);\narch/x86/kvm/msrs.c-1189-\tcase MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:\narch/x86/kvm/msrs.c:1190:\t\treturn guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK);\narch/x86/kvm/msrs.c-1191-\tdefault:\n--\narch/x86/kvm/msrs.c=1452=int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-1482-\t\tif (!msr_info-\u003ehost_initiated ||\narch/x86/kvm/msrs.c:1483:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_ARCH_CAPABILITIES))\narch/x86/kvm/msrs.c-1484-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c-1488-\t\tif (!msr_info-\u003ehost_initiated ||\narch/x86/kvm/msrs.c:1489:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))\narch/x86/kvm/msrs.c-1490-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c-1513-\narch/x86/kvm/msrs.c:1514:\t\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_SPEC_CTRL) \u0026\u0026\narch/x86/kvm/msrs.c:1515:\t\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_AMD_IBPB))\narch/x86/kvm/msrs.c-1516-\t\t\t\treserved_bits |= PRED_CMD_IBPB;\narch/x86/kvm/msrs.c-1517-\narch/x86/kvm/msrs.c:1518:\t\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_SBPB))\narch/x86/kvm/msrs.c-1519-\t\t\t\treserved_bits |= PRED_CMD_SBPB;\n--\narch/x86/kvm/msrs.c-1538-\t\tif (!msr_info-\u003ehost_initiated \u0026\u0026\narch/x86/kvm/msrs.c:1539:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D))\narch/x86/kvm/msrs.c-1540-\t\t\treturn 1;\n--\narch/x86/kvm/msrs.c-1562-\narch/x86/kvm/msrs.c:1563:\t\tif (guest_cpu_cap_has(vcpu, X86_FEATURE_GP_ON_USER_CPUID))\narch/x86/kvm/msrs.c-1564-\t\t\tvalid |= MSR_K7_HWCR_CPUID_USER_DIS;\n--\narch/x86/kvm/msrs.c-1595-\tcase MSR_IA32_TSC_ADJUST:\narch/x86/kvm/msrs.c:1596:\t\tif (guest_cpu_cap_has(vcpu, X86_FEATURE_TSC_ADJUST)) {\narch/x86/kvm/msrs.c-1597-\t\t\tif (!msr_info-\u003ehost_initiated) {\n--\narch/x86/kvm/msrs.c-1622-\t\t ((old_val ^ data) \u0026 MSR_IA32_MISC_ENABLE_MWAIT)) {\narch/x86/kvm/msrs.c:1623:\t\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_XMM3))\narch/x86/kvm/msrs.c-1624-\t\t\t\treturn 1;\n--\narch/x86/kvm/msrs.c-1801-\tcase MSR_AMD64_OSVW_ID_LENGTH:\narch/x86/kvm/msrs.c:1802:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_OSVW))\narch/x86/kvm/msrs.c-1803-\t\t\treturn 1;\n--\narch/x86/kvm/msrs.c-1806-\tcase MSR_AMD64_OSVW_STATUS:\narch/x86/kvm/msrs.c:1807:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_OSVW))\narch/x86/kvm/msrs.c-1808-\t\t\treturn 1;\n--\narch/x86/kvm/msrs.c-1825-\t\tif (!msr_info-\u003ehost_initiated \u0026\u0026\narch/x86/kvm/msrs.c:1826:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_XFD))\narch/x86/kvm/msrs.c-1827-\t\t\treturn 1;\n--\narch/x86/kvm/msrs.c-1835-\t\tif (!msr_info-\u003ehost_initiated \u0026\u0026\narch/x86/kvm/msrs.c:1836:\t\t !guest_cpu_cap_has(vcpu, X86_FEATURE_XFD))\narch/x86/kvm/msrs.c-1837-\t\t\treturn 1;\n--\narch/x86/kvm/msrs.c=1909=int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)\n--\narch/x86/kvm/msrs.c-1954-\tcase MSR_IA32_ARCH_CAPABILITIES:\narch/x86/kvm/msrs.c:1955:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_ARCH_CAPABILITIES))\narch/x86/kvm/msrs.c-1956-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c-1959-\tcase MSR_IA32_PERF_CAPABILITIES:\narch/x86/kvm/msrs.c:1960:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM))\narch/x86/kvm/msrs.c-1961-\t\t\treturn KVM_MSR_RET_UNSUPPORTED;\n--\narch/x86/kvm/msrs.c-2160-\tcase MSR_AMD64_OSVW_ID_LENGTH:\narch/x86/kvm/msrs.c:2161:\t\tif (!guest_cpu_cap_has(vcpu, X86_FEATURE_OSVW))\n\n"
} |
| 32/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22372 output: 40 thoughts: 111 Thoughts: **Investigating EFER_LMSLE_MBZ in KVM for x86 Architecture** Okay, let's dive into this `EFER_LMSLE_MBZ` thing. It looks like it's connected to KVM on the x86 architecture. I'm seeing it referenced in `arch/x86/kvm/cpuid.c`, specifically within `kvm_initialize_cpu_caps()`. The code uses `PASSTHROUGH_F(EFER_LMSLE_MBZ)`. I'm immediately wondering: exactly what CPUIID leaf is associated with `EFER_LMSLE_MBZ`? It's time to check the kernel headers to find the definition of `X86_FEATURE_EFER_LMSLE_MBZ`. That's where I'll get the details I need to understand what's being passed through and why. Let's see what the documentation tells me about this feature. |
| 33/3 | 2026/10/01 22:01 | tool | grepper |
0mArgs:{
"Expression": "X86_FEATURE_EFER_LMSLE_MBZ",
"PathPrefix": "arch/x86"
}Results: {
"Output": "arch/x86/include/asm/cpufeatures.h-352-#define X86_FEATURE_AMD_IBRS_SAME_MODE\t(13*32+19) /* Indirect Branch Restricted Speculation same mode protection*/\narch/x86/include/asm/cpufeatures.h:353:#define X86_FEATURE_EFER_LMSLE_MBZ\t(13*32+20) /* EFER.LMSLE must be zero */\narch/x86/include/asm/cpufeatures.h-354-#define X86_FEATURE_AMD_PPIN\t\t(13*32+23) /* \"amd_ppin\" Protected Processor Inventory Number */\n--\narch/x86/kvm/cpuid.c=1386=static int cpuid_func_emulated(struct kvm_cpuid_entry2 *entry, u32 func, u32 index,\n--\narch/x86/kvm/cpuid.c-1431-\t\tif (include_partially_emulated \u0026\u0026\narch/x86/kvm/cpuid.c:1432:\t\t !kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ)) {\narch/x86/kvm/cpuid.c-1433-\t\t\tentry-\u003eebx |= feature_bit(EFER_LMSLE_MBZ);\n--\narch/x86/kvm/msrs.c=583=static bool __kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer)\n--\narch/x86/kvm/msrs.c-607-\tif (efer \u0026 EFER_LMSLE \u0026\u0026\narch/x86/kvm/msrs.c:608:\t guest_cpu_cap_has(vcpu, X86_FEATURE_EFER_LMSLE_MBZ))\narch/x86/kvm/msrs.c-609-\t\treturn false;\n--\narch/x86/kvm/svm/svm.c=5522=static __init void svm_set_cpu_caps(void)\n--\narch/x86/kvm/svm/svm.c-5590-\tif (!nested)\narch/x86/kvm/svm/svm.c:5591:\t\tkvm_cpu_cap_set(X86_FEATURE_EFER_LMSLE_MBZ);\narch/x86/kvm/svm/svm.c-5592-\n--\narch/x86/kvm/vmx/vmx.c=8080=static __init void vmx_set_cpu_caps(void)\n--\narch/x86/kvm/vmx/vmx.c-8144-\t */\narch/x86/kvm/vmx/vmx.c:8145:\tkvm_cpu_cap_set(X86_FEATURE_EFER_LMSLE_MBZ);\narch/x86/kvm/vmx/vmx.c-8146-\n--\narch/x86/kvm/x86.c=6914=static void kvm_setup_efer_caps(void)\n--\narch/x86/kvm/x86.c-6939-\t\t */\narch/x86/kvm/x86.c:6940:\t\tif (!kvm_cpu_cap_has(X86_FEATURE_EFER_LMSLE_MBZ))\narch/x86/kvm/x86.c-6941-\t\t\tkvm_caps.supported_efer_bits |= EFER_LMSLE;\n"
} |
| 34/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23668 output: 42 thoughts: 49 Thoughts: Okay, let's dive into this. Here's how I'd approach that snippet, considering I'm trying to grok the context of that specific code change: **Analyzing the KVM CPUID Passthrough Logic** Alright, I'm thinking... I need to check `arch/x86/kvm/cpuid.c`, specifically around line 1171. My immediate goal is to understand the implications of adding `PASSTHROUGH_F(EFER_LMSLE_MBZ)`. I know that `cpuid.c` is the heart of KVM's CPUID emulation and passthrough functionality on x86, so anything touching it, especially dealing with specific CPUID leaves, can have profound implications for guest OS compatibility and performance. `PASSTHROUGH_F` probably signifies that a particular feature flag is being directly forwarded from the host CPU to the guest, meaning we're likely making a specific EFER (Extended Feature Enable Register) bit accessible. The inclusion of `EFER_LMSLE_MBZ` specifically tells me we are dealing with handling Long Mode Segment Limit Extensions, and its MBZ setting. MBZ - Must Be Zero is often a point of confusion for hypervisors if not handled correctly. So, I need to look at the surrounding code to determine *why* this change was made. Was it to support a new guest OS feature? Address a bug? Improve performance? I'll be looking for comments, commit messages, and related code that sheds light on the reasoning. I'll need to confirm what the relevant CPUID leaf is related to (that's crucial). I'll also be mentally verifying whether the passthrough is conditional on some other setting, as is often the case to ensure overall guest integrity. If this is a recent addition, it suggests a more modern x86 virtualization optimization, or perhaps a fix that was only discovered and introduced recently, and as such I'll want to review the related commits and potential regressions thoroughly. Essentially, I'm trying to get a complete picture of this particular passthrough, so I can understand any potential risks or benefits associated with it. |
| 35/3 | 2026/10/01 22:01 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/cpuid.c",
"FirstLine": 1160,
"LineCount": 30
}Results: {
"Contents": "1160:\t\t);\n1161:\t\n1162:\t\tkvm_cpu_cap_init(CPUID_8000_0008_EBX,\n1163:\t\t\tF(CLZERO),\n1164:\t\t\tF(XSAVEERPTR),\n1165:\t\t\tF(WBNOINVD),\n1166:\t\t\tF(AMD_IBPB),\n1167:\t\t\tF(AMD_IBRS),\n1168:\t\t\tF(AMD_SSBD),\n1169:\t\t\tF(VIRT_SSBD),\n1170:\t\t\tF(AMD_SSB_NO),\n1171:\t\t\tF(AMD_STIBP),\n1172:\t\t\tF(AMD_STIBP_ALWAYS_ON),\n1173:\t\t\tF(AMD_IBRS_SAME_MODE),\n1174:\t\t\t/*\n1175:\t\t\t * Vendor code also sets EFER_LMSLE_MBZ if KVM itself\n1176:\t\t\t * can't support EFER.LMSLE, e.g. if nested SVM is disabled.\n1177:\t\t\t */\n1178:\t\t\tPASSTHROUGH_F(EFER_LMSLE_MBZ),\n1179:\t\t\tF(AMD_PSFD),\n1180:\t\t\tF(AMD_IBPB_RET),\n1181:\t\t);\n1182:\t\n1183:\t\t/*\n1184:\t\t * AMD has separate bits for each SPEC_CTRL bit.\n1185:\t\t * arch/x86/kernel/cpu/bugs.c is kind enough to\n1186:\t\t * record that in cpufeatures so use them.\n1187:\t\t */\n1188:\t\tif (boot_cpu_has(X86_FEATURE_IBPB)) {\n1189:\t\t\tkvm_cpu_cap_set(X86_FEATURE_AMD_IBPB);\n"
} |
| 36/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24710 output: 38 thoughts: 105 Thoughts: **Investigating KVM Data Return Mechanisms** Alright, let's dive in. I need to refresh my memory on the different `ioctls` and underlying mechanisms within KVM that are used to return data back to userspace. My initial thought is to examine how CPUID data is exposed. Specifically, `KVM_GET_SUPPORTED_CPUID` and `KVM_GET_EMULATED_CPUID` are the immediate candidates, since they are explicitly designed to deliver CPUID entries to the user. I'll need to understand their implementation details, and to do that I will focus on understanding how `kvm_dev_ioctl_get_cpuid` functions. That function is clearly the core of data retrieval in those `ioctls`. This should give me a good understanding of KVM's general approach to this problem. |
| 37/3 | 2026/10/01 22:01 | tool | grepper |
0mArgs:{
"Expression": "KVM_GET_SUPPORTED_CPUID",
"PathPrefix": "arch/x86/kvm"
}Results: {
"Output": "arch/x86/kvm/cpuid.c=1985=static bool sanity_check_entries(struct kvm_cpuid_entry2 __user *entries,\n--\narch/x86/kvm/cpuid.c-1997-\t *\narch/x86/kvm/cpuid.c:1998:\t * Sadly, this wasn't enforced for KVM_GET_SUPPORTED_CPUID and so we\narch/x86/kvm/cpuid.c-1999-\t * have to give ourselves satisfied only with the emulated side. /me\n--\narch/x86/kvm/x86.c=2456=long kvm_arch_dev_ioctl(struct file *filp,\n--\narch/x86/kvm/x86.c-2465-\t\tbreak;\narch/x86/kvm/x86.c:2466:\tcase KVM_GET_SUPPORTED_CPUID:\narch/x86/kvm/x86.c-2467-\tcase KVM_GET_EMULATED_CPUID: {\n--\narch/x86/kvm/x86.h=679=void kvm_setup_xss_caps(void);\n--\narch/x86/kvm/x86.h-684- * This is intended to be used only when enumerating support to userspace,\narch/x86/kvm/x86.h:685: * e.g. in KVM_GET_SUPPORTED_CPUID and KVM_CAP_XSAVE2, it does NOT need to be\narch/x86/kvm/x86.h-686- * used to check/restrict guest behavior as KVM rejects KVM_SET_CPUID{2} if\n"
} |
| 38/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25492 output: 43 thoughts: 38 Thoughts: Here's my summary, as if I were thinking it: **Digging into KVM's x86 CPUID Handling** Okay, time to get my hands dirty. I want to understand exactly how the CPUID entries are being managed and copied within KVM for x86. My starting point is `arch/x86/kvm/x86.c`, and I'm zeroing in on roughly line 2465. That's the likely location where the allocation and copying logic for CPUID data is implemented. I want to see *exactly* how these entries are handled at the lowest level. I expect to find the meat of the implementation there. This should reveal the fundamental structures and processes at play. |
| 39/3 | 2026/10/01 22:01 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/x86.c",
"FirstLine": 2460,
"LineCount": 40
}Results: {
"Contents": "2460:\t\tlong r;\n2461:\t\n2462:\t\tswitch (ioctl) {\n2463:\t\tcase KVM_GET_MSR_INDEX_LIST:\n2464:\t\t\tr = kvm_get_msr_index_list(argp);\n2465:\t\t\tbreak;\n2466:\t\tcase KVM_GET_SUPPORTED_CPUID:\n2467:\t\tcase KVM_GET_EMULATED_CPUID: {\n2468:\t\t\tstruct kvm_cpuid2 __user *cpuid_arg = argp;\n2469:\t\t\tstruct kvm_cpuid2 cpuid;\n2470:\t\n2471:\t\t\tr = -EFAULT;\n2472:\t\t\tif (copy_from_user(\u0026cpuid, cpuid_arg, sizeof(cpuid)))\n2473:\t\t\t\tgoto out;\n2474:\t\n2475:\t\t\tr = kvm_dev_ioctl_get_cpuid(\u0026cpuid, cpuid_arg-\u003eentries,\n2476:\t\t\t\t\t\t ioctl);\n2477:\t\t\tif (r)\n2478:\t\t\t\tgoto out;\n2479:\t\n2480:\t\t\tr = -EFAULT;\n2481:\t\t\tif (copy_to_user(cpuid_arg, \u0026cpuid, sizeof(cpuid)))\n2482:\t\t\t\tgoto out;\n2483:\t\t\tr = 0;\n2484:\t\t\tbreak;\n2485:\t\t}\n2486:\t\tcase KVM_X86_GET_MCE_CAP_SUPPORTED:\n2487:\t\t\tr = -EFAULT;\n2488:\t\t\tif (copy_to_user(argp, \u0026kvm_caps.supported_mce_cap,\n2489:\t\t\t\t\t sizeof(kvm_caps.supported_mce_cap)))\n2490:\t\t\t\tgoto out;\n2491:\t\t\tr = 0;\n2492:\t\t\tbreak;\n2493:\t\tcase KVM_GET_MSR_FEATURE_INDEX_LIST:\n2494:\t\t\tr = kvm_get_feature_msr_index_list(argp);\n2495:\t\t\tbreak;\n2496:\t\tcase KVM_GET_MSRS:\n2497:\t\t\tr = kvm_get_feature_msrs(argp);\n2498:\t\t\tbreak;\n2499:\t#ifdef CONFIG_KVM_HYPERV\n"
} |
| 40/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26362 output: 39 thoughts: 33 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into KVM CPUID Handling** Okay, let's zero in on how KVM handles CPUID information for x86. I need to refresh my memory on the specific mechanism, so I'll start by checking out the `kvm_dev_ioctl_get_cpuid` function. That function is the likely entry point for retrieving CPUID data, and I'll find it in `arch/x86/kvm/cpuid.c`. I expect to find the core logic for translating guest CPUID requests into host CPUID lookups or emulations within that file. I'm hoping to understand the interaction between the guest's requested CPUID leaf and the host's actual CPU features, and how that is presented through KVM. I'm particularly interested in any virtualization layers or optimizations present to reduce the overhead of CPUID access. |
| 41/3 | 2026/10/01 22:01 | tool | grepper |
0mArgs:{
"Expression": "kvm_dev_ioctl_get_cpuid",
"PathPrefix": "arch/x86/kvm"
}Results: {
"Output": "arch/x86/kvm/cpuid.c=1985=static bool sanity_check_entries(struct kvm_cpuid_entry2 __user *entries,\n--\narch/x86/kvm/cpuid.c-2011-\narch/x86/kvm/cpuid.c:2012:int kvm_dev_ioctl_get_cpuid(struct kvm_cpuid2 *cpuid,\narch/x86/kvm/cpuid.c-2013-\t\t\t struct kvm_cpuid_entry2 __user *entries,\n--\narch/x86/kvm/cpuid.h=45=static inline struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,\n--\narch/x86/kvm/cpuid.h-51-\narch/x86/kvm/cpuid.h:52:int kvm_dev_ioctl_get_cpuid(struct kvm_cpuid2 *cpuid,\narch/x86/kvm/cpuid.h-53-\t\t\t struct kvm_cpuid_entry2 __user *entries,\n--\narch/x86/kvm/x86.c=2456=long kvm_arch_dev_ioctl(struct file *filp,\n--\narch/x86/kvm/x86.c-2474-\narch/x86/kvm/x86.c:2475:\t\tr = kvm_dev_ioctl_get_cpuid(\u0026cpuid, cpuid_arg-\u003eentries,\narch/x86/kvm/x86.c-2476-\t\t\t\t\t ioctl);\n"
} |
| 42/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27073 output: 42 thoughts: 33 Thoughts: Okay, let's dive into this. I'm going to pull up `kvm_dev_ioctl_get_cpuid` in `arch/x86/kvm/cpuid.c`. I know this is where the KVM code handles getting CPUID information from the guest. This is going to be interesting to see how they've implemented it, especially given the various security considerations and performance optimizations that are involved. I'm going to look for how they're handling things like feature masking, emulation of different CPU models, and probably some clever ways of dealing with the complexities of nested virtualization, if that's even in play here. Time to get my hands dirty. |
| 43/3 | 2026/10/01 22:01 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/cpuid.c",
"FirstLine": 2010,
"LineCount": 40
}Results: {
"Contents": "2010:\t}\n2011:\t\n2012:\tint kvm_dev_ioctl_get_cpuid(struct kvm_cpuid2 *cpuid,\n2013:\t\t\t\t struct kvm_cpuid_entry2 __user *entries,\n2014:\t\t\t\t unsigned int type)\n2015:\t{\n2016:\t\tstatic const u32 funcs[] = {\n2017:\t\t\t0, 0x80000000, CENTAUR_CPUID_SIGNATURE, KVM_CPUID_SIGNATURE,\n2018:\t\t};\n2019:\t\n2020:\t\tstruct kvm_cpuid_array array = {\n2021:\t\t\t.nent = 0,\n2022:\t\t};\n2023:\t\tint r, i;\n2024:\t\n2025:\t\tif (cpuid-\u003enent \u003c 1)\n2026:\t\t\treturn -E2BIG;\n2027:\t\tif (cpuid-\u003enent \u003e KVM_MAX_CPUID_ENTRIES)\n2028:\t\t\tcpuid-\u003enent = KVM_MAX_CPUID_ENTRIES;\n2029:\t\n2030:\t\tif (sanity_check_entries(entries, cpuid-\u003enent, type))\n2031:\t\t\treturn -EINVAL;\n2032:\t\n2033:\t\tarray.entries = kvzalloc_objs(struct kvm_cpuid_entry2, cpuid-\u003enent);\n2034:\t\tif (!array.entries)\n2035:\t\t\treturn -ENOMEM;\n2036:\t\n2037:\t\tarray.maxnent = cpuid-\u003enent;\n2038:\t\n2039:\t\tfor (i = 0; i \u003c ARRAY_SIZE(funcs); i++) {\n2040:\t\t\tr = get_cpuid_func(\u0026array, funcs[i], type);\n2041:\t\t\tif (r)\n2042:\t\t\t\tgoto out_free;\n2043:\t\t}\n2044:\t\tcpuid-\u003enent = array.nent;\n2045:\t\n2046:\t\tif (copy_to_user(entries, array.entries,\n2047:\t\t\t\t array.nent * sizeof(struct kvm_cpuid_entry2)))\n2048:\t\t\tr = -EFAULT;\n2049:\t\n"
} |
| 44/3 | 2026/10/01 22:01 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27908 output: 381 thoughts: 2235 Thoughts: **KMSAN Assessment: EFER_LMSLE_MBZ Support Patch** Okay, let's break this down. From what I can see, this patch primarily deals with adding support for the `EFER_LMSLE_MBZ` CPUID feature/defeature across KVM's various components, including CPUID emulation, EFER MSR validation, and SVM/VMX capability initialization. My primary concern is always: does this introduce or expose uninitialized memory that could lead to information leaks or other vulnerabilities? Looking closely at the code, `array.entries` is already initialized via `kvzalloc_objs`, which zeros the entire allocated memory. Inside `cpuid_func_emulated`, there's a clear `memset(entry, 0, sizeof(*entry))` call right at the start. This is important: every entry is guaranteed zeroed before any modification occurs. The switch statement within this function specifically handles the `0x80000008` case, and even there, the code only modifies `entry->ebx` – but the entry is always zeroed at the start. So, the initialization is handled. The calls to `cpuid_func_emulated` from `kvm_vcpu_after_set_cpuid` are also safe. The `emulated` struct is memset to zero before being passed. The relevant data is extracted through `cpuid_get_reg_unsafe`, but this only accesses the already-zeroed members. The same is true for the call in `__do_cpuid_func_emulated`. Even when `include_partially_emulated` is false, nothing is exposed that's not explicitly initialized. Moving on to `msrs.c`, `__kvm_valid_efer` works with a `u64 efer` passed by value, and calls `guest_cpu_cap_has`, which uses initialized capability bitmasks. There's no risk of reading uninitialized data. Similarly, `svm/svm.c` and `vmx/vmx.c` both use initialized bitmasks. `efer_trap` masks `exit_info_1` before passing it, ensuring that there is no data leak. And finally `kvm_setup_efer_caps` operates with already initialized values, which means no leaks. Given all of this, and considering the KMSAN rules, it's pretty clear that this patch *doesn't* introduce any uninitialized memory risks. All modifications are on scalar values, bitmasks, or explicitly zeroed structures. There's no risk of information leaks, no uninitialized buffer reads, and no uninitialized variables being used in conditional logic or sent to user space. Therefore, `NeedsKMSAN` is unequivocally `false`. The primary risks in this patch are around the correct handling of the MSR and CPUID bits - those are functional and virtualization specific, not uninitialized memory issues. |
| 45/3 | 2026/10/01 22:01 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch adds support for handling the AMD EFER_LMSLE_MBZ CPUID defeature bit (CPUID 0x80000008:EBX[20]), which dictates whether EFER.LMSLE is allowed to be set in KVM guests.\n\nSpecifically, the patch:\n1. Adds `PASSTHROUGH_F(EFER_LMSLE_MBZ)` in `kvm_initialize_cpu_caps()` and updates vendor CPU capability initialization in `svm.c` and `vmx.c`.\n2. Adds handling for leaf 0x80000008 in `cpuid_func_emulated()`, operating on `struct kvm_cpuid_entry2`, which is explicitly zeroed with `memset(entry, 0, sizeof(*entry))` upon entering the function.\n3. Updates `__kvm_valid_efer()` to reject EFER.LMSLE if the guest has `X86_FEATURE_EFER_LMSLE_MBZ`.\n4. Masks `EFER_LMSLE` in `efer_trap()` for SEV-ES guests.\n5. Updates KVM selftests.\n\nAll affected data are scalar integers, bitmasks, and explicitly initialized/zeroed structs (`memset` in `cpuid_func_emulated`, `kvzalloc_objs` in `kvm_dev_ioctl_get_cpuid`). The changes do not introduce or modify uninitialized memory reads, struct padding leaks to userspace, or uninitialized buffer accesses. Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch adds support for handling the AMD EFER_LMSLE_MBZ CPUID defeature bit (CPUID 0x80000008:EBX[20]), which dictates whether EFER.LMSLE is allowed to be set in KVM guests.\n\nSpecifically, the patch:\n1. Adds `PASSTHROUGH_F(EFER_LMSLE_MBZ)` in `kvm_initialize_cpu_caps()` and updates vendor CPU capability initialization in `svm.c` and `vmx.c`.\n2. Adds handling for leaf 0x80000008 in `cpuid_func_emulated()`, operating on `struct kvm_cpuid_entry2`, which is explicitly zeroed with `memset(entry, 0, sizeof(*entry))` upon entering the function.\n3. Updates `__kvm_valid_efer()` to reject EFER.LMSLE if the guest has `X86_FEATURE_EFER_LMSLE_MBZ`.\n4. Masks `EFER_LMSLE` in `efer_trap()` for SEV-ES guests.\n5. Updates KVM selftests.\n\nAll affected data are scalar integers, bitmasks, and explicitly initialized/zeroed structs (`memset` in `cpuid_func_emulated`, `kvzalloc_objs` in `kvm_dev_ioctl_get_cpuid`). The changes do not introduce or modify uninitialized memory reads, struct padding leaks to userspace, or uninitialized buffer accesses. Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|