| From bippy-1.2.0 Mon Sep 17 00:00:00 2001 |
| From: Greg Kroah-Hartman <gregkh@kernel.org> |
| To: <linux-cve-announce@vger.kernel.org> |
| Reply-to: <cve@kernel.org>, <linux-kernel@vger.kernel.org> |
| Subject: CVE-2025-21839: KVM: x86: Load DR6 with guest value only before entering .vcpu_run() loop |
| |
| Description |
| =========== |
| |
| In the Linux kernel, the following vulnerability has been resolved: |
| |
| KVM: x86: Load DR6 with guest value only before entering .vcpu_run() loop |
| |
| Move the conditional loading of hardware DR6 with the guest's DR6 value |
| out of the core .vcpu_run() loop to fix a bug where KVM can load hardware |
| with a stale vcpu->arch.dr6. |
| |
| When the guest accesses a DR and host userspace isn't debugging the guest, |
| KVM disables DR interception and loads the guest's values into hardware on |
| VM-Enter and saves them on VM-Exit. This allows the guest to access DRs |
| at will, e.g. so that a sequence of DR accesses to configure a breakpoint |
| only generates one VM-Exit. |
| |
| For DR0-DR3, the logic/behavior is identical between VMX and SVM, and also |
| identical between KVM_DEBUGREG_BP_ENABLED (userspace debugging the guest) |
| and KVM_DEBUGREG_WONT_EXIT (guest using DRs), and so KVM handles loading |
| DR0-DR3 in common code, _outside_ of the core kvm_x86_ops.vcpu_run() loop. |
| |
| But for DR6, the guest's value doesn't need to be loaded into hardware for |
| KVM_DEBUGREG_BP_ENABLED, and SVM provides a dedicated VMCB field whereas |
| VMX requires software to manually load the guest value, and so loading the |
| guest's value into DR6 is handled by {svm,vmx}_vcpu_run(), i.e. is done |
| _inside_ the core run loop. |
| |
| Unfortunately, saving the guest values on VM-Exit is initiated by common |
| x86, again outside of the core run loop. If the guest modifies DR6 (in |
| hardware, when DR interception is disabled), and then the next VM-Exit is |
| a fastpath VM-Exit, KVM will reload hardware DR6 with vcpu->arch.dr6 and |
| clobber the guest's actual value. |
| |
| The bug shows up primarily with nested VMX because KVM handles the VMX |
| preemption timer in the fastpath, and the window between hardware DR6 |
| being modified (in guest context) and DR6 being read by guest software is |
| orders of magnitude larger in a nested setup. E.g. in non-nested, the |
| VMX preemption timer would need to fire precisely between #DB injection |
| and the #DB handler's read of DR6, whereas with a KVM-on-KVM setup, the |
| window where hardware DR6 is "dirty" extends all the way from L1 writing |
| DR6 to VMRESUME (in L1). |
| |
| L1's view: |
| ========== |
| <L1 disables DR interception> |
| CPU 0/KVM-7289 [023] d.... 2925.640961: kvm_entry: vcpu 0 |
| A: L1 Writes DR6 |
| CPU 0/KVM-7289 [023] d.... 2925.640963: <hack>: Set DRs, DR6 = 0xffff0ff1 |
| |
| B: CPU 0/KVM-7289 [023] d.... 2925.640967: kvm_exit: vcpu 0 reason EXTERNAL_INTERRUPT intr_info 0x800000ec |
| |
| D: L1 reads DR6, arch.dr6 = 0 |
| CPU 0/KVM-7289 [023] d.... 2925.640969: <hack>: Sync DRs, DR6 = 0xffff0ff0 |
| |
| CPU 0/KVM-7289 [023] d.... 2925.640976: kvm_entry: vcpu 0 |
| L2 reads DR6, L1 disables DR interception |
| CPU 0/KVM-7289 [023] d.... 2925.640980: kvm_exit: vcpu 0 reason DR_ACCESS info1 0x0000000000000216 |
| CPU 0/KVM-7289 [023] d.... 2925.640983: kvm_entry: vcpu 0 |
| |
| CPU 0/KVM-7289 [023] d.... 2925.640983: <hack>: Set DRs, DR6 = 0xffff0ff0 |
| |
| L2 detects failure |
| CPU 0/KVM-7289 [023] d.... 2925.640987: kvm_exit: vcpu 0 reason HLT |
| L1 reads DR6 (confirms failure) |
| CPU 0/KVM-7289 [023] d.... 2925.640990: <hack>: Sync DRs, DR6 = 0xffff0ff0 |
| |
| L0's view: |
| ========== |
| L2 reads DR6, arch.dr6 = 0 |
| CPU 23/KVM-5046 [001] d.... 3410.005610: kvm_exit: vcpu 23 reason DR_ACCESS info1 0x0000000000000216 |
| CPU 23/KVM-5046 [001] ..... 3410.005610: kvm_nested_vmexit: vcpu 23 reason DR_ACCESS info1 0x0000000000000216 |
| |
| L2 => L1 nested VM-Exit |
| CPU 23/KVM-5046 [001] ..... 3410.005610: kvm_nested_vmexit_inject: reason: DR_ACCESS ext_inf1: 0x0000000000000216 |
| |
| CPU 23/KVM-5046 [001] d.... 3410.005610: kvm_entry: vcpu 23 |
| CPU 23/KVM-5046 [001] d.... 3410.005611: kvm_exit: vcpu 23 reason VMREAD |
| CPU 23/KVM-5046 [001] d.... 3410.005611: kvm_entry: vcpu 23 |
| CPU 23/KVM-5046 [001] d.... 3410.005612: kvm_exit: vcpu 23 reason VMREAD |
| CPU 23/KVM-5046 [001] d.... 3410.005612: kvm_entry: vcpu 23 |
| |
| L1 writes DR7, L0 disables DR interception |
| CPU 23/KVM-5046 [001] d.... 3410.005612: kvm_exit: vcpu 23 reason DR_ACCESS info1 0x0000000000000007 |
| CPU 23/KVM-5046 [001] d.... 3410.005613: kvm_entry: vcpu 23 |
| |
| L0 writes DR6 = 0 (arch.dr6) |
| CPU 23/KVM-5046 [001] d.... 3410.005613: <hack>: Set DRs, DR6 = 0xffff0ff0 |
| |
| A: <L1 writes DR6 = 1, no interception, arch.dr6 is still '0'> |
| |
| B: CPU 23/KVM-5046 [001] d.... 3410.005614: kvm_exit: vcpu 23 reason PREEMPTION_TIMER |
| CPU 23/KVM-5046 [001] d.... 3410.005614: kvm_entry: vcpu 23 |
| |
| C: L0 writes DR6 = 0 (arch.dr6) |
| CPU 23/KVM-5046 [001] d.... 3410.005614: <hack>: Set DRs, DR6 = 0xffff0ff0 |
| |
| L1 => L2 nested VM-Enter |
| CPU 23/KVM-5046 [001] d.... 3410.005616: kvm_exit: vcpu 23 reason VMRESUME |
| |
| L0 reads DR6, arch.dr6 = 0 |
| |
| The Linux kernel CVE team has assigned CVE-2025-21839 to this issue. |
| |
| |
| Affected and fixed versions |
| =========================== |
| |
| Issue introduced in 5.7 with commit d67668e9dd76d98136048935723947156737932b and fixed in 5.15.182 with commit 9efb2b99b96c86664bbdbdd2cdb354ac9627eb20 |
| Issue introduced in 5.7 with commit d67668e9dd76d98136048935723947156737932b and fixed in 6.1.138 with commit 93eeb6df1605b3a24f38afdba7ab903ba6b64133 |
| Issue introduced in 5.7 with commit d67668e9dd76d98136048935723947156737932b and fixed in 6.6.90 with commit a1723e9c53fe6431415be19302a56543daf503f5 |
| Issue introduced in 5.7 with commit d67668e9dd76d98136048935723947156737932b and fixed in 6.12.16 with commit 4eb063de686bfcdfd03a8c801d1bbe87d2d5eb55 |
| Issue introduced in 5.7 with commit d67668e9dd76d98136048935723947156737932b and fixed in 6.13.4 with commit d456de38d9eb753a4e9fde053c18d4ef8e485339 |
| Issue introduced in 5.7 with commit d67668e9dd76d98136048935723947156737932b and fixed in 6.14 with commit c2fee09fc167c74a64adb08656cb993ea475197e |
| |
| Please see https://www.kernel.org for a full list of currently supported |
| kernel versions by the kernel community. |
| |
| Unaffected versions might change over time as fixes are backported to |
| older supported kernel versions. The official CVE entry at |
| https://cve.org/CVERecord/?id=CVE-2025-21839 |
| will be updated if fixes are backported, please check that for the most |
| up to date information about this issue. |
| |
| |
| Affected files |
| ============== |
| |
| The file(s) affected by this issue are: |
| arch/x86/include/asm/kvm-x86-ops.h |
| arch/x86/include/asm/kvm_host.h |
| arch/x86/kvm/svm/svm.c |
| arch/x86/kvm/vmx/main.c |
| arch/x86/kvm/vmx/vmx.c |
| arch/x86/kvm/vmx/x86_ops.h |
| arch/x86/kvm/x86.c |
| |
| |
| Mitigation |
| ========== |
| |
| The Linux kernel CVE team recommends that you update to the latest |
| stable kernel version for this, and many other bugfixes. Individual |
| changes are never tested alone, but rather are part of a larger kernel |
| release. Cherry-picking individual commits is not recommended or |
| supported by the Linux kernel community at all. If however, updating to |
| the latest release is impossible, the individual changes to resolve this |
| issue can be found at these commits: |
| https://git.kernel.org/stable/c/9efb2b99b96c86664bbdbdd2cdb354ac9627eb20 |
| https://git.kernel.org/stable/c/93eeb6df1605b3a24f38afdba7ab903ba6b64133 |
| https://git.kernel.org/stable/c/a1723e9c53fe6431415be19302a56543daf503f5 |
| https://git.kernel.org/stable/c/4eb063de686bfcdfd03a8c801d1bbe87d2d5eb55 |
| https://git.kernel.org/stable/c/d456de38d9eb753a4e9fde053c18d4ef8e485339 |
| https://git.kernel.org/stable/c/c2fee09fc167c74a64adb08656cb993ea475197e |