| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/07 09:10 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"KVM",
"KVM_AMD",
"KVM_INTEL"
],
"FocusSymbols": [
"kvm_flush_pml_buffer",
"kvm_update_cpu_dirty_logging",
"svm_update_cpu_dirty_logging",
"svm_flush_pml_buffer",
"pml_full_interception",
"nested_svm_exit_special",
"nested_vmcb02_prepare_control",
"vmx_update_cpu_dirty_logging",
"vmx_flush_pml_buffer"
],
"KMSANReasoning": "The patch series introduces AMD SVM support for Page Modification Logging (PML) and consolidates shared PML handling logic between Intel VMX and AMD SVM.\n\nAll allocated PML buffer pages are zero-initialized at creation time via `__GFP_ZERO` (in `alloc_page` and `snp_safe_alloc_page_node`). The changes do not introduce any kernel-to-user memory copy operations (e.g. `copy_to_user`, ioctl outputs), do not leave any stack or heap structures uninitialized, and do not introduce control-flow branching based on uninitialized memory. Therefore, there are no uninitialized memory or info-leak risks requiring a dedicated KMSAN fuzzing session; standard KASAN and functional testing are sufficient.",
"NeedsKMSAN": false,
"Reasoning": "The patch implements Page Modification Logging (PML) dirty logging support for AMD SVM and refactors the shared PML buffer flushing and dirty logging state machine across x86 KVM, Intel VMX, and AMD SVM. These changes touch reachable core virtualization code paths in KVM and affect nested virtualization and exit handling.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/07 09:10 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit a8b5312779dd64dbc6311a236fd260a349381dd6\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Sep 7 09:10:51 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h\nindex f70ee74b5f921..cbfcc66a18e9b 100644\n--- a/arch/x86/include/asm/cpufeatures.h\n+++ b/arch/x86/include/asm/cpufeatures.h\n@@ -238,6 +238,7 @@\n // free: was #define X86_FEATURE_PVUNLOCK ( 8*32+20) /* PV unlock function */\n #define X86_FEATURE_VCPUPREEMPT\t\t( 8*32+21) /* PV vcpu_is_preempted function */\n #define X86_FEATURE_TDX_GUEST\t\t( 8*32+22) /* \"tdx_guest\" Intel Trust Domain Extensions Guest */\n+#define X86_FEATURE_PML\t\t\t( 8*32+23) /* AMD Page Modification logging */\n \n /* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */\n #define X86_FEATURE_FSGSBASE\t\t( 9*32+ 0) /* \"fsgsbase\" RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/\ndiff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h\nindex 3af7395c2430a..2debf62942909 100644\n--- a/arch/x86/include/asm/kvm_host.h\n+++ b/arch/x86/include/asm/kvm_host.h\n@@ -789,6 +789,8 @@ struct kvm_vcpu_arch {\n \t */\n \tstruct kvm_mmu_memory_cache mmu_external_spt_cache;\n \n+\tstruct page *pml_page;\n+\n \t/*\n \t * QEMU userspace and the guest each have their own FPU state.\n \t * In vcpu_run, we switch between the user and guest FPU contexts.\n@@ -1670,7 +1672,7 @@ struct kvm_x86_ops {\n \t\t\t struct x86_exception *exception);\n \tvoid (*handle_exit_irqoff)(struct kvm_vcpu *vcpu);\n \n-\tvoid (*update_cpu_dirty_logging)(struct kvm_vcpu *vcpu);\n+\tvoid (*update_cpu_dirty_logging)(struct kvm_vcpu *vcpu, bool enable);\n \n \tvoid (*vcpu_blocking)(struct kvm_vcpu *vcpu);\n \tvoid (*vcpu_unblocking)(struct kvm_vcpu *vcpu);\ndiff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h\nindex aa63431ba92c3..e0a7549a7c727 100644\n--- a/arch/x86/include/asm/svm.h\n+++ b/arch/x86/include/asm/svm.h\n@@ -165,7 +165,10 @@ struct __attribute__ ((__packed__)) vmcb_control_area {\n \tu8 reserved_9[22];\n \tu64 allowed_sev_features;\t/* Offset 0x138 */\n \tu64 guest_sev_features;\t\t/* Offset 0x140 */\n-\tu8 reserved_10[664];\n+\tu8 reserved_10[128];\n+\tu64 pml_addr;\t\t\t/* Offset 0x1c8 */\n+\tu16 pml_index;\t\t\t/* Offset 0x1d0 */\n+\tu8 reserved_11[526];\n \t/*\n \t * Offset 0x3e0, 32 bytes reserved\n \t * for use by hypervisor/software.\n@@ -240,10 +243,11 @@ struct __attribute__ ((__packed__)) vmcb_control_area {\n #define SVM_IOIO_SIZE_MASK (7 \u003c\u003c SVM_IOIO_SIZE_SHIFT)\n #define SVM_IOIO_ASIZE_MASK (7 \u003c\u003c SVM_IOIO_ASIZE_SHIFT)\n \n-#define SVM_MISC_ENABLE_NP\t\tBIT(0)\n-#define SVM_MISC_ENABLE_SEV\t\tBIT(1)\n-#define SVM_MISC_ENABLE_SEV_ES\tBIT(2)\n-#define SVM_MISC_ENABLE_GMET\tBIT(3)\n+#define SVM_MISC_ENABLE_NP\t\tBIT_ULL(0)\n+#define SVM_MISC_ENABLE_SEV\t\tBIT_ULL(1)\n+#define SVM_MISC_ENABLE_SEV_ES\t\tBIT_ULL(2)\n+#define SVM_MISC_ENABLE_GMET\t\tBIT_ULL(3)\n+#define SVM_MISC_ENABLE_PML\t\tBIT_ULL(11)\n \n #define SVM_MISC2_ENABLE_V_LBR\tBIT_ULL(0)\n #define SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE\tBIT_ULL(1)\ndiff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h\nindex 010a45c9f6147..e806761850921 100644\n--- a/arch/x86/include/uapi/asm/svm.h\n+++ b/arch/x86/include/uapi/asm/svm.h\n@@ -101,6 +101,7 @@\n #define SVM_EXIT_AVIC_INCOMPLETE_IPI\t\t0x401\n #define SVM_EXIT_AVIC_UNACCELERATED_ACCESS\t0x402\n #define SVM_EXIT_VMGEXIT 0x403\n+#define SVM_EXIT_PML_FULL\t0x407\n \n /* SEV-ES software-defined VMGEXIT events */\n #define SVM_VMGEXIT_MMIO_READ\t\t\t0x80000001ull\n@@ -236,6 +237,7 @@\n \t{ SVM_EXIT_AVIC_INCOMPLETE_IPI,\t\t\"avic_incomplete_ipi\" }, \\\n \t{ SVM_EXIT_AVIC_UNACCELERATED_ACCESS, \"avic_unaccelerated_access\" }, \\\n \t{ SVM_EXIT_VMGEXIT,\t\t\"vmgexit\" }, \\\n+\t{ SVM_EXIT_PML_FULL,\t\t\"pml_full\" }, \\\n \t{ SVM_VMGEXIT_MMIO_READ,\t\"vmgexit_mmio_read\" }, \\\n \t{ SVM_VMGEXIT_MMIO_WRITE,\t\"vmgexit_mmio_write\" }, \\\n \t{ SVM_VMGEXIT_NMI_COMPLETE,\t\"vmgexit_nmi_complete\" }, \\\ndiff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c\nindex 8665a6474806c..b18cc9c88dd57 100644\n--- a/arch/x86/kernel/cpu/scattered.c\n+++ b/arch/x86/kernel/cpu/scattered.c\n@@ -55,6 +55,7 @@ static const struct cpuid_bit cpuid_bits[] = {\n \t{ X86_FEATURE_AMD_FAST_CPPC,\t\tCPUID_EDX, 15, 0x80000007, 0 },\n \t{ X86_FEATURE_CPPC_PERF_PRIO,\t\tCPUID_EDX, 16, 0x80000007, 0 },\n \t{ X86_FEATURE_MBA,\t\t\tCPUID_EBX, 6, 0x80000008, 0 },\n+\t{ X86_FEATURE_PML,\t\t\tCPUID_ECX, 4, 0x8000000a, 0 },\n \t{ X86_FEATURE_X2AVIC_EXT,\t\tCPUID_ECX, 6, 0x8000000a, 0 },\n \t{ X86_FEATURE_COHERENCY_SFW_NO,\t\tCPUID_EBX, 31, 0x8000001f, 0 },\n \t{ X86_FEATURE_SMBA,\t\t\tCPUID_EBX, 2, 0x80000020, 0 },\ndiff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c\nindex 73f37b050d0a0..da417c4c59154 100644\n--- a/arch/x86/kvm/svm/nested.c\n+++ b/arch/x86/kvm/svm/nested.c\n@@ -901,6 +901,13 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)\n \tvmcb02-\u003econtrol.msrpm_base_pa = vmcb01-\u003econtrol.msrpm_base_pa;\n \tvmcb_mark_dirty(vmcb02, VMCB_PERM_MAP);\n \n+\t/*\n+\t * PML is never enabled in hardware for L2. Make sure that an\n+\t * unexpected PML write would trigger a PML_FULL VM-Exit.\n+\t */\n+\tif (pml)\n+\t\tvmcb02-\u003econtrol.pml_index = -1;\n+\n \t/*\n \t * Stash vmcb02's counter if the guest hasn't moved past the guilty\n \t * instruction; otherwise, reset the counter to '0'.\n@@ -1820,6 +1827,13 @@ int nested_svm_exit_special(struct vcpu_svm *svm)\n \t\tif (nested_svm_is_l2_tlb_flush_hcall(vcpu))\n \t\t\treturn NESTED_EXIT_HOST;\n \t\tbreak;\n+\tcase SVM_EXIT_PML_FULL:\n+\t\t/*\n+\t\t * All PML full exits are handled by KVM. KVM emulates PML in\n+\t\t * software for L1, but never enables PML in hardware on behalf\n+\t\t * of L1.\n+\t\t */\n+\t\treturn NESTED_EXIT_HOST;\n \tdefault:\n \t\tbreak;\n \t}\ndiff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c\nindex 5705723f1f412..cfe990f805040 100644\n--- a/arch/x86/kvm/svm/sev.c\n+++ b/arch/x86/kvm/svm/sev.c\n@@ -4994,7 +4994,7 @@ struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)\n \t * Allocate an SNP-safe page to workaround the SNP erratum where\n \t * the CPU will incorrectly signal an RMP violation #PF if a\n \t * hugepage (2MB or 1GB) collides with the RMP entry of a\n-\t * 2MB-aligned VMCB, VMSA, or AVIC backing page.\n+\t * 2MB-aligned VMCB, VMSA, PML or AVIC backing page.\n \t *\n \t * Allocate one extra page, choose a page which is not\n \t * 2MB-aligned, and free the other.\ndiff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c\nindex 7d59d301e1e54..950c250a77b51 100644\n--- a/arch/x86/kvm/svm/svm.c\n+++ b/arch/x86/kvm/svm/svm.c\n@@ -179,6 +179,9 @@ module_param(vnmi, bool, 0444);\n \n module_param(enable_mediated_pmu, bool, 0444);\n \n+bool __ro_after_init pml = true;\n+module_param(pml, bool, 0444);\n+\n static bool __ro_after_init svm_gp_erratum_intercept = true;\n \n static u8 rsm_ins_bytes[] = \"\\x0f\\xaa\";\n@@ -1267,6 +1270,23 @@ static void init_vmcb(struct kvm_vcpu *vcpu, bool init_event)\n \tif (vcpu-\u003ekvm-\u003earch.bus_lock_detection_enabled)\n \t\tsvm_set_intercept(svm, INTERCEPT_BUSLOCK);\n \n+\tif (pml) {\n+\t\t/*\n+\t\t * Populate the page address and index here, PML is enabled\n+\t\t * when dirty logging is enabled on the memslot through\n+\t\t * svm_update_cpu_dirty_logging()\n+\t\t */\n+\t\tcontrol-\u003epml_addr = (u64)__sme_set(page_to_phys(vcpu-\u003earch.pml_page));\n+\n+\t\t/*\n+\t\t * The VMCB control area remains valid after an intercepted SHUTDOWN\n+\t\t * (AMD APM Rev 3.45+), so only initialize PML index on reset to avoid\n+\t\t * discarding already-logged entries that haven't been flushed.\n+\t\t */\n+\t\tif (!init_event)\n+\t\t\tcontrol-\u003epml_index = PML_HEAD_INDEX;\n+\t}\n+\n \tif (is_sev_guest(vcpu))\n \t\tsev_init_vmcb(svm, init_event);\n \n@@ -1326,9 +1346,15 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)\n \tif (!vmcb01_page)\n \t\tgoto out;\n \n+\tif (pml) {\n+\t\tvcpu-\u003earch.pml_page = snp_safe_alloc_page();\n+\t\tif (!vcpu-\u003earch.pml_page)\n+\t\t\tgoto error_free_vmcb_page;\n+\t}\n+\n \terr = sev_vcpu_create(vcpu);\n \tif (err)\n-\t\tgoto error_free_vmcb_page;\n+\t\tgoto error_free_pml_page;\n \n \terr = avic_init_vcpu(svm);\n \tif (err)\n@@ -1353,6 +1379,9 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)\n \n error_free_sev:\n \tsev_free_vcpu(vcpu);\n+error_free_pml_page:\n+\tif (vcpu-\u003earch.pml_page)\n+\t\t__free_page(vcpu-\u003earch.pml_page);\n error_free_vmcb_page:\n \t__free_page(vmcb01_page);\n out:\n@@ -1370,6 +1399,9 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu)\n \n \tsev_free_vcpu(vcpu);\n \n+\tif (pml \u0026\u0026 vcpu-\u003earch.pml_page)\n+\t\t__free_page(vcpu-\u003earch.pml_page);\n+\n \t__free_page(__sme_pa_to_page(svm-\u003evmcb01.pa));\n \tsvm_vcpu_free_msrpm(svm-\u003emsrpm);\n }\n@@ -3344,6 +3376,45 @@ static int vmmcall_interception(struct kvm_vcpu *vcpu)\n \treturn kvm_emulate_hypercall(vcpu);\n }\n \n+static void svm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)\n+{\n+\tstruct vcpu_svm *svm = to_svm(vcpu);\n+\tstruct vmcb *vmcb01 = svm-\u003evmcb01.ptr;\n+\n+\tif (enable)\n+\t\tvmcb01-\u003econtrol.misc_ctl |= SVM_MISC_ENABLE_PML;\n+\telse\n+\t\tvmcb01-\u003econtrol.misc_ctl \u0026= ~SVM_MISC_ENABLE_PML;\n+\n+\tvmcb_mark_dirty(vmcb01, VMCB_NPT);\n+}\n+\n+static void svm_flush_pml_buffer(struct kvm_vcpu *vcpu)\n+{\n+\tstruct vcpu_svm *svm = to_svm(vcpu);\n+\tstruct vmcb_control_area *control = \u0026svm-\u003evmcb-\u003econtrol;\n+\n+\t/* Do nothing if PML buffer is empty */\n+\tif (control-\u003epml_index == PML_HEAD_INDEX)\n+\t\treturn;\n+\n+\tkvm_flush_pml_buffer(vcpu, control-\u003epml_index);\n+\n+\t/* Reset the PML index */\n+\tcontrol-\u003epml_index = PML_HEAD_INDEX;\n+}\n+\n+static int pml_full_interception(struct kvm_vcpu *vcpu)\n+{\n+\ttrace_kvm_pml_full(vcpu-\u003evcpu_id);\n+\n+\t/*\n+\t * PML buffer is already flushed at the beginning of svm_handle_exit().\n+\t * Nothing to do here.\n+\t */\n+\treturn 1;\n+}\n+\n static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {\n \t[SVM_EXIT_READ_CR0]\t\t\t= cr_interception,\n \t[SVM_EXIT_READ_CR3]\t\t\t= cr_interception,\n@@ -3421,6 +3492,7 @@ static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {\n #ifdef CONFIG_KVM_AMD_SEV\n \t[SVM_EXIT_VMGEXIT]\t\t\t= sev_handle_vmgexit,\n #endif\n+\t[SVM_EXIT_PML_FULL]\t\t\t= pml_full_interception,\n };\n \n static void dump_vmcb(struct kvm_vcpu *vcpu)\n@@ -3470,8 +3542,14 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)\n \tpr_err(\"%-20s%016llx\\n\", \"exit_info2:\", control-\u003eexit_info_2);\n \tpr_err(\"%-20s%08x\\n\", \"exit_int_info:\", control-\u003eexit_int_info);\n \tpr_err(\"%-20s%08x\\n\", \"exit_int_info_err:\", control-\u003eexit_int_info_err);\n-\tpr_err(\"%-20s%lld\\n\", \"misc_ctl:\", control-\u003emisc_ctl);\n+\tpr_err(\"%-20s%llx\\n\", \"misc_ctl:\", control-\u003emisc_ctl);\n \tpr_err(\"%-20s%016llx\\n\", \"nested_cr3:\", control-\u003enested_cr3);\n+\n+\tif (pml) {\n+\t\tpr_err(\"%-20s%016llx\\n\", \"pml_addr:\", control-\u003epml_addr);\n+\t\tpr_err(\"%-20s%04x\\n\", \"pml_index:\", control-\u003epml_index);\n+\t}\n+\n \tpr_err(\"%-20s%016llx\\n\", \"avic_vapic_bar:\", control-\u003eavic_vapic_bar);\n \tpr_err(\"%-20s%016llx\\n\", \"ghcb:\", control-\u003eghcb_gpa);\n \tpr_err(\"%-20s%08x\\n\", \"event_inj:\", control-\u003eevent_inj);\n@@ -3649,6 +3727,13 @@ int svm_invoke_exit_handler(struct kvm_vcpu *vcpu, u64 __exit_code)\n \t (u64)exit_code != __exit_code)\n \t\tgoto unexpected_vmexit;\n \n+\t/*\n+\t * PML is never enabled when running L2, bail immediately if a PML full\n+\t * exit occurs as something is horribly wrong.\n+\t */\n+\tif (unlikely(is_guest_mode(vcpu) \u0026\u0026 exit_code == SVM_EXIT_PML_FULL))\n+\t\tgoto unexpected_vmexit;\n+\n #ifdef CONFIG_MITIGATION_RETPOLINE\n \tif (exit_code == SVM_EXIT_MSR)\n \t\treturn msr_interception(vcpu);\n@@ -3717,6 +3802,14 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)\n \tstruct vcpu_svm *svm = to_svm(vcpu);\n \tstruct kvm_run *kvm_run = vcpu-\u003erun;\n \n+\t/*\n+\t * Opportunistically flush the PML buffer on VM exit. This keeps the\n+\t * dirty bitmap current by processing logged GPAs rather than waiting for\n+\t * PML_FULL exit.\n+\t */\n+\tif (vcpu-\u003ekvm-\u003earch.cpu_dirty_log_size \u0026\u0026 !is_guest_mode(vcpu))\n+\t\tsvm_flush_pml_buffer(vcpu);\n+\n \tif (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE))\n \t\treturn 0;\n \n@@ -5311,6 +5404,9 @@ static int svm_vm_init(struct kvm *kvm)\n \tif (!pause_filter_count || !pause_filter_thresh)\n \t\tkvm_disable_exits(kvm, KVM_X86_DISABLE_EXITS_PAUSE);\n \n+\tif (pml)\n+\t\tkvm-\u003earch.cpu_dirty_log_size = PML_LOG_NR_ENTRIES;\n+\n \tsvm_srso_vm_init();\n \treturn 0;\n }\n@@ -5429,6 +5525,8 @@ struct kvm_x86_ops svm_x86_ops __initdata = {\n \t.check_intercept = svm_check_intercept,\n \t.handle_exit_irqoff = svm_handle_exit_irqoff,\n \n+\t.update_cpu_dirty_logging = svm_update_cpu_dirty_logging,\n+\n \t.deliver_interrupt = svm_deliver_interrupt,\n \t.pi_update_irte = avic_pi_update_irte,\n \t.setup_mce = svm_setup_mce,\n@@ -5689,6 +5787,10 @@ static __init int svm_hardware_setup(void)\n \n \tnrips = nrips \u0026\u0026 boot_cpu_has(X86_FEATURE_NRIPS);\n \n+\tpml = pml \u0026\u0026 npt_enabled \u0026\u0026 cpu_feature_enabled(X86_FEATURE_PML);\n+\tif (pml)\n+\t\tpr_info(\"Page modification logging supported\\n\");\n+\n \tif (lbrv) {\n \t\tif (!boot_cpu_has(X86_FEATURE_LBRV))\n \t\t\tlbrv = false;\ndiff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h\nindex e958943b81627..f566dae44bb0f 100644\n--- a/arch/x86/kvm/svm/svm.h\n+++ b/arch/x86/kvm/svm/svm.h\n@@ -53,6 +53,7 @@ extern int vgif;\n extern bool intercept_smi;\n extern bool vnmi;\n extern int lbrv;\n+extern bool pml;\n \n extern int tsc_aux_uret_slot __ro_after_init;\n \ndiff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c\nindex 4c52ab8d07869..e77b5b731275c 100644\n--- a/arch/x86/kvm/vmx/main.c\n+++ b/arch/x86/kvm/vmx/main.c\n@@ -108,7 +108,7 @@ static void vt_vcpu_load(struct kvm_vcpu *vcpu, int cpu)\n \tvmx_vcpu_load(vcpu, cpu);\n }\n \n-static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)\n+static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)\n {\n \t/*\n \t * Basic TDX does not support feature PML. KVM does not enable PML in\n@@ -117,7 +117,7 @@ static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)\n \tif (WARN_ON_ONCE(is_td_vcpu(vcpu)))\n \t\treturn;\n \n-\tvmx_update_cpu_dirty_logging(vcpu);\n+\tvmx_update_cpu_dirty_logging(vcpu, enable);\n }\n \n static void vt_prepare_switch_to_guest(struct kvm_vcpu *vcpu)\ndiff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c\nindex 612ab07d4100f..b3768b4b212fe 100644\n--- a/arch/x86/kvm/vmx/vmx.c\n+++ b/arch/x86/kvm/vmx/vmx.c\n@@ -4883,10 +4883,11 @@ int vmx_vcpu_precreate(struct kvm *kvm)\n \n #define VMX_XSS_EXIT_BITMAP 0\n \n-static void init_vmcs(struct vcpu_vmx *vmx)\n+static void init_vmcs(struct kvm_vcpu *vcpu)\n {\n-\tstruct kvm *kvm = vmx-\u003evcpu.kvm;\n+\tstruct kvm *kvm = vcpu-\u003ekvm;\n \tstruct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm);\n+\tstruct vcpu_vmx *vmx = to_vmx(vcpu);\n \n \tif (nested)\n \t\tnested_vmx_set_vmcs_shadowing_bitmap();\n@@ -4911,7 +4912,7 @@ static void init_vmcs(struct vcpu_vmx *vmx)\n \tif (cpu_has_tertiary_exec_ctrls())\n \t\ttertiary_exec_controls_set(vmx, vmx_tertiary_exec_control(vmx));\n \n-\tif (enable_apicv \u0026\u0026 lapic_in_kernel(\u0026vmx-\u003evcpu)) {\n+\tif (enable_apicv \u0026\u0026 lapic_in_kernel(vcpu)) {\n \t\tvmcs_write64(EOI_EXIT_BITMAP0, 0);\n \t\tvmcs_write64(EOI_EXIT_BITMAP1, 0);\n \t\tvmcs_write64(EOI_EXIT_BITMAP2, 0);\n@@ -4923,7 +4924,7 @@ static void init_vmcs(struct vcpu_vmx *vmx)\n \t\tvmcs_write64(POSTED_INTR_DESC_ADDR, __pa((\u0026vmx-\u003evt.pi_desc)));\n \t}\n \n-\tif (vmx_can_use_ipiv(\u0026vmx-\u003evcpu)) {\n+\tif (vmx_can_use_ipiv(vcpu)) {\n \t\tvmcs_write64(PID_POINTER_TABLE, __pa(kvm_vmx-\u003epid_table));\n \t\tvmcs_write16(LAST_PID_POINTER_INDEX, kvm-\u003earch.max_vcpu_ids - 1);\n \t}\n@@ -4958,15 +4959,15 @@ static void init_vmcs(struct vcpu_vmx *vmx)\n \tvmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx-\u003emsr_autoload.guest.val));\n \n \tif (vmcs_config.vmentry_ctrl \u0026 VM_ENTRY_LOAD_IA32_PAT)\n-\t\tvmcs_write64(GUEST_IA32_PAT, vmx-\u003evcpu.arch.pat);\n+\t\tvmcs_write64(GUEST_IA32_PAT, vcpu-\u003earch.pat);\n \n \tvm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl());\n \n \t/* 22.2.1, 20.8.1 */\n \tvm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl());\n \n-\tvmx-\u003evcpu.arch.cr0_guest_owned_bits = vmx_l1_guest_owned_cr0_bits();\n-\tvmcs_writel(CR0_GUEST_HOST_MASK, ~vmx-\u003evcpu.arch.cr0_guest_owned_bits);\n+\tvcpu-\u003earch.cr0_guest_owned_bits = vmx_l1_guest_owned_cr0_bits();\n+\tvmcs_writel(CR0_GUEST_HOST_MASK, ~vcpu-\u003earch.cr0_guest_owned_bits);\n \n \tset_cr4_guest_host_mask(vmx);\n \n@@ -4977,11 +4978,11 @@ static void init_vmcs(struct vcpu_vmx *vmx)\n \t\tvmcs_write64(XSS_EXIT_BITMAP, VMX_XSS_EXIT_BITMAP);\n \n \tif (enable_pml) {\n-\t\tvmcs_write64(PML_ADDRESS, page_to_phys(vmx-\u003epml_pg));\n+\t\tvmcs_write64(PML_ADDRESS, page_to_phys(vcpu-\u003earch.pml_page));\n \t\tvmcs_write16(GUEST_PML_INDEX, PML_HEAD_INDEX);\n \t}\n \n-\tvmx_write_encls_bitmap(\u0026vmx-\u003evcpu, NULL);\n+\tvmx_write_encls_bitmap(vcpu, NULL);\n \n \tif (vmx_pt_mode_is_host_guest()) {\n \t\tmemset(\u0026vmx-\u003ept_desc, 0, sizeof(vmx-\u003ept_desc));\n@@ -4994,13 +4995,13 @@ static void init_vmcs(struct vcpu_vmx *vmx)\n \tvmcs_writel(GUEST_SYSENTER_ESP, 0);\n \tvmcs_writel(GUEST_SYSENTER_EIP, 0);\n \n-\tvmx_guest_debugctl_write(\u0026vmx-\u003evcpu, 0);\n+\tvmx_guest_debugctl_write(vcpu, 0);\n \n \tif (cpu_has_vmx_tpr_shadow()) {\n \t\tvmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0);\n-\t\tif (cpu_need_tpr_shadow(\u0026vmx-\u003evcpu))\n+\t\tif (cpu_need_tpr_shadow(vcpu))\n \t\t\tvmcs_write64(VIRTUAL_APIC_PAGE_ADDR,\n-\t\t\t\t __pa(vmx-\u003evcpu.arch.apic-\u003eregs));\n+\t\t\t\t __pa(vcpu-\u003earch.apic-\u003eregs));\n \t\tvmcs_write32(TPR_THRESHOLD, 0);\n \t}\n \n@@ -5011,7 +5012,7 @@ static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu)\n {\n \tstruct vcpu_vmx *vmx = to_vmx(vcpu);\n \n-\tinit_vmcs(vmx);\n+\tinit_vmcs(vcpu);\n \n \tif (nested \u0026\u0026\n \t kvm_check_has_quirk(vcpu-\u003ekvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS))\n@@ -6418,48 +6419,25 @@ void vmx_get_entry_info(struct kvm_vcpu *vcpu, u32 *intr_info, u32 *error_code)\n \t\t*error_code = 0;\n }\n \n-static void vmx_destroy_pml_buffer(struct vcpu_vmx *vmx)\n+static void vmx_destroy_pml_buffer(struct kvm_vcpu *vcpu)\n {\n-\tif (vmx-\u003epml_pg) {\n-\t\t__free_page(vmx-\u003epml_pg);\n-\t\tvmx-\u003epml_pg = NULL;\n+\tif (vcpu-\u003earch.pml_page) {\n+\t\t__free_page(vcpu-\u003earch.pml_page);\n+\t\tvcpu-\u003earch.pml_page = NULL;\n \t}\n }\n \n static void vmx_flush_pml_buffer(struct kvm_vcpu *vcpu)\n {\n-\tstruct vcpu_vmx *vmx = to_vmx(vcpu);\n-\tu16 pml_idx, pml_tail_index;\n-\tu64 *pml_buf;\n-\tint i;\n+\tu16 pml_idx;\n \n \tpml_idx = vmcs_read16(GUEST_PML_INDEX);\n \n \t/* Do nothing if PML buffer is empty */\n \tif (pml_idx == PML_HEAD_INDEX)\n \t\treturn;\n-\t/*\n-\t * PML index always points to the next available PML buffer entity\n-\t * unless PML log has just overflowed.\n-\t */\n-\tpml_tail_index = (pml_idx \u003e= PML_LOG_NR_ENTRIES) ? 0 : pml_idx + 1;\n-\n-\t/*\n-\t * PML log is written backwards: the CPU first writes the entry 511\n-\t * then the entry 510, and so on.\n-\t *\n-\t * Read the entries in the same order they were written, to ensure that\n-\t * the dirty ring is filled in the same order the CPU wrote them.\n-\t */\n-\tpml_buf = page_address(vmx-\u003epml_pg);\n \n-\tfor (i = PML_HEAD_INDEX; i \u003e= pml_tail_index; i--) {\n-\t\tu64 gpa;\n-\n-\t\tgpa = pml_buf[i];\n-\t\tWARN_ON(gpa \u0026 (PAGE_SIZE - 1));\n-\t\tkvm_vcpu_mark_page_dirty(vcpu, gpa \u003e\u003e PAGE_SHIFT);\n-\t}\n+\tkvm_flush_pml_buffer(vcpu, pml_idx);\n \n \t/* reset PML index */\n \tvmcs_write16(GUEST_PML_INDEX, PML_HEAD_INDEX);\n@@ -7648,7 +7626,7 @@ void vmx_vcpu_free(struct kvm_vcpu *vcpu)\n \tstruct vcpu_vmx *vmx = to_vmx(vcpu);\n \n \tif (enable_pml)\n-\t\tvmx_destroy_pml_buffer(vmx);\n+\t\tvmx_destroy_pml_buffer(vcpu);\n \tfree_vpid(vmx-\u003evpid);\n \tnested_vmx_free_vcpu(vcpu);\n \tfree_loaded_vmcs(vmx-\u003eloaded_vmcs);\n@@ -7680,8 +7658,8 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu)\n \t * for the guest), etc.\n \t */\n \tif (enable_pml) {\n-\t\tvmx-\u003epml_pg = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);\n-\t\tif (!vmx-\u003epml_pg)\n+\t\tvcpu-\u003earch.pml_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);\n+\t\tif (!vcpu-\u003earch.pml_page)\n \t\t\tgoto free_vpid;\n \t}\n \n@@ -7752,7 +7730,7 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu)\n free_vmcs:\n \tfree_loaded_vmcs(vmx-\u003eloaded_vmcs);\n free_pml:\n-\tvmx_destroy_pml_buffer(vmx);\n+\tvmx_destroy_pml_buffer(vcpu);\n free_vpid:\n \tfree_vpid(vmx-\u003evpid);\n \treturn err;\n@@ -8440,21 +8418,13 @@ static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit\n }\n #endif\n \n-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)\n+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)\n {\n \tstruct vcpu_vmx *vmx = to_vmx(vcpu);\n \n-\tif (WARN_ON_ONCE(!enable_pml))\n-\t\treturn;\n-\n \tguard(vmx_vmcs01)(vcpu);\n \n-\t/*\n-\t * Note, nr_memslots_dirty_logging can be changed concurrent with this\n-\t * code, but in that case another update request will be made and so\n-\t * the guest will never run with a stale PML value.\n-\t */\n-\tif (atomic_read(\u0026vcpu-\u003ekvm-\u003enr_memslots_dirty_logging))\n+\tif (enable)\n \t\tsecondary_exec_controls_setbit(vmx, SECONDARY_EXEC_ENABLE_PML);\n \telse\n \t\tsecondary_exec_controls_clearbit(vmx, SECONDARY_EXEC_ENABLE_PML);\ndiff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h\nindex dc8517f15bc46..5f505ad14dde1 100644\n--- a/arch/x86/kvm/vmx/vmx.h\n+++ b/arch/x86/kvm/vmx/vmx.h\n@@ -265,13 +265,6 @@ struct vcpu_vmx {\n \tunsigned int ple_window;\n \tbool ple_window_dirty;\n \n-\t/* Support for PML */\n-#define PML_LOG_NR_ENTRIES\t512\n-\t/* PML is written backwards: this is the first entry written by the CPU */\n-#define PML_HEAD_INDEX\t\t(PML_LOG_NR_ENTRIES-1)\n-\n-\tstruct page *pml_pg;\n-\n \t/* apic deadline value in host tsc */\n \tu64 hv_deadline_tsc;\n \n@@ -397,7 +390,7 @@ u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu);\n \n gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags);\n \n-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu);\n+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable);\n \n u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated);\n bool vmx_is_valid_debugctl(struct kvm_vcpu *vcpu, u64 data, bool host_initiated);\ndiff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h\nindex 054fd14bb2e16..4810f9b1b0fc6 100644\n--- a/arch/x86/kvm/vmx/x86_ops.h\n+++ b/arch/x86/kvm/vmx/x86_ops.h\n@@ -118,7 +118,7 @@ u64 vmx_get_l2_tsc_offset(struct kvm_vcpu *vcpu);\n u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu);\n void vmx_write_tsc_offset(struct kvm_vcpu *vcpu);\n void vmx_write_tsc_multiplier(struct kvm_vcpu *vcpu);\n-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu);\n+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable);\n #ifdef CONFIG_X86_64\n int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,\n \t\t bool *expired);\ndiff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c\nindex 79468ddfe4736..6d26de8b7c3cc 100644\n--- a/arch/x86/kvm/x86.c\n+++ b/arch/x86/kvm/x86.c\n@@ -3941,6 +3941,37 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)\n \t\tkvm_vcpu_kick(vcpu);\n }\n \n+void kvm_flush_pml_buffer(struct kvm_vcpu *vcpu, u16 pml_idx)\n+{\n+\tu16 pml_tail_index;\n+\tu64 *pml_buf;\n+\tint i;\n+\n+\t/*\n+\t * PML index always points to the next available PML buffer entity\n+\t * unless PML log has just overflowed.\n+\t */\n+\tpml_tail_index = (pml_idx \u003e= PML_LOG_NR_ENTRIES) ? 0 : pml_idx + 1;\n+\n+\t/*\n+\t * PML log is written backwards: the CPU first writes the entry 511\n+\t * then the entry 510, and so on.\n+\t *\n+\t * Read the entries in the same order they were written, to ensure that\n+\t * the dirty ring is filled in the same order the CPU wrote them.\n+\t */\n+\tpml_buf = page_address(vcpu-\u003earch.pml_page);\n+\n+\tfor (i = PML_HEAD_INDEX; i \u003e= pml_tail_index; i--) {\n+\t\tu64 gpa;\n+\n+\t\tgpa = pml_buf[i];\n+\t\tWARN_ON_ONCE(gpa \u0026 (PAGE_SIZE - 1));\n+\t\tkvm_vcpu_mark_page_dirty(vcpu, gpa \u003e\u003e PAGE_SHIFT);\n+\t}\n+}\n+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_flush_pml_buffer);\n+\n int kvm_vm_ioctl_enable_cap(struct kvm *kvm,\n \t\t\t struct kvm_enable_cap *cap)\n {\n@@ -8042,6 +8073,21 @@ static void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu)\n \tkvm_x86_call(set_apic_access_page_addr)(vcpu);\n }\n \n+static void kvm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)\n+{\n+\t/*\n+\t * Note, nr_memslots_dirty_logging can be changed concurrent with this\n+\t * code, but in that case another update request will be made and so\n+\t * the guest will never run with a stale PML value.\n+\t */\n+\tbool enable = atomic_read(\u0026vcpu-\u003ekvm-\u003enr_memslots_dirty_logging);\n+\n+\tif (WARN_ON_ONCE(!vcpu-\u003ekvm-\u003earch.cpu_dirty_log_size))\n+\t\treturn;\n+\n+\tkvm_x86_call(update_cpu_dirty_logging)(vcpu, enable);\n+}\n+\n /*\n * Called within kvm-\u003esrcu read side.\n * Returns 1 to let vcpu_run() continue the guest execution loop without\n@@ -8208,7 +8254,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)\n \t\t\tkvm_x86_call(recalc_intercepts)(vcpu);\n \n \t\tif (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu))\n-\t\t\tkvm_x86_call(update_cpu_dirty_logging)(vcpu);\n+\t\t\tkvm_update_cpu_dirty_logging(vcpu);\n \n \t\tif (kvm_check_request(KVM_REQ_UPDATE_PROTECTED_GUEST_STATE, vcpu)) {\n \t\t\tkvm_vcpu_reset(vcpu, true);\ndiff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h\nindex 0f5919b092e47..2d747c20965ce 100644\n--- a/arch/x86/kvm/x86.h\n+++ b/arch/x86/kvm/x86.h\n@@ -900,4 +900,11 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl,\n \n int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);\n \n+/* Support for PML */\n+#define PML_LOG_NR_ENTRIES\t512\n+/* PML is written backwards: this is the first entry written by the CPU */\n+#define PML_HEAD_INDEX\t\t(PML_LOG_NR_ENTRIES-1)\n+\n+void kvm_flush_pml_buffer(struct kvm_vcpu *vcpu, u16 pml_idx);\n+\n #endif\n"
} |
| 2/1 | 2026/09/07 09:10 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": [
"KVM",
"KVM_AMD",
"KVM_INTEL"
],
"FocusSymbols": [
"kvm_flush_pml_buffer",
"kvm_update_cpu_dirty_logging",
"svm_update_cpu_dirty_logging",
"svm_flush_pml_buffer",
"pml_full_interception",
"nested_svm_exit_special",
"nested_vmcb02_prepare_control",
"vmx_update_cpu_dirty_logging",
"vmx_flush_pml_buffer"
],
"Reasoning": "The patch implements Page Modification Logging (PML) dirty logging support for AMD SVM and refactors the shared PML buffer flushing and dirty logging state machine across x86 KVM, Intel VMX, and AMD SVM. These changes touch reachable core virtualization code paths in KVM and affect nested virtualization and exit handling.",
"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 a8b5312779dd64dbc6311a236fd260a349381dd6
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Sep 7 09:10:51 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index f70ee74b5f921..cbfcc66a18e9b 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -238,6 +238,7 @@
// free: was #define X86_FEATURE_PVUNLOCK ( 8*32+20) /* PV unlock function */
#define X86_FEATURE_VCPUPREEMPT ( 8*32+21) /* PV vcpu_is_preempted function */
#define X86_FEATURE_TDX_GUEST ( 8*32+22) /* "tdx_guest" Intel Trust Domain Extensions Guest */
+#define X86_FEATURE_PML ( 8*32+23) /* AMD Page Modification logging */
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
#define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* "fsgsbase" RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 3af7395c2430a..2debf62942909 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -789,6 +789,8 @@ struct kvm_vcpu_arch {
*/
struct kvm_mmu_memory_cache mmu_external_spt_cache;
+ struct page *pml_page;
+
/*
* QEMU userspace and the guest each have their own FPU state.
* In vcpu_run, we switch between the user and guest FPU contexts.
@@ -1670,7 +1672,7 @@ struct kvm_x86_ops {
struct x86_exception *exception);
void (*handle_exit_irqoff)(struct kvm_vcpu *vcpu);
- void (*update_cpu_dirty_logging)(struct kvm_vcpu *vcpu);
+ void (*update_cpu_dirty_logging)(struct kvm_vcpu *vcpu, bool enable);
void (*vcpu_blocking)(struct kvm_vcpu *vcpu);
void (*vcpu_unblocking)(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index aa63431ba92c3..e0a7549a7c727 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -165,7 +165,10 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
u8 reserved_9[22];
u64 allowed_sev_features; /* Offset 0x138 */
u64 guest_sev_features; /* Offset 0x140 */
- u8 reserved_10[664];
+ u8 reserved_10[128];
+ u64 pml_addr; /* Offset 0x1c8 */
+ u16 pml_index; /* Offset 0x1d0 */
+ u8 reserved_11[526];
/*
* Offset 0x3e0, 32 bytes reserved
* for use by hypervisor/software.
@@ -240,10 +243,11 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
#define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT)
#define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT)
-#define SVM_MISC_ENABLE_NP BIT(0)
-#define SVM_MISC_ENABLE_SEV BIT(1)
-#define SVM_MISC_ENABLE_SEV_ES BIT(2)
-#define SVM_MISC_ENABLE_GMET BIT(3)
+#define SVM_MISC_ENABLE_NP BIT_ULL(0)
+#define SVM_MISC_ENABLE_SEV BIT_ULL(1)
+#define SVM_MISC_ENABLE_SEV_ES BIT_ULL(2)
+#define SVM_MISC_ENABLE_GMET BIT_ULL(3)
+#define SVM_MISC_ENABLE_PML BIT_ULL(11)
#define SVM_MISC2_ENABLE_V_LBR BIT_ULL(0)
#define SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE BIT_ULL(1)
diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index 010a45c9f6147..e806761850921 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -101,6 +101,7 @@
#define SVM_EXIT_AVIC_INCOMPLETE_IPI 0x401
#define SVM_EXIT_AVIC_UNACCELERATED_ACCESS 0x402
#define SVM_EXIT_VMGEXIT 0x403
+#define SVM_EXIT_PML_FULL 0x407
/* SEV-ES software-defined VMGEXIT events */
#define SVM_VMGEXIT_MMIO_READ 0x80000001ull
@@ -236,6 +237,7 @@
{ SVM_EXIT_AVIC_INCOMPLETE_IPI, "avic_incomplete_ipi" }, \
{ SVM_EXIT_AVIC_UNACCELERATED_ACCESS, "avic_unaccelerated_access" }, \
{ SVM_EXIT_VMGEXIT, "vmgexit" }, \
+ { SVM_EXIT_PML_FULL, "pml_full" }, \
{ SVM_VMGEXIT_MMIO_READ, "vmgexit_mmio_read" }, \
{ SVM_VMGEXIT_MMIO_WRITE, "vmgexit_mmio_write" }, \
{ SVM_VMGEXIT_NMI_COMPLETE, "vmgexit_nmi_complete" }, \
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 8665a6474806c..b18cc9c88dd57 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -55,6 +55,7 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_AMD_FAST_CPPC, CPUID_EDX, 15, 0x80000007, 0 },
{ X86_FEATURE_CPPC_PERF_PRIO, CPUID_EDX, 16, 0x80000007, 0 },
{ X86_FEATURE_MBA, CPUID_EBX, 6, 0x80000008, 0 },
+ { X86_FEATURE_PML, CPUID_ECX, 4, 0x8000000a, 0 },
{ X86_FEATURE_X2AVIC_EXT, CPUID_ECX, 6, 0x8000000a, 0 },
{ X86_FEATURE_COHERENCY_SFW_NO, CPUID_EBX, 31, 0x8000001f, 0 },
{ X86_FEATURE_SMBA, CPUID_EBX, 2, 0x80000020, 0 },
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 73f37b050d0a0..da417c4c59154 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -901,6 +901,13 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
vmcb02->control.msrpm_base_pa = vmcb01->control.msrpm_base_pa;
vmcb_mark_dirty(vmcb02, VMCB_PERM_MAP);
+ /*
+ * PML is never enabled in hardware for L2. Make sure that an
+ * unexpected PML write would trigger a PML_FULL VM-Exit.
+ */
+ if (pml)
+ vmcb02->control.pml_index = -1;
+
/*
* Stash vmcb02's counter if the guest hasn't moved past the guilty
* instruction; otherwise, reset the counter to '0'.
@@ -1820,6 +1827,13 @@ int nested_svm_exit_special(struct vcpu_svm *svm)
if (nested_svm_is_l2_tlb_flush_hcall(vcpu))
return NESTED_EXIT_HOST;
break;
+ case SVM_EXIT_PML_FULL:
+ /*
+ * All PML full exits are handled by KVM. KVM emulates PML in
+ * software for L1, but never enables PML in hardware on behalf
+ * of L1.
+ */
+ return NESTED_EXIT_HOST;
default:
break;
}
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 5705723f1f412..cfe990f805040 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4994,7 +4994,7 @@ struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)
* Allocate an SNP-safe page to workaround the SNP erratum where
* the CPU will incorrectly signal an RMP violation #PF if a
* hugepage (2MB or 1GB) collides with the RMP entry of a
- * 2MB-aligned VMCB, VMSA, or AVIC backing page.
+ * 2MB-aligned VMCB, VMSA, PML or AVIC backing page.
*
* Allocate one extra page, choose a page which is not
* 2MB-aligned, and free the other.
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7d59d301e1e54..950c250a77b51 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -179,6 +179,9 @@ module_param(vnmi, bool, 0444);
module_param(enable_mediated_pmu, bool, 0444);
+bool __ro_after_init pml = true;
+module_param(pml, bool, 0444);
+
static bool __ro_after_init svm_gp_erratum_intercept = true;
static u8 rsm_ins_bytes[] = "\x0f\xaa";
@@ -1267,6 +1270,23 @@ static void init_vmcb(struct kvm_vcpu *vcpu, bool init_event)
if (vcpu->kvm->arch.bus_lock_detection_enabled)
svm_set_intercept(svm, INTERCEPT_BUSLOCK);
+ if (pml) {
+ /*
+ * Populate the page address and index here, PML is enabled
+ * when dirty logging is enabled on the memslot through
+ * svm_update_cpu_dirty_logging()
+ */
+ control->pml_addr = (u64)__sme_set(page_to_phys(vcpu->arch.pml_page));
+
+ /*
+ * The VMCB control area remains valid after an intercepted SHUTDOWN
+ * (AMD APM Rev 3.45+), so only initialize PML index on reset to avoid
+ * discarding already-logged entries that haven't been flushed.
+ */
+ if (!init_event)
+ control->pml_index = PML_HEAD_INDEX;
+ }
+
if (is_sev_guest(vcpu))
sev_init_vmcb(svm, init_event);
@@ -1326,9 +1346,15 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
if (!vmcb01_page)
goto out;
+ if (pml) {
+ vcpu->arch.pml_page = snp_safe_alloc_page();
+ if (!vcpu->arch.pml_page)
+ goto error_free_vmcb_page;
+ }
+
err = sev_vcpu_create(vcpu);
if (err)
- goto error_free_vmcb_page;
+ goto error_free_pml_page;
err = avic_init_vcpu(svm);
if (err)
@@ -1353,6 +1379,9 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
error_free_sev:
sev_free_vcpu(vcpu);
+error_free_pml_page:
+ if (vcpu->arch.pml_page)
+ __free_page(vcpu->arch.pml_page);
error_free_vmcb_page:
__free_page(vmcb01_page);
out:
@@ -1370,6 +1399,9 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu)
sev_free_vcpu(vcpu);
+ if (pml && vcpu->arch.pml_page)
+ __free_page(vcpu->arch.pml_page);
+
__free_page(__sme_pa_to_page(svm->vmcb01.pa));
svm_vcpu_free_msrpm(svm->msrpm);
}
@@ -3344,6 +3376,45 @@ static int vmmcall_interception(struct kvm_vcpu *vcpu)
return kvm_emulate_hypercall(vcpu);
}
+static void svm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ struct vmcb *vmcb01 = svm->vmcb01.ptr;
+
+ if (enable)
+ vmcb01->control.misc_ctl |= SVM_MISC_ENABLE_PML;
+ else
+ vmcb01->control.misc_ctl &= ~SVM_MISC_ENABLE_PML;
+
+ vmcb_mark_dirty(vmcb01, VMCB_NPT);
+}
+
+static void svm_flush_pml_buffer(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ struct vmcb_control_area *control = &svm->vmcb->control;
+
+ /* Do nothing if PML buffer is empty */
+ if (control->pml_index == PML_HEAD_INDEX)
+ return;
+
+ kvm_flush_pml_buffer(vcpu, control->pml_index);
+
+ /* Reset the PML index */
+ control->pml_index = PML_HEAD_INDEX;
+}
+
+static int pml_full_interception(struct kvm_vcpu *vcpu)
+{
+ trace_kvm_pml_full(vcpu->vcpu_id);
+
+ /*
+ * PML buffer is already flushed at the beginning of svm_handle_exit().
+ * Nothing to do here.
+ */
+ return 1;
+}
+
static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
[SVM_EXIT_READ_CR0] = cr_interception,
[SVM_EXIT_READ_CR3] = cr_interception,
@@ -3421,6 +3492,7 @@ static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
#ifdef CONFIG_KVM_AMD_SEV
[SVM_EXIT_VMGEXIT] = sev_handle_vmgexit,
#endif
+ [SVM_EXIT_PML_FULL] = pml_full_interception,
};
static void dump_vmcb(struct kvm_vcpu *vcpu)
@@ -3470,8 +3542,14 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
pr_err("%-20s%016llx\n", "exit_info2:", control->exit_info_2);
pr_err("%-20s%08x\n", "exit_int_info:", control->exit_int_info);
pr_err("%-20s%08x\n", "exit_int_info_err:", control->exit_int_info_err);
- pr_err("%-20s%lld\n", "misc_ctl:", control->misc_ctl);
+ pr_err("%-20s%llx\n", "misc_ctl:", control->misc_ctl);
pr_err("%-20s%016llx\n", "nested_cr3:", control->nested_cr3);
+
+ if (pml) {
+ pr_err("%-20s%016llx\n", "pml_addr:", control->pml_addr);
+ pr_err("%-20s%04x\n", "pml_index:", control->pml_index);
+ }
+
pr_err("%-20s%016llx\n", "avic_vapic_bar:", control->avic_vapic_bar);
pr_err("%-20s%016llx\n", "ghcb:", control->ghcb_gpa);
pr_err("%-20s%08x\n", "event_inj:", control->event_inj);
@@ -3649,6 +3727,13 @@ int svm_invoke_exit_handler(struct kvm_vcpu *vcpu, u64 __exit_code)
(u64)exit_code != __exit_code)
goto unexpected_vmexit;
+ /*
+ * PML is never enabled when running L2, bail immediately if a PML full
+ * exit occurs as something is horribly wrong.
+ */
+ if (unlikely(is_guest_mode(vcpu) && exit_code == SVM_EXIT_PML_FULL))
+ goto unexpected_vmexit;
+
#ifdef CONFIG_MITIGATION_RETPOLINE
if (exit_code == SVM_EXIT_MSR)
return msr_interception(vcpu);
@@ -3717,6 +3802,14 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
struct vcpu_svm *svm = to_svm(vcpu);
struct kvm_run *kvm_run = vcpu->run;
+ /*
+ * Opportunistically flush the PML buffer on VM exit. This keeps the
+ * dirty bitmap current by processing logged GPAs rather than waiting for
+ * PML_FULL exit.
+ */
+ if (vcpu->kvm->arch.cpu_dirty_log_size && !is_guest_mode(vcpu))
+ svm_flush_pml_buffer(vcpu);
+
if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE))
return 0;
@@ -5311,6 +5404,9 @@ static int svm_vm_init(struct kvm *kvm)
if (!pause_filter_count || !pause_filter_thresh)
kvm_disable_exits(kvm, KVM_X86_DISABLE_EXITS_PAUSE);
+ if (pml)
+ kvm->arch.cpu_dirty_log_size = PML_LOG_NR_ENTRIES;
+
svm_srso_vm_init();
return 0;
}
@@ -5429,6 +5525,8 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.check_intercept = svm_check_intercept,
.handle_exit_irqoff = svm_handle_exit_irqoff,
+ .update_cpu_dirty_logging = svm_update_cpu_dirty_logging,
+
.deliver_interrupt = svm_deliver_interrupt,
.pi_update_irte = avic_pi_update_irte,
.setup_mce = svm_setup_mce,
@@ -5689,6 +5787,10 @@ static __init int svm_hardware_setup(void)
nrips = nrips && boot_cpu_has(X86_FEATURE_NRIPS);
+ pml = pml && npt_enabled && cpu_feature_enabled(X86_FEATURE_PML);
+ if (pml)
+ pr_info("Page modification logging supported\n");
+
if (lbrv) {
if (!boot_cpu_has(X86_FEATURE_LBRV))
lbrv = false;
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index e958943b81627..f566dae44bb0f 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -53,6 +53,7 @@ extern int vgif;
extern bool intercept_smi;
extern bool vnmi;
extern int lbrv;
+extern bool pml;
extern int tsc_aux_uret_slot __ro_after_init;
diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
index 4c52ab8d07869..e77b5b731275c 100644
--- a/arch/x86/kvm/vmx/main.c
+++ b/arch/x86/kvm/vmx/main.c
@@ -108,7 +108,7 @@ static void vt_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
vmx_vcpu_load(vcpu, cpu);
}
-static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)
{
/*
* Basic TDX does not support feature PML. KVM does not enable PML in
@@ -117,7 +117,7 @@ static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
if (WARN_ON_ONCE(is_td_vcpu(vcpu)))
return;
- vmx_update_cpu_dirty_logging(vcpu);
+ vmx_update_cpu_dirty_logging(vcpu, enable);
}
static void vt_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 612ab07d4100f..b3768b4b212fe 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4883,10 +4883,11 @@ int vmx_vcpu_precreate(struct kvm *kvm)
#define VMX_XSS_EXIT_BITMAP 0
-static void init_vmcs(struct vcpu_vmx *vmx)
+static void init_vmcs(struct kvm_vcpu *vcpu)
{
- struct kvm *kvm = vmx->vcpu.kvm;
+ struct kvm *kvm = vcpu->kvm;
struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm);
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
if (nested)
nested_vmx_set_vmcs_shadowing_bitmap();
@@ -4911,7 +4912,7 @@ static void init_vmcs(struct vcpu_vmx *vmx)
if (cpu_has_tertiary_exec_ctrls())
tertiary_exec_controls_set(vmx, vmx_tertiary_exec_control(vmx));
- if (enable_apicv && lapic_in_kernel(&vmx->vcpu)) {
+ if (enable_apicv && lapic_in_kernel(vcpu)) {
vmcs_write64(EOI_EXIT_BITMAP0, 0);
vmcs_write64(EOI_EXIT_BITMAP1, 0);
vmcs_write64(EOI_EXIT_BITMAP2, 0);
@@ -4923,7 +4924,7 @@ static void init_vmcs(struct vcpu_vmx *vmx)
vmcs_write64(POSTED_INTR_DESC_ADDR, __pa((&vmx->vt.pi_desc)));
}
- if (vmx_can_use_ipiv(&vmx->vcpu)) {
+ if (vmx_can_use_ipiv(vcpu)) {
vmcs_write64(PID_POINTER_TABLE, __pa(kvm_vmx->pid_table));
vmcs_write16(LAST_PID_POINTER_INDEX, kvm->arch.max_vcpu_ids - 1);
}
@@ -4958,15 +4959,15 @@ static void init_vmcs(struct vcpu_vmx *vmx)
vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.guest.val));
if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT)
- vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat);
+ vmcs_write64(GUEST_IA32_PAT, vcpu->arch.pat);
vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl());
/* 22.2.1, 20.8.1 */
vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl());
- vmx->vcpu.arch.cr0_guest_owned_bits = vmx_l1_guest_owned_cr0_bits();
- vmcs_writel(CR0_GUEST_HOST_MASK, ~vmx->vcpu.arch.cr0_guest_owned_bits);
+ vcpu->arch.cr0_guest_owned_bits = vmx_l1_guest_owned_cr0_bits();
+ vmcs_writel(CR0_GUEST_HOST_MASK, ~vcpu->arch.cr0_guest_owned_bits);
set_cr4_guest_host_mask(vmx);
@@ -4977,11 +4978,11 @@ static void init_vmcs(struct vcpu_vmx *vmx)
vmcs_write64(XSS_EXIT_BITMAP, VMX_XSS_EXIT_BITMAP);
if (enable_pml) {
- vmcs_write64(PML_ADDRESS, page_to_phys(vmx->pml_pg));
+ vmcs_write64(PML_ADDRESS, page_to_phys(vcpu->arch.pml_page));
vmcs_write16(GUEST_PML_INDEX, PML_HEAD_INDEX);
}
- vmx_write_encls_bitmap(&vmx->vcpu, NULL);
+ vmx_write_encls_bitmap(vcpu, NULL);
if (vmx_pt_mode_is_host_guest()) {
memset(&vmx->pt_desc, 0, sizeof(vmx->pt_desc));
@@ -4994,13 +4995,13 @@ static void init_vmcs(struct vcpu_vmx *vmx)
vmcs_writel(GUEST_SYSENTER_ESP, 0);
vmcs_writel(GUEST_SYSENTER_EIP, 0);
- vmx_guest_debugctl_write(&vmx->vcpu, 0);
+ vmx_guest_debugctl_write(vcpu, 0);
if (cpu_has_vmx_tpr_shadow()) {
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0);
- if (cpu_need_tpr_shadow(&vmx->vcpu))
+ if (cpu_need_tpr_shadow(vcpu))
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
- __pa(vmx->vcpu.arch.apic->regs));
+ __pa(vcpu->arch.apic->regs));
vmcs_write32(TPR_THRESHOLD, 0);
}
@@ -5011,7 +5012,7 @@ static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- init_vmcs(vmx);
+ init_vmcs(vcpu);
if (nested &&
kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS))
@@ -6418,48 +6419,25 @@ void vmx_get_entry_info(struct kvm_vcpu *vcpu, u32 *intr_info, u32 *error_code)
*error_code = 0;
}
-static void vmx_destroy_pml_buffer(struct vcpu_vmx *vmx)
+static void vmx_destroy_pml_buffer(struct kvm_vcpu *vcpu)
{
- if (vmx->pml_pg) {
- __free_page(vmx->pml_pg);
- vmx->pml_pg = NULL;
+ if (vcpu->arch.pml_page) {
+ __free_page(vcpu->arch.pml_page);
+ vcpu->arch.pml_page = NULL;
}
}
static void vmx_flush_pml_buffer(struct kvm_vcpu *vcpu)
{
- struct vcpu_vmx *vmx = to_vmx(vcpu);
- u16 pml_idx, pml_tail_index;
- u64 *pml_buf;
- int i;
+ u16 pml_idx;
pml_idx = vmcs_read16(GUEST_PML_INDEX);
/* Do nothing if PML buffer is empty */
if (pml_idx == PML_HEAD_INDEX)
return;
- /*
- * PML index always points to the next available PML buffer entity
- * unless PML log has just overflowed.
- */
- pml_tail_index = (pml_idx >= PML_LOG_NR_ENTRIES) ? 0 : pml_idx + 1;
-
- /*
- * PML log is written backwards: the CPU first writes the entry 511
- * then the entry 510, and so on.
- *
- * Read the entries in the same order they were written, to ensure that
- * the dirty ring is filled in the same order the CPU wrote them.
- */
- pml_buf = page_address(vmx->pml_pg);
- for (i = PML_HEAD_INDEX; i >= pml_tail_index; i--) {
- u64 gpa;
-
- gpa = pml_buf[i];
- WARN_ON(gpa & (PAGE_SIZE - 1));
- kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
- }
+ kvm_flush_pml_buffer(vcpu, pml_idx);
/* reset PML index */
vmcs_write16(GUEST_PML_INDEX, PML_HEAD_INDEX);
@@ -7648,7 +7626,7 @@ void vmx_vcpu_free(struct kvm_vcpu *vcpu)
struct vcpu_vmx *vmx = to_vmx(vcpu);
if (enable_pml)
- vmx_destroy_pml_buffer(vmx);
+ vmx_destroy_pml_buffer(vcpu);
free_vpid(vmx->vpid);
nested_vmx_free_vcpu(vcpu);
free_loaded_vmcs(vmx->loaded_vmcs);
@@ -7680,8 +7658,8 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu)
* for the guest), etc.
*/
if (enable_pml) {
- vmx->pml_pg = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
- if (!vmx->pml_pg)
+ vcpu->arch.pml_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+ if (!vcpu->arch.pml_page)
goto free_vpid;
}
@@ -7752,7 +7730,7 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu)
free_vmcs:
free_loaded_vmcs(vmx->loaded_vmcs);
free_pml:
- vmx_destroy_pml_buffer(vmx);
+ vmx_destroy_pml_buffer(vcpu);
free_vpid:
free_vpid(vmx->vpid);
return err;
@@ -8440,21 +8418,13 @@ static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit
}
#endif
-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- if (WARN_ON_ONCE(!enable_pml))
- return;
-
guard(vmx_vmcs01)(vcpu);
- /*
- * Note, nr_memslots_dirty_logging can be changed concurrent with this
- * code, but in that case another update request will be made and so
- * the guest will never run with a stale PML value.
- */
- if (atomic_read(&vcpu->kvm->nr_memslots_dirty_logging))
+ if (enable)
secondary_exec_controls_setbit(vmx, SECONDARY_EXEC_ENABLE_PML);
else
secondary_exec_controls_clearbit(vmx, SECONDARY_EXEC_ENABLE_PML);
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index dc8517f15bc46..5f505ad14dde1 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -265,13 +265,6 @@ struct vcpu_vmx {
unsigned int ple_window;
bool ple_window_dirty;
- /* Support for PML */
-#define PML_LOG_NR_ENTRIES 512
- /* PML is written backwards: this is the first entry written by the CPU */
-#define PML_HEAD_INDEX (PML_LOG_NR_ENTRIES-1)
-
- struct page *pml_pg;
-
/* apic deadline value in host tsc */
u64 hv_deadline_tsc;
@@ -397,7 +390,7 @@ u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu);
gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags);
-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu);
+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable);
u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated);
bool vmx_is_valid_debugctl(struct kvm_vcpu *vcpu, u64 data, bool host_initiated);
diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h
index 054fd14bb2e16..4810f9b1b0fc6 100644
--- a/arch/x86/kvm/vmx/x86_ops.h
+++ b/arch/x86/kvm/vmx/x86_ops.h
@@ -118,7 +118,7 @@ u64 vmx_get_l2_tsc_offset(struct kvm_vcpu *vcpu);
u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu);
void vmx_write_tsc_offset(struct kvm_vcpu *vcpu);
void vmx_write_tsc_multiplier(struct kvm_vcpu *vcpu);
-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu);
+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable);
#ifdef CONFIG_X86_64
int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,
bool *expired);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 79468ddfe4736..6d26de8b7c3cc 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3941,6 +3941,37 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
kvm_vcpu_kick(vcpu);
}
+void kvm_flush_pml_buffer(struct kvm_vcpu *vcpu, u16 pml_idx)
+{
+ u16 pml_tail_index;
+ u64 *pml_buf;
+ int i;
+
+ /*
+ * PML index always points to the next available PML buffer entity
+ * unless PML log has just overflowed.
+ */
+ pml_tail_index = (pml_idx >= PML_LOG_NR_ENTRIES) ? 0 : pml_idx + 1;
+
+ /*
+ * PML log is written backwards: the CPU first writes the entry 511
+ * then the entry 510, and so on.
+ *
+ * Read the entries in the same order they were written, to ensure that
+ * the dirty ring is filled in the same order the CPU wrote them.
+ */
+ pml_buf = page_address(vcpu->arch.pml_page);
+
+ for (i = PML_HEAD_INDEX; i >= pml_tail_index; i--) {
+ u64 gpa;
+
+ gpa = pml_buf[i];
+ WARN_ON_ONCE(gpa & (PAGE_SIZE - 1));
+ kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
+ }
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_flush_pml_buffer);
+
int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
struct kvm_enable_cap *cap)
{
@@ -8042,6 +8073,21 @@ static void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu)
kvm_x86_call(set_apic_access_page_addr)(vcpu);
}
+static void kvm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+{
+ /*
+ * Note, nr_memslots_dirty_logging can be changed concurrent with this
+ * code, but in that case another update request will be made and so
+ * the guest will never run with a stale PML value.
+ */
+ bool enable = atomic_read(&vcpu->kvm->nr_memslots_dirty_logging);
+
+ if (WARN_ON_ONCE(!vcpu->kvm->arch.cpu_dirty_log_size))
+ return;
+
+ kvm_x86_call(update_cpu_dirty_logging)(vcpu, enable);
+}
+
/*
* Called within kvm->srcu read side.
* Returns 1 to let vcpu_run() continue the guest execution loop without
@@ -8208,7 +8254,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
kvm_x86_call(recalc_intercepts)(vcpu);
if (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu))
- kvm_x86_call(update_cpu_dirty_logging)(vcpu);
+ kvm_update_cpu_dirty_logging(vcpu);
if (kvm_check_request(KVM_REQ_UPDATE_PROTECTED_GUEST_STATE, vcpu)) {
kvm_vcpu_reset(vcpu, true);
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 0f5919b092e47..2d747c20965ce 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -900,4 +900,11 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl,
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
+/* Support for PML */
+#define PML_LOG_NR_ENTRIES 512
+/* PML is written backwards: this is the first entry written by the CPU */
+#define PML_HEAD_INDEX (PML_LOG_NR_ENTRIES-1)
+
+void kvm_flush_pml_buffer(struct kvm_vcpu *vcpu, u16 pml_idx);
+
#endif
|
| 3/2 | 2026/09/07 09:10 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 14402 output: 31 thoughts: 648 Thoughts: **Analyzing the KVM Patch for Fuzzing Potential** Okay, let's break this down. My initial assessment is that this patch is definitely worth a closer look from a fuzzing perspective. It looks like it significantly touches the KVM x86 subsystem – and we're talking both generic x86, as well as AMD's SVM and Intel's VMX implementations. Specifically, it seems to add support for PML (Page Modification Logging) on AMD SVM, including nested virtualization and SEV (Secure Encrypted Virtualization) considerations. The way it refactors PML dirty logging between Intel VMX and AMD SVM also piques my interest. We've got changes moving `pml_page` around, generalizing functions, adding new callbacks like `svm_update_cpu_dirty_logging`, and some significant refactoring around how the logging is handled in both VMX and SVM. The modifications in `nested.c` for SVM nested virtualization, especially handling `PML_FULL` exits and the initialization of `vmcb02->control.pml_index`, are definitely worth examining. I'm also seeing new `WARN_ON_ONCE` checks being added, which is always interesting from a fuzzing viewpoint. Given that KVM is a central virtualization component and that syzkaller already heavily fuzzes it on x86, this is a prime target. We're talking core execution paths being touched: `vcpu_enter_guest`, `svm_handle_exit`, `svm_vcpu_create`, and a lot of the logging functions. This kind of interaction with MMU, dirty logging, and nested virtualization is, from a fuzzing perspective, a goldmine. **Conclusion:** This is a definite "YES" in terms of fuzzing potential (WorthFuzzing=true). Now, let's see about those Kconfig dependencies. I'll use grepper to grab the relevant configurations for KVM, KVM_AMD, and KVM_INTEL and then start crafting some fuzzing strategies. |
| 4/2 | 2026/09/07 09:11 | 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"
} |
| 5/2 | 2026/09/07 09:11 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 16726 output: 36 thoughts: 25 Thoughts: Here's my summary, as if I were thinking these thoughts: **Examining KVM Compilation for x86** Okay, so I'm digging into how these KVM components are compiled, specifically for x86. I've already been looking at some of the relevant build configurations, but I need to make sure I haven't missed anything. I'm going to take a closer look at the `arch/x86/kvm/Makefile`. That's where I'd expect to find the specifics regarding which source files get compiled, and how, within that architecture-specific context. I need to understand this process thoroughly to troubleshoot the problem. |
| 6/2 | 2026/09/07 09:11 | 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"
} |
| 7/2 | 2026/09/07 09:11 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 17813 output: 37 thoughts: 114 Thoughts: **Analyzing KVM and SEV Integration** Okay, let's break this down. My initial focus is on the kernel configuration options, specifically `KVM_AMD` and `KVM_INTEL`. These, of course, are built upon the foundational `KVM_X86` and `KVM` modules. I need to understand how the SEV (Secure Encrypted Virtualization) integration fits into this picture. I see that `KVM_AMD_SEV` is mentioned, and the `sev.c` source file is flagged as relevant. Specifically, a comment change and the function `snp_safe_alloc_page_node` have piqued my interest. That function is used within `svm_vcpu_create` through `snp_safe_alloc_page()`. This means there might be some crucial allocation logic related to secure memory in the AMD KVM environment. Therefore, the next logical step is to dive into the implementation of `snp_safe_alloc_page` itself. I need to examine its definition within both `sev.c` and `svm.h` to understand the low-level memory allocation handling and security considerations. This should illuminate the SEV-specific mechanisms being employed within the KVM/AMD stack. |
| 8/2 | 2026/09/07 09:11 | tool | grepper |
0mArgs:{
"Expression": "snp_safe_alloc_page",
"PathPrefix": "arch/x86/kvm/svm"
}Results: {
"Output": "arch/x86/kvm/svm/nested.c=1487=int svm_allocate_nested(struct vcpu_svm *svm)\n--\narch/x86/kvm/svm/nested.c-1493-\narch/x86/kvm/svm/nested.c:1494:\tvmcb02_page = snp_safe_alloc_page();\narch/x86/kvm/svm/nested.c-1495-\tif (!vmcb02_page)\n--\narch/x86/kvm/svm/sev.c=4862=int sev_vcpu_create(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/sev.c-4875-\t */\narch/x86/kvm/svm/sev.c:4876:\tvmsa_page = snp_safe_alloc_page();\narch/x86/kvm/svm/sev.c-4877-\tif (!vmsa_page)\n--\narch/x86/kvm/svm/sev.c=4948=void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)\n--\narch/x86/kvm/svm/sev.c-4984-\narch/x86/kvm/svm/sev.c:4985:struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)\narch/x86/kvm/svm/sev.c-4986-{\n--\narch/x86/kvm/svm/svm.c=619=static int svm_cpu_init(int cpu)\n--\narch/x86/kvm/svm/svm.c-625-\tmemset(sd, 0, sizeof(struct svm_cpu_data));\narch/x86/kvm/svm/svm.c:626:\tsave_area_page = snp_safe_alloc_page_node(cpu_to_node(cpu), GFP_KERNEL);\narch/x86/kvm/svm/svm.c-627-\tif (!save_area_page)\n--\narch/x86/kvm/svm/svm.c=1335=static int svm_vcpu_create(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/svm.c-1344-\terr = -ENOMEM;\narch/x86/kvm/svm/svm.c:1345:\tvmcb01_page = snp_safe_alloc_page();\narch/x86/kvm/svm/svm.c-1346-\tif (!vmcb01_page)\n--\narch/x86/kvm/svm/svm.c-1349-\tif (pml) {\narch/x86/kvm/svm/svm.c:1350:\t\tvcpu-\u003earch.pml_page = snp_safe_alloc_page();\narch/x86/kvm/svm/svm.c-1351-\t\tif (!vcpu-\u003earch.pml_page)\n--\narch/x86/kvm/svm/svm.c=5414=static void *svm_alloc_apic_backing_page(struct kvm_vcpu *vcpu)\narch/x86/kvm/svm/svm.c-5415-{\narch/x86/kvm/svm/svm.c:5416:\tstruct page *page = snp_safe_alloc_page();\narch/x86/kvm/svm/svm.c-5417-\n--\narch/x86/kvm/svm/svm.h=993=int sev_handle_vmgexit(struct kvm_vcpu *vcpu);\n--\narch/x86/kvm/svm/svm.h-996-\narch/x86/kvm/svm/svm.h:997:struct page *snp_safe_alloc_page_node(int node, gfp_t gfp);\narch/x86/kvm/svm/svm.h:998:static inline struct page *snp_safe_alloc_page(void)\narch/x86/kvm/svm/svm.h-999-{\narch/x86/kvm/svm/svm.h:1000:\treturn snp_safe_alloc_page_node(numa_node_id(), GFP_KERNEL_ACCOUNT);\narch/x86/kvm/svm/svm.h-1001-}\n--\narch/x86/kvm/svm/svm.h=1020=void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa);\narch/x86/kvm/svm/svm.h-1021-#else\narch/x86/kvm/svm/svm.h:1022:static inline struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)\narch/x86/kvm/svm/svm.h-1023-{\n--\narch/x86/kvm/svm/svm.h-1026-\narch/x86/kvm/svm/svm.h:1027:static inline struct page *snp_safe_alloc_page(void)\narch/x86/kvm/svm/svm.h-1028-{\narch/x86/kvm/svm/svm.h:1029:\treturn snp_safe_alloc_page_node(numa_node_id(), GFP_KERNEL_ACCOUNT);\narch/x86/kvm/svm/svm.h-1030-}\n"
} |
| 9/2 | 2026/09/07 09:11 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 19687 output: 35 thoughts: 1124 Thoughts: **Focus Symbol Selection for KVM Changes** Okay, let's get down to brass tacks. I need to identify the most relevant focus symbols, given these recent modifications and additions to the KVM code. My primary goal is to pick non-ubiquitous, highly targeted symbols for analysis. First, I've got to review the modified and newly added functions across the relevant files: `arch/x86/kvm/x86.c`, `arch/x86/kvm/svm/svm.c`, `arch/x86/kvm/svm/nested.c`, and `arch/x86/kvm/vmx/vmx.c` / `main.c`. I've gone through the lists and have a short list of functions: * `kvm_flush_pml_buffer` * `kvm_update_cpu_dirty_logging` * `svm_update_cpu_dirty_logging` * `svm_flush_pml_buffer` * `pml_full_interception` * `nested_vmcb02_prepare_control` * `nested_svm_exit_special` * `vmx_update_cpu_dirty_logging` * `vmx_flush_pml_buffer` Next, I need to verify that these functions are not ubiquitous kernel hotspots. They appear to be specific to KVM, PML, dirty logging, and nested SVM. No obvious issues here. Now, I'll need to double check the configuration options. I'm going to quickly check for the presence of the following configs: * `KVM` * `KVM_AMD` * `KVM_INTEL` * `KVM_X86` * `VIRTUALIZATION` Looks like everything is under `arch/x86/kvm/Kconfig` or `arch/x86/Kconfig` and `arch/Kconfig`. With the symbols identified and validated, I can now proceed to analyze the impact of these changes. |
| 10/2 | 2026/09/07 09:11 | tool | grepper |
0mArgs:{
"Expression": "config (KVM|VIRTUALIZATION)",
"PathPrefix": "arch/x86"
}Results: {
"Output": "arch/x86/Kconfig=830=source \"arch/x86/xen/Kconfig\"\narch/x86/Kconfig-831-\narch/x86/Kconfig:832:config KVM_GUEST\narch/x86/Kconfig-833-\tbool \"KVM Guest support (including kvmclock)\"\n--\narch/x86/kvm/Kconfig=6=source \"virt/kvm/Kconfig\"\narch/x86/kvm/Kconfig-7-\narch/x86/kvm/Kconfig:8:menuconfig VIRTUALIZATION\narch/x86/kvm/Kconfig-9-\tbool \"Virtualization\"\n--\narch/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"
} |
| 11/2 | 2026/09/07 09:11 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 22702 output: 218 thoughts: 230 Thoughts: **My Assessment of This KVM Patch** Okay, here's the breakdown as I see it. This is straightforward; everything is crystal clear. We're definitely fuzzing this, which is good. The configuration flags are set to enable this functionality across KVM, and specifically for AMD and Intel hardware, which makes sense. I see the focus symbols listed; these are the crucial functions that are going to be impacted, or are at the heart of the changes. Based on the symbols, it looks like this patch is all about Page Modification Logging, or PML, and it's being implemented and refactored for AMD's Secure Virtual Machine (SVM) and the existing generic x86 KVM code, as well as Intel's VMX. The function names point to significant changes in how dirty logging is handled across various virtualization paths. We've got new exit handlers for handling specific virtual machine exits, including nested SVM exits, which is a key area for performance and security. There's also buffer flushing logic, and updates to the dirty logging state control in key virtualization routines. The main thing is that we're dealing with very low-level virtualization details and management of how memory pages are tracked, potentially to optimize how guest virtual machines are managed. I'm ready to mark this as `set-results`. |
| 12/2 | 2026/09/07 09:11 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"KVM",
"KVM_AMD",
"KVM_INTEL"
],
"FocusSymbols": [
"kvm_flush_pml_buffer",
"kvm_update_cpu_dirty_logging",
"svm_update_cpu_dirty_logging",
"svm_flush_pml_buffer",
"pml_full_interception",
"nested_svm_exit_special",
"nested_vmcb02_prepare_control",
"vmx_update_cpu_dirty_logging",
"vmx_flush_pml_buffer"
],
"Reasoning": "The patch implements Page Modification Logging (PML) dirty logging support for AMD SVM and refactors the shared PML buffer flushing and dirty logging state machine across x86 KVM, Intel VMX, and AMD SVM. These changes touch reachable core virtualization code paths in KVM and affect nested virtualization and exit handling.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"KVM",
"KVM_AMD",
"KVM_INTEL"
],
"FocusSymbols": [
"kvm_flush_pml_buffer",
"kvm_update_cpu_dirty_logging",
"svm_update_cpu_dirty_logging",
"svm_flush_pml_buffer",
"pml_full_interception",
"nested_svm_exit_special",
"nested_vmcb02_prepare_control",
"vmx_update_cpu_dirty_logging",
"vmx_flush_pml_buffer"
],
"Reasoning": "The patch implements Page Modification Logging (PML) dirty logging support for AMD SVM and refactors the shared PML buffer flushing and dirty logging state machine across x86 KVM, Intel VMX, and AMD SVM. These changes touch reachable core virtualization code paths in KVM and affect nested virtualization and exit handling.",
"WorthFuzzing": true
} |
| 13/1 | 2026/09/07 09:11 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 14/2 | 2026/09/07 09:11 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch series introduces AMD SVM support for Page Modification Logging (PML) and consolidates shared PML handling logic between Intel VMX and AMD SVM.\n\nAll allocated PML buffer pages are zero-initialized at creation time via `__GFP_ZERO` (in `alloc_page` and `snp_safe_alloc_page_node`). The changes do not introduce any kernel-to-user memory copy operations (e.g. `copy_to_user`, ioctl outputs), do not leave any stack or heap structures uninitialized, and do not introduce control-flow branching based on uninitialized memory. Therefore, there are no uninitialized memory or info-leak risks requiring a dedicated KMSAN fuzzing session; standard KASAN and functional testing are sufficient.",
"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 a8b5312779dd64dbc6311a236fd260a349381dd6
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Sep 7 09:10:51 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index f70ee74b5f921..cbfcc66a18e9b 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -238,6 +238,7 @@
// free: was #define X86_FEATURE_PVUNLOCK ( 8*32+20) /* PV unlock function */
#define X86_FEATURE_VCPUPREEMPT ( 8*32+21) /* PV vcpu_is_preempted function */
#define X86_FEATURE_TDX_GUEST ( 8*32+22) /* "tdx_guest" Intel Trust Domain Extensions Guest */
+#define X86_FEATURE_PML ( 8*32+23) /* AMD Page Modification logging */
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EBX), word 9 */
#define X86_FEATURE_FSGSBASE ( 9*32+ 0) /* "fsgsbase" RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE instructions*/
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 3af7395c2430a..2debf62942909 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -789,6 +789,8 @@ struct kvm_vcpu_arch {
*/
struct kvm_mmu_memory_cache mmu_external_spt_cache;
+ struct page *pml_page;
+
/*
* QEMU userspace and the guest each have their own FPU state.
* In vcpu_run, we switch between the user and guest FPU contexts.
@@ -1670,7 +1672,7 @@ struct kvm_x86_ops {
struct x86_exception *exception);
void (*handle_exit_irqoff)(struct kvm_vcpu *vcpu);
- void (*update_cpu_dirty_logging)(struct kvm_vcpu *vcpu);
+ void (*update_cpu_dirty_logging)(struct kvm_vcpu *vcpu, bool enable);
void (*vcpu_blocking)(struct kvm_vcpu *vcpu);
void (*vcpu_unblocking)(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index aa63431ba92c3..e0a7549a7c727 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -165,7 +165,10 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
u8 reserved_9[22];
u64 allowed_sev_features; /* Offset 0x138 */
u64 guest_sev_features; /* Offset 0x140 */
- u8 reserved_10[664];
+ u8 reserved_10[128];
+ u64 pml_addr; /* Offset 0x1c8 */
+ u16 pml_index; /* Offset 0x1d0 */
+ u8 reserved_11[526];
/*
* Offset 0x3e0, 32 bytes reserved
* for use by hypervisor/software.
@@ -240,10 +243,11 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
#define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT)
#define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT)
-#define SVM_MISC_ENABLE_NP BIT(0)
-#define SVM_MISC_ENABLE_SEV BIT(1)
-#define SVM_MISC_ENABLE_SEV_ES BIT(2)
-#define SVM_MISC_ENABLE_GMET BIT(3)
+#define SVM_MISC_ENABLE_NP BIT_ULL(0)
+#define SVM_MISC_ENABLE_SEV BIT_ULL(1)
+#define SVM_MISC_ENABLE_SEV_ES BIT_ULL(2)
+#define SVM_MISC_ENABLE_GMET BIT_ULL(3)
+#define SVM_MISC_ENABLE_PML BIT_ULL(11)
#define SVM_MISC2_ENABLE_V_LBR BIT_ULL(0)
#define SVM_MISC2_ENABLE_V_VMLOAD_VMSAVE BIT_ULL(1)
diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index 010a45c9f6147..e806761850921 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -101,6 +101,7 @@
#define SVM_EXIT_AVIC_INCOMPLETE_IPI 0x401
#define SVM_EXIT_AVIC_UNACCELERATED_ACCESS 0x402
#define SVM_EXIT_VMGEXIT 0x403
+#define SVM_EXIT_PML_FULL 0x407
/* SEV-ES software-defined VMGEXIT events */
#define SVM_VMGEXIT_MMIO_READ 0x80000001ull
@@ -236,6 +237,7 @@
{ SVM_EXIT_AVIC_INCOMPLETE_IPI, "avic_incomplete_ipi" }, \
{ SVM_EXIT_AVIC_UNACCELERATED_ACCESS, "avic_unaccelerated_access" }, \
{ SVM_EXIT_VMGEXIT, "vmgexit" }, \
+ { SVM_EXIT_PML_FULL, "pml_full" }, \
{ SVM_VMGEXIT_MMIO_READ, "vmgexit_mmio_read" }, \
{ SVM_VMGEXIT_MMIO_WRITE, "vmgexit_mmio_write" }, \
{ SVM_VMGEXIT_NMI_COMPLETE, "vmgexit_nmi_complete" }, \
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index 8665a6474806c..b18cc9c88dd57 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -55,6 +55,7 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_AMD_FAST_CPPC, CPUID_EDX, 15, 0x80000007, 0 },
{ X86_FEATURE_CPPC_PERF_PRIO, CPUID_EDX, 16, 0x80000007, 0 },
{ X86_FEATURE_MBA, CPUID_EBX, 6, 0x80000008, 0 },
+ { X86_FEATURE_PML, CPUID_ECX, 4, 0x8000000a, 0 },
{ X86_FEATURE_X2AVIC_EXT, CPUID_ECX, 6, 0x8000000a, 0 },
{ X86_FEATURE_COHERENCY_SFW_NO, CPUID_EBX, 31, 0x8000001f, 0 },
{ X86_FEATURE_SMBA, CPUID_EBX, 2, 0x80000020, 0 },
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 73f37b050d0a0..da417c4c59154 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -901,6 +901,13 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
vmcb02->control.msrpm_base_pa = vmcb01->control.msrpm_base_pa;
vmcb_mark_dirty(vmcb02, VMCB_PERM_MAP);
+ /*
+ * PML is never enabled in hardware for L2. Make sure that an
+ * unexpected PML write would trigger a PML_FULL VM-Exit.
+ */
+ if (pml)
+ vmcb02->control.pml_index = -1;
+
/*
* Stash vmcb02's counter if the guest hasn't moved past the guilty
* instruction; otherwise, reset the counter to '0'.
@@ -1820,6 +1827,13 @@ int nested_svm_exit_special(struct vcpu_svm *svm)
if (nested_svm_is_l2_tlb_flush_hcall(vcpu))
return NESTED_EXIT_HOST;
break;
+ case SVM_EXIT_PML_FULL:
+ /*
+ * All PML full exits are handled by KVM. KVM emulates PML in
+ * software for L1, but never enables PML in hardware on behalf
+ * of L1.
+ */
+ return NESTED_EXIT_HOST;
default:
break;
}
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 5705723f1f412..cfe990f805040 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4994,7 +4994,7 @@ struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)
* Allocate an SNP-safe page to workaround the SNP erratum where
* the CPU will incorrectly signal an RMP violation #PF if a
* hugepage (2MB or 1GB) collides with the RMP entry of a
- * 2MB-aligned VMCB, VMSA, or AVIC backing page.
+ * 2MB-aligned VMCB, VMSA, PML or AVIC backing page.
*
* Allocate one extra page, choose a page which is not
* 2MB-aligned, and free the other.
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7d59d301e1e54..950c250a77b51 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -179,6 +179,9 @@ module_param(vnmi, bool, 0444);
module_param(enable_mediated_pmu, bool, 0444);
+bool __ro_after_init pml = true;
+module_param(pml, bool, 0444);
+
static bool __ro_after_init svm_gp_erratum_intercept = true;
static u8 rsm_ins_bytes[] = "\x0f\xaa";
@@ -1267,6 +1270,23 @@ static void init_vmcb(struct kvm_vcpu *vcpu, bool init_event)
if (vcpu->kvm->arch.bus_lock_detection_enabled)
svm_set_intercept(svm, INTERCEPT_BUSLOCK);
+ if (pml) {
+ /*
+ * Populate the page address and index here, PML is enabled
+ * when dirty logging is enabled on the memslot through
+ * svm_update_cpu_dirty_logging()
+ */
+ control->pml_addr = (u64)__sme_set(page_to_phys(vcpu->arch.pml_page));
+
+ /*
+ * The VMCB control area remains valid after an intercepted SHUTDOWN
+ * (AMD APM Rev 3.45+), so only initialize PML index on reset to avoid
+ * discarding already-logged entries that haven't been flushed.
+ */
+ if (!init_event)
+ control->pml_index = PML_HEAD_INDEX;
+ }
+
if (is_sev_guest(vcpu))
sev_init_vmcb(svm, init_event);
@@ -1326,9 +1346,15 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
if (!vmcb01_page)
goto out;
+ if (pml) {
+ vcpu->arch.pml_page = snp_safe_alloc_page();
+ if (!vcpu->arch.pml_page)
+ goto error_free_vmcb_page;
+ }
+
err = sev_vcpu_create(vcpu);
if (err)
- goto error_free_vmcb_page;
+ goto error_free_pml_page;
err = avic_init_vcpu(svm);
if (err)
@@ -1353,6 +1379,9 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
error_free_sev:
sev_free_vcpu(vcpu);
+error_free_pml_page:
+ if (vcpu->arch.pml_page)
+ __free_page(vcpu->arch.pml_page);
error_free_vmcb_page:
__free_page(vmcb01_page);
out:
@@ -1370,6 +1399,9 @@ static void svm_vcpu_free(struct kvm_vcpu *vcpu)
sev_free_vcpu(vcpu);
+ if (pml && vcpu->arch.pml_page)
+ __free_page(vcpu->arch.pml_page);
+
__free_page(__sme_pa_to_page(svm->vmcb01.pa));
svm_vcpu_free_msrpm(svm->msrpm);
}
@@ -3344,6 +3376,45 @@ static int vmmcall_interception(struct kvm_vcpu *vcpu)
return kvm_emulate_hypercall(vcpu);
}
+static void svm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ struct vmcb *vmcb01 = svm->vmcb01.ptr;
+
+ if (enable)
+ vmcb01->control.misc_ctl |= SVM_MISC_ENABLE_PML;
+ else
+ vmcb01->control.misc_ctl &= ~SVM_MISC_ENABLE_PML;
+
+ vmcb_mark_dirty(vmcb01, VMCB_NPT);
+}
+
+static void svm_flush_pml_buffer(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_svm *svm = to_svm(vcpu);
+ struct vmcb_control_area *control = &svm->vmcb->control;
+
+ /* Do nothing if PML buffer is empty */
+ if (control->pml_index == PML_HEAD_INDEX)
+ return;
+
+ kvm_flush_pml_buffer(vcpu, control->pml_index);
+
+ /* Reset the PML index */
+ control->pml_index = PML_HEAD_INDEX;
+}
+
+static int pml_full_interception(struct kvm_vcpu *vcpu)
+{
+ trace_kvm_pml_full(vcpu->vcpu_id);
+
+ /*
+ * PML buffer is already flushed at the beginning of svm_handle_exit().
+ * Nothing to do here.
+ */
+ return 1;
+}
+
static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
[SVM_EXIT_READ_CR0] = cr_interception,
[SVM_EXIT_READ_CR3] = cr_interception,
@@ -3421,6 +3492,7 @@ static int (*const svm_exit_handlers[])(struct kvm_vcpu *vcpu) = {
#ifdef CONFIG_KVM_AMD_SEV
[SVM_EXIT_VMGEXIT] = sev_handle_vmgexit,
#endif
+ [SVM_EXIT_PML_FULL] = pml_full_interception,
};
static void dump_vmcb(struct kvm_vcpu *vcpu)
@@ -3470,8 +3542,14 @@ static void dump_vmcb(struct kvm_vcpu *vcpu)
pr_err("%-20s%016llx\n", "exit_info2:", control->exit_info_2);
pr_err("%-20s%08x\n", "exit_int_info:", control->exit_int_info);
pr_err("%-20s%08x\n", "exit_int_info_err:", control->exit_int_info_err);
- pr_err("%-20s%lld\n", "misc_ctl:", control->misc_ctl);
+ pr_err("%-20s%llx\n", "misc_ctl:", control->misc_ctl);
pr_err("%-20s%016llx\n", "nested_cr3:", control->nested_cr3);
+
+ if (pml) {
+ pr_err("%-20s%016llx\n", "pml_addr:", control->pml_addr);
+ pr_err("%-20s%04x\n", "pml_index:", control->pml_index);
+ }
+
pr_err("%-20s%016llx\n", "avic_vapic_bar:", control->avic_vapic_bar);
pr_err("%-20s%016llx\n", "ghcb:", control->ghcb_gpa);
pr_err("%-20s%08x\n", "event_inj:", control->event_inj);
@@ -3649,6 +3727,13 @@ int svm_invoke_exit_handler(struct kvm_vcpu *vcpu, u64 __exit_code)
(u64)exit_code != __exit_code)
goto unexpected_vmexit;
+ /*
+ * PML is never enabled when running L2, bail immediately if a PML full
+ * exit occurs as something is horribly wrong.
+ */
+ if (unlikely(is_guest_mode(vcpu) && exit_code == SVM_EXIT_PML_FULL))
+ goto unexpected_vmexit;
+
#ifdef CONFIG_MITIGATION_RETPOLINE
if (exit_code == SVM_EXIT_MSR)
return msr_interception(vcpu);
@@ -3717,6 +3802,14 @@ static int svm_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath)
struct vcpu_svm *svm = to_svm(vcpu);
struct kvm_run *kvm_run = vcpu->run;
+ /*
+ * Opportunistically flush the PML buffer on VM exit. This keeps the
+ * dirty bitmap current by processing logged GPAs rather than waiting for
+ * PML_FULL exit.
+ */
+ if (vcpu->kvm->arch.cpu_dirty_log_size && !is_guest_mode(vcpu))
+ svm_flush_pml_buffer(vcpu);
+
if (unlikely(exit_fastpath == EXIT_FASTPATH_EXIT_USERSPACE))
return 0;
@@ -5311,6 +5404,9 @@ static int svm_vm_init(struct kvm *kvm)
if (!pause_filter_count || !pause_filter_thresh)
kvm_disable_exits(kvm, KVM_X86_DISABLE_EXITS_PAUSE);
+ if (pml)
+ kvm->arch.cpu_dirty_log_size = PML_LOG_NR_ENTRIES;
+
svm_srso_vm_init();
return 0;
}
@@ -5429,6 +5525,8 @@ struct kvm_x86_ops svm_x86_ops __initdata = {
.check_intercept = svm_check_intercept,
.handle_exit_irqoff = svm_handle_exit_irqoff,
+ .update_cpu_dirty_logging = svm_update_cpu_dirty_logging,
+
.deliver_interrupt = svm_deliver_interrupt,
.pi_update_irte = avic_pi_update_irte,
.setup_mce = svm_setup_mce,
@@ -5689,6 +5787,10 @@ static __init int svm_hardware_setup(void)
nrips = nrips && boot_cpu_has(X86_FEATURE_NRIPS);
+ pml = pml && npt_enabled && cpu_feature_enabled(X86_FEATURE_PML);
+ if (pml)
+ pr_info("Page modification logging supported\n");
+
if (lbrv) {
if (!boot_cpu_has(X86_FEATURE_LBRV))
lbrv = false;
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index e958943b81627..f566dae44bb0f 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -53,6 +53,7 @@ extern int vgif;
extern bool intercept_smi;
extern bool vnmi;
extern int lbrv;
+extern bool pml;
extern int tsc_aux_uret_slot __ro_after_init;
diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c
index 4c52ab8d07869..e77b5b731275c 100644
--- a/arch/x86/kvm/vmx/main.c
+++ b/arch/x86/kvm/vmx/main.c
@@ -108,7 +108,7 @@ static void vt_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
vmx_vcpu_load(vcpu, cpu);
}
-static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)
{
/*
* Basic TDX does not support feature PML. KVM does not enable PML in
@@ -117,7 +117,7 @@ static void vt_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
if (WARN_ON_ONCE(is_td_vcpu(vcpu)))
return;
- vmx_update_cpu_dirty_logging(vcpu);
+ vmx_update_cpu_dirty_logging(vcpu, enable);
}
static void vt_prepare_switch_to_guest(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 612ab07d4100f..b3768b4b212fe 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -4883,10 +4883,11 @@ int vmx_vcpu_precreate(struct kvm *kvm)
#define VMX_XSS_EXIT_BITMAP 0
-static void init_vmcs(struct vcpu_vmx *vmx)
+static void init_vmcs(struct kvm_vcpu *vcpu)
{
- struct kvm *kvm = vmx->vcpu.kvm;
+ struct kvm *kvm = vcpu->kvm;
struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm);
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
if (nested)
nested_vmx_set_vmcs_shadowing_bitmap();
@@ -4911,7 +4912,7 @@ static void init_vmcs(struct vcpu_vmx *vmx)
if (cpu_has_tertiary_exec_ctrls())
tertiary_exec_controls_set(vmx, vmx_tertiary_exec_control(vmx));
- if (enable_apicv && lapic_in_kernel(&vmx->vcpu)) {
+ if (enable_apicv && lapic_in_kernel(vcpu)) {
vmcs_write64(EOI_EXIT_BITMAP0, 0);
vmcs_write64(EOI_EXIT_BITMAP1, 0);
vmcs_write64(EOI_EXIT_BITMAP2, 0);
@@ -4923,7 +4924,7 @@ static void init_vmcs(struct vcpu_vmx *vmx)
vmcs_write64(POSTED_INTR_DESC_ADDR, __pa((&vmx->vt.pi_desc)));
}
- if (vmx_can_use_ipiv(&vmx->vcpu)) {
+ if (vmx_can_use_ipiv(vcpu)) {
vmcs_write64(PID_POINTER_TABLE, __pa(kvm_vmx->pid_table));
vmcs_write16(LAST_PID_POINTER_INDEX, kvm->arch.max_vcpu_ids - 1);
}
@@ -4958,15 +4959,15 @@ static void init_vmcs(struct vcpu_vmx *vmx)
vmcs_write64(VM_ENTRY_MSR_LOAD_ADDR, __pa(vmx->msr_autoload.guest.val));
if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT)
- vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat);
+ vmcs_write64(GUEST_IA32_PAT, vcpu->arch.pat);
vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl());
/* 22.2.1, 20.8.1 */
vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl());
- vmx->vcpu.arch.cr0_guest_owned_bits = vmx_l1_guest_owned_cr0_bits();
- vmcs_writel(CR0_GUEST_HOST_MASK, ~vmx->vcpu.arch.cr0_guest_owned_bits);
+ vcpu->arch.cr0_guest_owned_bits = vmx_l1_guest_owned_cr0_bits();
+ vmcs_writel(CR0_GUEST_HOST_MASK, ~vcpu->arch.cr0_guest_owned_bits);
set_cr4_guest_host_mask(vmx);
@@ -4977,11 +4978,11 @@ static void init_vmcs(struct vcpu_vmx *vmx)
vmcs_write64(XSS_EXIT_BITMAP, VMX_XSS_EXIT_BITMAP);
if (enable_pml) {
- vmcs_write64(PML_ADDRESS, page_to_phys(vmx->pml_pg));
+ vmcs_write64(PML_ADDRESS, page_to_phys(vcpu->arch.pml_page));
vmcs_write16(GUEST_PML_INDEX, PML_HEAD_INDEX);
}
- vmx_write_encls_bitmap(&vmx->vcpu, NULL);
+ vmx_write_encls_bitmap(vcpu, NULL);
if (vmx_pt_mode_is_host_guest()) {
memset(&vmx->pt_desc, 0, sizeof(vmx->pt_desc));
@@ -4994,13 +4995,13 @@ static void init_vmcs(struct vcpu_vmx *vmx)
vmcs_writel(GUEST_SYSENTER_ESP, 0);
vmcs_writel(GUEST_SYSENTER_EIP, 0);
- vmx_guest_debugctl_write(&vmx->vcpu, 0);
+ vmx_guest_debugctl_write(vcpu, 0);
if (cpu_has_vmx_tpr_shadow()) {
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR, 0);
- if (cpu_need_tpr_shadow(&vmx->vcpu))
+ if (cpu_need_tpr_shadow(vcpu))
vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
- __pa(vmx->vcpu.arch.apic->regs));
+ __pa(vcpu->arch.apic->regs));
vmcs_write32(TPR_THRESHOLD, 0);
}
@@ -5011,7 +5012,7 @@ static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- init_vmcs(vmx);
+ init_vmcs(vcpu);
if (nested &&
kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_STUFF_FEATURE_MSRS))
@@ -6418,48 +6419,25 @@ void vmx_get_entry_info(struct kvm_vcpu *vcpu, u32 *intr_info, u32 *error_code)
*error_code = 0;
}
-static void vmx_destroy_pml_buffer(struct vcpu_vmx *vmx)
+static void vmx_destroy_pml_buffer(struct kvm_vcpu *vcpu)
{
- if (vmx->pml_pg) {
- __free_page(vmx->pml_pg);
- vmx->pml_pg = NULL;
+ if (vcpu->arch.pml_page) {
+ __free_page(vcpu->arch.pml_page);
+ vcpu->arch.pml_page = NULL;
}
}
static void vmx_flush_pml_buffer(struct kvm_vcpu *vcpu)
{
- struct vcpu_vmx *vmx = to_vmx(vcpu);
- u16 pml_idx, pml_tail_index;
- u64 *pml_buf;
- int i;
+ u16 pml_idx;
pml_idx = vmcs_read16(GUEST_PML_INDEX);
/* Do nothing if PML buffer is empty */
if (pml_idx == PML_HEAD_INDEX)
return;
- /*
- * PML index always points to the next available PML buffer entity
- * unless PML log has just overflowed.
- */
- pml_tail_index = (pml_idx >= PML_LOG_NR_ENTRIES) ? 0 : pml_idx + 1;
-
- /*
- * PML log is written backwards: the CPU first writes the entry 511
- * then the entry 510, and so on.
- *
- * Read the entries in the same order they were written, to ensure that
- * the dirty ring is filled in the same order the CPU wrote them.
- */
- pml_buf = page_address(vmx->pml_pg);
- for (i = PML_HEAD_INDEX; i >= pml_tail_index; i--) {
- u64 gpa;
-
- gpa = pml_buf[i];
- WARN_ON(gpa & (PAGE_SIZE - 1));
- kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
- }
+ kvm_flush_pml_buffer(vcpu, pml_idx);
/* reset PML index */
vmcs_write16(GUEST_PML_INDEX, PML_HEAD_INDEX);
@@ -7648,7 +7626,7 @@ void vmx_vcpu_free(struct kvm_vcpu *vcpu)
struct vcpu_vmx *vmx = to_vmx(vcpu);
if (enable_pml)
- vmx_destroy_pml_buffer(vmx);
+ vmx_destroy_pml_buffer(vcpu);
free_vpid(vmx->vpid);
nested_vmx_free_vcpu(vcpu);
free_loaded_vmcs(vmx->loaded_vmcs);
@@ -7680,8 +7658,8 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu)
* for the guest), etc.
*/
if (enable_pml) {
- vmx->pml_pg = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
- if (!vmx->pml_pg)
+ vcpu->arch.pml_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+ if (!vcpu->arch.pml_page)
goto free_vpid;
}
@@ -7752,7 +7730,7 @@ int vmx_vcpu_create(struct kvm_vcpu *vcpu)
free_vmcs:
free_loaded_vmcs(vmx->loaded_vmcs);
free_pml:
- vmx_destroy_pml_buffer(vmx);
+ vmx_destroy_pml_buffer(vcpu);
free_vpid:
free_vpid(vmx->vpid);
return err;
@@ -8440,21 +8418,13 @@ static void vmx_update_hv_timer(struct kvm_vcpu *vcpu, bool force_immediate_exit
}
#endif
-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- if (WARN_ON_ONCE(!enable_pml))
- return;
-
guard(vmx_vmcs01)(vcpu);
- /*
- * Note, nr_memslots_dirty_logging can be changed concurrent with this
- * code, but in that case another update request will be made and so
- * the guest will never run with a stale PML value.
- */
- if (atomic_read(&vcpu->kvm->nr_memslots_dirty_logging))
+ if (enable)
secondary_exec_controls_setbit(vmx, SECONDARY_EXEC_ENABLE_PML);
else
secondary_exec_controls_clearbit(vmx, SECONDARY_EXEC_ENABLE_PML);
diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h
index dc8517f15bc46..5f505ad14dde1 100644
--- a/arch/x86/kvm/vmx/vmx.h
+++ b/arch/x86/kvm/vmx/vmx.h
@@ -265,13 +265,6 @@ struct vcpu_vmx {
unsigned int ple_window;
bool ple_window_dirty;
- /* Support for PML */
-#define PML_LOG_NR_ENTRIES 512
- /* PML is written backwards: this is the first entry written by the CPU */
-#define PML_HEAD_INDEX (PML_LOG_NR_ENTRIES-1)
-
- struct page *pml_pg;
-
/* apic deadline value in host tsc */
u64 hv_deadline_tsc;
@@ -397,7 +390,7 @@ u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu);
gva_t vmx_get_untagged_addr(struct kvm_vcpu *vcpu, gva_t gva, unsigned int flags);
-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu);
+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable);
u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated);
bool vmx_is_valid_debugctl(struct kvm_vcpu *vcpu, u64 data, bool host_initiated);
diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h
index 054fd14bb2e16..4810f9b1b0fc6 100644
--- a/arch/x86/kvm/vmx/x86_ops.h
+++ b/arch/x86/kvm/vmx/x86_ops.h
@@ -118,7 +118,7 @@ u64 vmx_get_l2_tsc_offset(struct kvm_vcpu *vcpu);
u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu);
void vmx_write_tsc_offset(struct kvm_vcpu *vcpu);
void vmx_write_tsc_multiplier(struct kvm_vcpu *vcpu);
-void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu);
+void vmx_update_cpu_dirty_logging(struct kvm_vcpu *vcpu, bool enable);
#ifdef CONFIG_X86_64
int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,
bool *expired);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 79468ddfe4736..6d26de8b7c3cc 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3941,6 +3941,37 @@ void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
kvm_vcpu_kick(vcpu);
}
+void kvm_flush_pml_buffer(struct kvm_vcpu *vcpu, u16 pml_idx)
+{
+ u16 pml_tail_index;
+ u64 *pml_buf;
+ int i;
+
+ /*
+ * PML index always points to the next available PML buffer entity
+ * unless PML log has just overflowed.
+ */
+ pml_tail_index = (pml_idx >= PML_LOG_NR_ENTRIES) ? 0 : pml_idx + 1;
+
+ /*
+ * PML log is written backwards: the CPU first writes the entry 511
+ * then the entry 510, and so on.
+ *
+ * Read the entries in the same order they were written, to ensure that
+ * the dirty ring is filled in the same order the CPU wrote them.
+ */
+ pml_buf = page_address(vcpu->arch.pml_page);
+
+ for (i = PML_HEAD_INDEX; i >= pml_tail_index; i--) {
+ u64 gpa;
+
+ gpa = pml_buf[i];
+ WARN_ON_ONCE(gpa & (PAGE_SIZE - 1));
+ kvm_vcpu_mark_page_dirty(vcpu, gpa >> PAGE_SHIFT);
+ }
+}
+EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_flush_pml_buffer);
+
int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
struct kvm_enable_cap *cap)
{
@@ -8042,6 +8073,21 @@ static void kvm_vcpu_reload_apic_access_page(struct kvm_vcpu *vcpu)
kvm_x86_call(set_apic_access_page_addr)(vcpu);
}
+static void kvm_update_cpu_dirty_logging(struct kvm_vcpu *vcpu)
+{
+ /*
+ * Note, nr_memslots_dirty_logging can be changed concurrent with this
+ * code, but in that case another update request will be made and so
+ * the guest will never run with a stale PML value.
+ */
+ bool enable = atomic_read(&vcpu->kvm->nr_memslots_dirty_logging);
+
+ if (WARN_ON_ONCE(!vcpu->kvm->arch.cpu_dirty_log_size))
+ return;
+
+ kvm_x86_call(update_cpu_dirty_logging)(vcpu, enable);
+}
+
/*
* Called within kvm->srcu read side.
* Returns 1 to let vcpu_run() continue the guest execution loop without
@@ -8208,7 +8254,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
kvm_x86_call(recalc_intercepts)(vcpu);
if (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu))
- kvm_x86_call(update_cpu_dirty_logging)(vcpu);
+ kvm_update_cpu_dirty_logging(vcpu);
if (kvm_check_request(KVM_REQ_UPDATE_PROTECTED_GUEST_STATE, vcpu)) {
kvm_vcpu_reset(vcpu, true);
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 0f5919b092e47..2d747c20965ce 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -900,4 +900,11 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl,
int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
+/* Support for PML */
+#define PML_LOG_NR_ENTRIES 512
+/* PML is written backwards: this is the first entry written by the CPU */
+#define PML_HEAD_INDEX (PML_LOG_NR_ENTRIES-1)
+
+void kvm_flush_pml_buffer(struct kvm_vcpu *vcpu, u16 pml_idx);
+
#endif
|
| 15/3 | 2026/09/07 09:11 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 13614 output: 21 thoughts: 1199 Thoughts: **KMSAN Fuzzing Evaluation for AMD PML Support in KVM SVM** Okay, let's assess this patch series. My initial thought is to determine if adding KMSAN to the fuzzing setup is warranted, considering the potential for memory corruption. The patch's core is adding AMD PML support within KVM SVM and refactoring some PML code between VMX and the common x86 KVM. Let me break down the changes to get a clearer picture. It seems like the core of the implementation involves: * Adding the `X86_FEATURE_PML` flag. * Moving the `pml_page` pointer into the `kvm_vcpu_arch` structure. This is significant, as it now becomes per-VCPU state. I need to understand if this page is properly initialized, zeroed, or if it might contain stale data. That could be a source of problems. * Adding `pml_addr` and `pml_index` to the `vmcb_control_area`. These additions are crucial because they determine where the PML data will be stored and the current location within the buffer. The `pml_index` and associated offsets are key areas for potential errors, such as off-by-one errors or incorrect indexing. * The introduction of `SVM_EXIT_PML_FULL`. This indicates a specific exit from the guest. This exit handler and associated data need special attention. * The `module_param(pml, bool, 0444);` and subsequent usage suggests a runtime switch for enabling PML. Good, that simplifies testing strategies. * The `snp_safe_alloc_page()` call in `svm_vcpu_create` is important. This is where the page is being allocated. I need to check how it's initialized: Is it zeroed? If not, is there other initialization? * The `svm_flush_pml_buffer` function and its interactions with `kvm_flush_pml_buffer` are vital. This is the flushing mechanism. This code needs to be very carefully reviewed. * Handling of `SVM_EXIT_PML_FULL` exits. This is the heart of the PML functionality. Errors in this area might lead to guest crashes or information leaks. * The use of `cpu_dirty_log_size` and its initialization based on PML being enabled is a critical factor for detecting which code paths are going to be enabled. Now, let's focus on the allocation of `vcpu->arch.pml_page`. I will need to verify the initialization of this memory using grepper or by reading the corresponding file. The security implications hinge on whether this allocated page is correctly sanitized or if uninitialized data might be present. In particular, I must understand the behaviour with `snp_safe_alloc_page()`. |
| 16/3 | 2026/09/07 09:11 | tool | grepper |
0mArgs:{
"Expression": "snp_safe_alloc_page"
}Results: {
"Output": "arch/x86/kvm/svm/nested.c=1487=int svm_allocate_nested(struct vcpu_svm *svm)\n--\narch/x86/kvm/svm/nested.c-1493-\narch/x86/kvm/svm/nested.c:1494:\tvmcb02_page = snp_safe_alloc_page();\narch/x86/kvm/svm/nested.c-1495-\tif (!vmcb02_page)\n--\narch/x86/kvm/svm/sev.c=4862=int sev_vcpu_create(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/sev.c-4875-\t */\narch/x86/kvm/svm/sev.c:4876:\tvmsa_page = snp_safe_alloc_page();\narch/x86/kvm/svm/sev.c-4877-\tif (!vmsa_page)\n--\narch/x86/kvm/svm/sev.c=4948=void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)\n--\narch/x86/kvm/svm/sev.c-4984-\narch/x86/kvm/svm/sev.c:4985:struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)\narch/x86/kvm/svm/sev.c-4986-{\n--\narch/x86/kvm/svm/svm.c=619=static int svm_cpu_init(int cpu)\n--\narch/x86/kvm/svm/svm.c-625-\tmemset(sd, 0, sizeof(struct svm_cpu_data));\narch/x86/kvm/svm/svm.c:626:\tsave_area_page = snp_safe_alloc_page_node(cpu_to_node(cpu), GFP_KERNEL);\narch/x86/kvm/svm/svm.c-627-\tif (!save_area_page)\n--\narch/x86/kvm/svm/svm.c=1335=static int svm_vcpu_create(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/svm.c-1344-\terr = -ENOMEM;\narch/x86/kvm/svm/svm.c:1345:\tvmcb01_page = snp_safe_alloc_page();\narch/x86/kvm/svm/svm.c-1346-\tif (!vmcb01_page)\n--\narch/x86/kvm/svm/svm.c-1349-\tif (pml) {\narch/x86/kvm/svm/svm.c:1350:\t\tvcpu-\u003earch.pml_page = snp_safe_alloc_page();\narch/x86/kvm/svm/svm.c-1351-\t\tif (!vcpu-\u003earch.pml_page)\n--\narch/x86/kvm/svm/svm.c=5414=static void *svm_alloc_apic_backing_page(struct kvm_vcpu *vcpu)\narch/x86/kvm/svm/svm.c-5415-{\narch/x86/kvm/svm/svm.c:5416:\tstruct page *page = snp_safe_alloc_page();\narch/x86/kvm/svm/svm.c-5417-\n--\narch/x86/kvm/svm/svm.h=993=int sev_handle_vmgexit(struct kvm_vcpu *vcpu);\n--\narch/x86/kvm/svm/svm.h-996-\narch/x86/kvm/svm/svm.h:997:struct page *snp_safe_alloc_page_node(int node, gfp_t gfp);\narch/x86/kvm/svm/svm.h:998:static inline struct page *snp_safe_alloc_page(void)\narch/x86/kvm/svm/svm.h-999-{\narch/x86/kvm/svm/svm.h:1000:\treturn snp_safe_alloc_page_node(numa_node_id(), GFP_KERNEL_ACCOUNT);\narch/x86/kvm/svm/svm.h-1001-}\n--\narch/x86/kvm/svm/svm.h=1020=void sev_free_decrypted_vmsa(struct kvm_vcpu *vcpu, struct vmcb_save_area *vmsa);\narch/x86/kvm/svm/svm.h-1021-#else\narch/x86/kvm/svm/svm.h:1022:static inline struct page *snp_safe_alloc_page_node(int node, gfp_t gfp)\narch/x86/kvm/svm/svm.h-1023-{\n--\narch/x86/kvm/svm/svm.h-1026-\narch/x86/kvm/svm/svm.h:1027:static inline struct page *snp_safe_alloc_page(void)\narch/x86/kvm/svm/svm.h-1028-{\narch/x86/kvm/svm/svm.h:1029:\treturn snp_safe_alloc_page_node(numa_node_id(), GFP_KERNEL_ACCOUNT);\narch/x86/kvm/svm/svm.h-1030-}\n"
} |
| 17/3 | 2026/09/07 09:11 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 16903 output: 43 thoughts: 32 Thoughts: Here's my analysis of the provided thought process, tailored for an expert audience: **Analyzing `snp_safe_alloc_page_node` in `arch/x86/kvm/svm/sev.c`** Okay, I'm diving into `snp_safe_alloc_page_node` within `arch/x86/kvm/svm/sev.c`. My immediate goal is to understand how this function is handling memory allocation, specifically within the context of AMD's Secure Encrypted Virtualization (SEV) environment, as this is how KVM handles the protected VMs. I'll need to pay close attention to the node allocation, as SEV deals with memory encryption and integrity protection and this can greatly impact how we handle page allocation. I'm expecting to see specific considerations for: * **Page Type:** Is it allocating regular pages, or are there special flags related to SEV, such as memory encryption enablement. * **Memory Node:** How is it dealing with NUMA nodes? SEV can introduce complexities here since page placement matters for performance. I need to figure out the node allocation, any considerations regarding the memory policy and the current NUMA configuration of the system. * **Safety:** The function name includes "safe," so I'll be looking for any checks or mitigations related to potential memory leaks, allocation failures, or other security vulnerabilities, which are critical in a SEV environment. This function must handle allocation failures gracefully as those can leak information about the VM. * **SEV Context:** I'll be looking for references to SEV-specific structures or functions that it may be using to manage encrypted memory. I'm especially interested in how it integrates with the AMD Secure Memory Encryption features. Essentially, I want to fully understand the flow of allocation within this critical section of the code, how it integrates with KVM's overall memory management strategy, and the particular nuances that SEV brings to the table. |
| 18/3 | 2026/09/07 09:11 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/svm/sev.c",
"FirstLine": 4985,
"LineCount": 40
}Results: {
"Contents": "4985:\tstruct page *snp_safe_alloc_page_node(int node, gfp_t gfp)\n4986:\t{\n4987:\t\tunsigned long pfn;\n4988:\t\tstruct page *p;\n4989:\t\n4990:\t\tif (!cc_platform_has(CC_ATTR_HOST_SEV_SNP))\n4991:\t\t\treturn alloc_pages_node(node, gfp | __GFP_ZERO, 0);\n4992:\t\n4993:\t\t/*\n4994:\t\t * Allocate an SNP-safe page to workaround the SNP erratum where\n4995:\t\t * the CPU will incorrectly signal an RMP violation #PF if a\n4996:\t\t * hugepage (2MB or 1GB) collides with the RMP entry of a\n4997:\t\t * 2MB-aligned VMCB, VMSA, PML or AVIC backing page.\n4998:\t\t *\n4999:\t\t * Allocate one extra page, choose a page which is not\n5000:\t\t * 2MB-aligned, and free the other.\n5001:\t\t */\n5002:\t\tp = alloc_pages_node(node, gfp | __GFP_ZERO, 1);\n5003:\t\tif (!p)\n5004:\t\t\treturn NULL;\n5005:\t\n5006:\t\tsplit_page(p, 1);\n5007:\t\n5008:\t\tpfn = page_to_pfn(p);\n5009:\t\tif (IS_ALIGNED(pfn, PTRS_PER_PMD))\n5010:\t\t\t__free_page(p++);\n5011:\t\telse\n5012:\t\t\t__free_page(p + 1);\n5013:\t\n5014:\t\treturn p;\n5015:\t}\n5016:\t\n5017:\tvoid sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)\n5018:\t{\n5019:\t\tstruct kvm_memory_slot *slot;\n5020:\t\tstruct kvm *kvm = vcpu-\u003ekvm;\n5021:\t\tint order, rmp_level, ret;\n5022:\t\tstruct page *page;\n5023:\t\tbool assigned;\n5024:\t\tkvm_pfn_t pfn;\n"
} |
| 19/3 | 2026/09/07 09:11 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 18031 output: 182 thoughts: 860 Thoughts: **KMSAN Assessment of AMD PML Support and Refactoring** Okay, let's break this down. My initial concern was centered around potential KMSAN issues stemming from the introduction of AMD PML support and the refactoring of the PML flush logic. I needed to ensure that we weren't introducing any new avenues for leaking uninitialized kernel memory to userspace or creating subtle race conditions. First, I looked at how the memory for these PML buffers is handled. The code explicitly uses `__GFP_ZERO` during allocation (`alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO)` in VMX and `snp_safe_alloc_page()` elsewhere). So, the PML page is *guaranteed* to be zeroed upon allocation. This immediately alleviates a significant portion of my worries. The hardware itself logs GPAs into this pre-zeroed page when dirty logging is active. Next, I went through a checklist of potential KMSAN triggers: 1. **Kernel structures to userspace with uninitialized fields?** Nope. No new ioctl structures, `copy_to_user` calls, Netlink messages, or socket options are introduced that might inadvertently expose uninitialized memory. 2. **Conditional logic or branching on uninitialized variables?** I see no evidence of this. The logic flow appears to be sound, with no decision-making based on potentially garbage values. 3. **Complex data structures allocation/initialization with later uninitialized field access?** Again, no. The allocated page is fully initialized. 4. **Bounds checks/length issues leading to uninitialized byte access?** The code does not appear to have any such vulnerabilities. The `kvm_flush_pml_buffer` function, moved from `vmx_flush_pml_buffer`, iterates backward through the PML buffer, reading the logged GPAs. The important point is that this memory has been explicitly zeroed at allocation time. Given the zero-initialization of the PML buffers, the absence of kernel-to-user memory copies, no branching on uninitialized variables, and no new uninitialized heap or stack memory exposure, I'm confident that KMSAN is not necessary here. Standard sanitizers (KASAN, LOCKDEP) and KVM's own unit tests should be perfectly sufficient for catching any issues related to memory access bounds, page management, and synchronization concerns within the context of this patch. The patch's core function is simply the logging of valid GPAs into a zeroed memory area; KMSAN is not applicable. |
| 20/3 | 2026/09/07 09:11 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series introduces AMD SVM support for Page Modification Logging (PML) and consolidates shared PML handling logic between Intel VMX and AMD SVM.\n\nAll allocated PML buffer pages are zero-initialized at creation time via `__GFP_ZERO` (in `alloc_page` and `snp_safe_alloc_page_node`). The changes do not introduce any kernel-to-user memory copy operations (e.g. `copy_to_user`, ioctl outputs), do not leave any stack or heap structures uninitialized, and do not introduce control-flow branching based on uninitialized memory. Therefore, there are no uninitialized memory or info-leak risks requiring a dedicated KMSAN fuzzing session; standard KASAN and functional testing are sufficient.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series introduces AMD SVM support for Page Modification Logging (PML) and consolidates shared PML handling logic between Intel VMX and AMD SVM.\n\nAll allocated PML buffer pages are zero-initialized at creation time via `__GFP_ZERO` (in `alloc_page` and `snp_safe_alloc_page_node`). The changes do not introduce any kernel-to-user memory copy operations (e.g. `copy_to_user`, ioctl outputs), do not leave any stack or heap structures uninitialized, and do not introduce control-flow branching based on uninitialized memory. Therefore, there are no uninitialized memory or info-leak risks requiring a dedicated KMSAN fuzzing session; standard KASAN and functional testing are sufficient.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|