]> xenbits.xensource.com Git - xen.git/commitdiff
x86/cet: Clear IST supervisor token busy bits on S3 resume
authorAndrew Cooper <andrew.cooper3@citrix.com>
Mon, 14 Mar 2022 10:30:46 +0000 (10:30 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Fri, 25 Mar 2022 17:11:55 +0000 (17:11 +0000)
Stacks are not freed across S3.  Execution just stops, leaving supervisor
token busy bits active.  Fixing this for the primary shadow stack was done
previously, but there is a (rare) risk that an IST token is left busy too, if
the platform power-off happens to intersect with an NMI/#MC arriving.  This
will manifest as #DF next time the IST vector gets used.

Introduce rdssp() and wrss() helpers in a new shstk.h, cleaning up
fixup_exception_return() and explaining the trick with the literal 1.

Then this infrastructure to rewrite the IST tokens in load_system_tables()
when all the other IST details are being set up.  In the case that an IST
token were left busy across S3, this will clear the busy bit before the stack
gets used.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
(cherry picked from commit e421ed0f68488863599532bda575c03c33cde0e0)

xen/arch/x86/cpu/common.c
xen/arch/x86/traps.c
xen/include/asm-x86/shstk.h [new file with mode: 0644]

index 0388d720df5e518fa7f02a15b621447174405ba8..dd592a69c50ae789f48d7ab181dd18e92133ab36 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/apic.h>
 #include <asm/random.h>
 #include <asm/setup.h>
+#include <asm/shstk.h>
 #include <mach_apic.h>
 #include <public/sysctl.h> /* for XEN_INVALID_{SOCKET,CORE}_ID */
 
@@ -811,15 +812,31 @@ void load_system_tables(void)
         */
        if (cpu_has_xen_shstk) {
                volatile uint64_t *ist_ssp = tss_page->ist_ssp;
+               unsigned long
+                       mce_ssp = stack_top + (IST_MCE * IST_SHSTK_SIZE) - 8,
+                       nmi_ssp = stack_top + (IST_NMI * IST_SHSTK_SIZE) - 8,
+                       db_ssp  = stack_top + (IST_DB  * IST_SHSTK_SIZE) - 8,
+                       df_ssp  = stack_top + (IST_DF  * IST_SHSTK_SIZE) - 8;
 
                ist_ssp[0] = 0x8600111111111111ul;
-               ist_ssp[IST_MCE] = stack_top + (IST_MCE * IST_SHSTK_SIZE) - 8;
-               ist_ssp[IST_NMI] = stack_top + (IST_NMI * IST_SHSTK_SIZE) - 8;
-               ist_ssp[IST_DB]  = stack_top + (IST_DB  * IST_SHSTK_SIZE) - 8;
-               ist_ssp[IST_DF]  = stack_top + (IST_DF  * IST_SHSTK_SIZE) - 8;
+               ist_ssp[IST_MCE] = mce_ssp;
+               ist_ssp[IST_NMI] = nmi_ssp;
+               ist_ssp[IST_DB]  = db_ssp;
+               ist_ssp[IST_DF]  = df_ssp;
                for ( i = IST_DF + 1; i < ARRAY_SIZE(tss_page->ist_ssp); ++i )
                        ist_ssp[i] = 0x8600111111111111ul;
 
+               if (IS_ENABLED(CONFIG_XEN_SHSTK) && rdssp() != SSP_NO_SHSTK) {
+                       /*
+                        * Rewrite supervisor tokens when shadow stacks are
+                        * active.  This resets any busy bits left across S3.
+                        */
+                       wrss(mce_ssp, _p(mce_ssp));
+                       wrss(nmi_ssp, _p(nmi_ssp));
+                       wrss(db_ssp,  _p(db_ssp));
+                       wrss(df_ssp,  _p(df_ssp));
+               }
+
                wrmsrl(MSR_INTERRUPT_SSP_TABLE, (unsigned long)ist_ssp);
        }
 
index d8dd583a4add11f9299f4c00984f7b10719ee5f9..de372853d1fdb70aa95e936ce711dc154b6aff0d 100644 (file)
@@ -81,6 +81,7 @@
 #include <xsm/xsm.h>
 #include <asm/pv/traps.h>
 #include <asm/pv/mm.h>
+#include <asm/shstk.h>
 
 /*
  * opt_nmi: one of 'ignore', 'dom0', or 'fatal'.
@@ -782,8 +783,7 @@ static void fixup_exception_return(struct cpu_user_regs *regs,
     {
         unsigned long ssp, *ptr, *base;
 
-        asm ( "rdsspq %0" : "=r" (ssp) : "0" (1) );
-        if ( ssp == 1 )
+        if ( (ssp = rdssp()) == SSP_NO_SHSTK )
             goto shstk_done;
 
         ptr = _p(ssp);
@@ -812,9 +812,7 @@ static void fixup_exception_return(struct cpu_user_regs *regs,
              */
             if ( ptr[0] == regs->rip && ptr[1] == regs->cs )
             {
-                asm ( "wrssq %[fix], %[stk]"
-                      : [stk] "=m" (ptr[0])
-                      : [fix] "r" (fixup) );
+                wrss(fixup, ptr);
                 goto shstk_done;
             }
         }
diff --git a/xen/include/asm-x86/shstk.h b/xen/include/asm-x86/shstk.h
new file mode 100644 (file)
index 0000000..fdc9cc6
--- /dev/null
@@ -0,0 +1,46 @@
+/******************************************************************************
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (c) 2022 Citrix Systems Ltd.
+ */
+#ifndef XEN_ASM_SHSTK_H
+#define XEN_ASM_SHSTK_H
+
+/*
+ * RDSSP is a nop when shadow stacks are inactive.  Also, SSP has a minimum
+ * alignment of 4 which is enforced by hardware.
+ *
+ * We load 1 into a register, then RDSSP.  If shadow stacks are not enabled,
+ * RDSSP is a nop, and the 1 is preserved.  Otherwise, the 1 is clobbered with
+ * the real SSP, which has the bottom two bits clear.
+ */
+#define SSP_NO_SHSTK 1
+
+static inline unsigned long rdssp(void)
+{
+    unsigned long ssp;
+
+    asm volatile ( "rdsspq %0" : "=r" (ssp) : "0" (SSP_NO_SHSTK) );
+
+    return ssp;
+}
+
+static inline void wrss(unsigned long val, unsigned long *ptr)
+{
+    asm ( "wrssq %[val], %[ptr]"
+          : [ptr] "=m" (*ptr)
+          : [val] "r" (val) );
+}
+
+#endif /* XEN_ASM_SHSTK_H */