]> xenbits.xensource.com Git - people/andrewcoop/xen.git/commitdiff
x86/svm: Fix asymmetry with AMD DR MASK context switching
authorAndrew Cooper <andrew.cooper3@citrix.com>
Thu, 21 Sep 2023 16:26:23 +0000 (17:26 +0100)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Wed, 11 Oct 2023 05:36:50 +0000 (06:36 +0100)
The handling of MSR_DR{0..3}_MASK is asymmetric between PV and HVM guests.

HVM guests context switch in based on the guest view of DBEXT, whereas PV
guest switch in base on the host capability.  Both guest types leave the
context dirty for the next vCPU.

This leads to the following issue:

 * PV or HVM vCPU has debugging active (%dr7 + mask)
 * Switch out deactivates %dr7 but leaves other state stale in hardware
 * HVM vCPU with debugging activate but can't see DBEXT is switched in
 * Switch in loads %dr7 but leaves the mask MSRs alone

Now, the HVM vCPU is operating in the context of the prior vCPU's mask MSR,
and furthermore in a case where it genuinely expects there to be no masking
MSRs.

As a stopgap, adjust the HVM path to switch in/out the masks based on host
capabilities rather than guest visibility (i.e. like the PV path).  Adjustment
of the of the intercepts still needs to be dependent on the guest visibility
of DBEXT.

This is part of XSA-444 / CVE-2023-34327

Fixes: c097f54912d3 ("x86/SVM: support data breakpoint extension registers")
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/traps.c

index beb076ea8d62102ae65bb0b8317530e0f0570bb1..24c417ca7199f8422ab24697d985873d05b0cee1 100644 (file)
@@ -317,6 +317,10 @@ static void svm_save_dr(struct vcpu *v)
     v->arch.hvm.flag_dr_dirty = 0;
     vmcb_set_dr_intercepts(vmcb, ~0u);
 
+    /*
+     * The guest can only have changed the mask MSRs if we previous dropped
+     * intercepts.  Re-read them from hardware.
+     */
     if ( v->domain->arch.cpuid->extd.dbext )
     {
         svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_RW);
@@ -348,17 +352,25 @@ static void __restore_debug_registers(struct vmcb_struct *vmcb, struct vcpu *v)
 
     ASSERT(v == current);
 
-    if ( v->domain->arch.cpuid->extd.dbext )
+    /*
+     * Both the PV and HVM paths leave stale DR_MASK values in hardware on
+     * context-switch-out.  If we're activating %dr7 for the guest, we must
+     * sync the DR_MASKs too, whether or not the guest can see them.
+     */
+    if ( boot_cpu_has(X86_FEATURE_DBEXT) )
     {
-        svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE);
-        svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE);
-        svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE);
-        svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE);
-
         wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, v->arch.msrs->dr_mask[0]);
         wrmsrl(MSR_AMD64_DR1_ADDRESS_MASK, v->arch.msrs->dr_mask[1]);
         wrmsrl(MSR_AMD64_DR2_ADDRESS_MASK, v->arch.msrs->dr_mask[2]);
         wrmsrl(MSR_AMD64_DR3_ADDRESS_MASK, v->arch.msrs->dr_mask[3]);
+
+        if ( v->domain->arch.cpuid->extd.dbext )
+        {
+            svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE);
+            svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE);
+            svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE);
+            svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE);
+        }
     }
 
     write_debugreg(0, v->arch.dr[0]);
index 0a005f088bca2fda7723fee9e80a66c2b6b62f66..e1356f696aba6351a3454667556a7c7c4ed723a5 100644 (file)
@@ -2225,6 +2225,11 @@ void activate_debugregs(const struct vcpu *curr)
     if ( curr->arch.dr7 & DR7_ACTIVE_MASK )
         write_debugreg(7, curr->arch.dr7);
 
+    /*
+     * Both the PV and HVM paths leave stale DR_MASK values in hardware on
+     * context-switch-out.  If we're activating %dr7 for the guest, we must
+     * sync the DR_MASKs too, whether or not the guest can see them.
+     */
     if ( boot_cpu_has(X86_FEATURE_DBEXT) )
     {
         wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, curr->arch.msrs->dr_mask[0]);