x86/shstk: Rework the stack layout to support shadow stacks

We have two free pages in the current stack.  A useful property of shadow
stacks and regular stacks is that they act as each others guard pages as far
as OoB writes go.  As wild OoB stack reads aren't likely, we don't lose any
meaningful protection from using read-only guard pages in general (rather than
non-present guard pages), but result is far simpler for Xen as a whole by not
having a feature/mode dependent stack configuration.

Move the regular IST stacks up by one page, to allow their shadow stack page
to be in slot 0.  The primary shadow stack uses slot 5.

As the shadow IST stacks are only 1k large, shuffle the order of IST vectors
to have #DF numerically highest, so there is no chance of a shadow stack
overflow clobbering the supervisor token.

The XPTI code already breaks the MEMORY_GUARD abstraction for stacks by
forcing it to be in effect (i.e. guard page not present).  To avoid having too
many configurations, do away with the concept entirely, and unconditionally
map the pages in their read-only form.

A later change will turn these properly into shadow stacks.  Some of the
comments written here are the intended result, and will become true in the
subsequent change.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
This commit is contained in:
Andrew Cooper committed 2020-05-29 23:09:46 +01:00
1 parent 5ad05b9c24
commit 6001660473
8 files changed
+30 -47

No files matched your search

+5 -5
View File
@@ -753,14 +753,14 @@ void load_system_tables(void)
* valid on every instruction boundary. (Note: these are all
* semantically ACCESS_ONCE() due to tss's volatile qualifier.)
*
* rsp0 refers to the primary stack. #MC, #DF, NMI and #DB handlers
* rsp0 refers to the primary stack. #MC, NMI, #DB and #DF handlers
* each get their own stacks. No IO Bitmap.
*/
tss->rsp0 = stack_bottom;
tss->ist[IST_MCE - 1] = stack_top + IST_MCE * PAGE_SIZE;
tss->ist[IST_DF - 1] = stack_top + IST_DF * PAGE_SIZE;
tss->ist[IST_NMI - 1] = stack_top + IST_NMI * PAGE_SIZE;
tss->ist[IST_DB - 1] = stack_top + IST_DB * PAGE_SIZE;
tss->ist[IST_MCE - 1] = stack_top + (1 + IST_MCE) * PAGE_SIZE;
tss->ist[IST_NMI - 1] = stack_top + (1 + IST_NMI) * PAGE_SIZE;
tss->ist[IST_DB - 1] = stack_top + (1 + IST_DB) * PAGE_SIZE;
tss->ist[IST_DF - 1] = stack_top + (1 + IST_DF) * PAGE_SIZE;
tss->bitmap = IOBMP_INVALID_OFFSET;
/* All other stack pointers poisioned. */
+6 -13
View File
@@ -5997,25 +5997,18 @@ void memguard_unguard_range(void *p, unsigned long l)
void memguard_guard_stack(void *p)
{
/* IST_MAX IST pages + at least 1 guard page + primary stack. */
BUILD_BUG_ON((IST_MAX + 1) * PAGE_SIZE + PRIMARY_STACK_SIZE > STACK_SIZE);
map_pages_to_xen((unsigned long)p, virt_to_mfn(p), 1, _PAGE_NONE);
memguard_guard_range(p + IST_MAX * PAGE_SIZE,
STACK_SIZE - PRIMARY_STACK_SIZE - IST_MAX * PAGE_SIZE);
p += PRIMARY_SHSTK_SLOT * PAGE_SIZE;
map_pages_to_xen((unsigned long)p, virt_to_mfn(p), 1, _PAGE_NONE);
}
void memguard_unguard_stack(void *p)
{
memguard_unguard_range(p + IST_MAX * PAGE_SIZE,
STACK_SIZE - PRIMARY_STACK_SIZE - IST_MAX * PAGE_SIZE);
}
map_pages_to_xen((unsigned long)p, virt_to_mfn(p), 1, PAGE_HYPERVISOR_RW);
bool memguard_is_stack_guard_page(unsigned long addr)
{
addr &= STACK_SIZE - 1;
return addr >= IST_MAX * PAGE_SIZE &&
addr < STACK_SIZE - PRIMARY_STACK_SIZE;
p += PRIMARY_SHSTK_SLOT * PAGE_SIZE;
map_pages_to_xen((unsigned long)p, virt_to_mfn(p), 1, PAGE_HYPERVISOR_RW);
}
void arch_dump_shared_mem_info(void)
+1 -2
View File
@@ -823,8 +823,7 @@ static int setup_cpu_root_pgt(unsigned int cpu)
/* Install direct map page table entries for stack, IDT, and TSS. */
for ( off = rc = 0; !rc && off < STACK_SIZE; off += PAGE_SIZE )
if ( !memguard_is_stack_guard_page(off) )
rc = clone_mapping(__va(__pa(stack_base[cpu])) + off, rpt);
rc = clone_mapping(__va(__pa(stack_base[cpu])) + off, rpt);
if ( !rc )
rc = clone_mapping(idt_tables[cpu], rpt);
+6 -17
View File
@@ -365,20 +365,15 @@ static void show_guest_stack(struct vcpu *v, const struct cpu_user_regs *regs)
/*
* Notes for get_stack_trace_bottom() and get_stack_dump_bottom()
*
* Stack pages 0 - 3:
* Stack pages 1 - 4:
* These are all 1-page IST stacks. Each of these stacks have an exception
* frame and saved register state at the top. The interesting bound for a
* trace is the word adjacent to this, while the bound for a dump is the
* very top, including the exception frame.
*
* Stack pages 4 and 5:
* None of these are particularly interesting. With MEMORY_GUARD, page 5 is
* explicitly not present, so attempting to dump or trace it is
* counterproductive. Without MEMORY_GUARD, it is possible for a call chain
* to use the entire primary stack and wander into page 5. In this case,
* consider these pages an extension of the primary stack to aid debugging
* hopefully rare situations where the primary stack has effective been
* overflown.
* Stack pages 0 and 5:
* Shadow stacks. These are mapped read-only, and used by CET-SS capable
* processors. They will never contain regular stack data.
*
* Stack pages 6 and 7:
* These form the primary stack, and have a cpu_info at the top. For a
@@ -392,13 +387,10 @@ unsigned long get_stack_trace_bottom(unsigned long sp)
{
switch ( get_stack_page(sp) )
{
case 0 ... 3:
case 1 ... 4:
return ROUNDUP(sp, PAGE_SIZE) -
offsetof(struct cpu_user_regs, es) - sizeof(unsigned long);
#ifndef MEMORY_GUARD
case 4 ... 5:
#endif
case 6 ... 7:
return ROUNDUP(sp, STACK_SIZE) -
sizeof(struct cpu_info) - sizeof(unsigned long);
@@ -412,12 +404,9 @@ unsigned long get_stack_dump_bottom(unsigned long sp)
{
switch ( get_stack_page(sp) )
{
case 0 ... 3:
case 1 ... 4:
return ROUNDUP(sp, PAGE_SIZE) - sizeof(unsigned long);
#ifndef MEMORY_GUARD
case 4 ... 5:
#endif
case 6 ... 7:
return ROUNDUP(sp, STACK_SIZE) - sizeof(unsigned long);
+3
View File
@@ -75,6 +75,9 @@
/* Primary stack is restricted to 8kB by guard pages. */
#define PRIMARY_STACK_SIZE 8192
/* Primary shadow stack is slot 5 of 8, immediately under the primary stack. */
#define PRIMARY_SHSTK_SLOT 5
/* Total size of syscall and emulation stubs. */
#define STUB_BUF_SHIFT (L1_CACHE_SHIFT > 7 ? L1_CACHE_SHIFT : 7)
#define STUB_BUF_SIZE (1 << STUB_BUF_SHIFT)
+6 -6
View File
@@ -16,12 +16,12 @@
*
* 7 - Primary stack (with a struct cpu_info at the top)
* 6 - Primary stack
* 5 - Optionally not present (MEMORY_GUARD)
* 4 - Unused; optionally not present (MEMORY_GUARD)
* 3 - Unused; optionally not present (MEMORY_GUARD)
* 2 - MCE IST stack
* 1 - NMI IST stack
* 0 - Double Fault IST stack
* 5 - Primay Shadow Stack (read-only)
* 4 - #DF IST stack
* 3 - #DB IST stack
* 2 - NMI IST stack
* 1 - #MC IST stack
* 0 - IST Shadow Stacks (4x 1k, read-only)
*/
/*
-1
View File
@@ -536,7 +536,6 @@ void memguard_unguard_range(void *p, unsigned long l);
void memguard_guard_stack(void *p);
void memguard_unguard_stack(void *p);
bool __attribute_const__ memguard_is_stack_guard_page(unsigned long addr);
struct mmio_ro_emulate_ctxt {
unsigned long cr2;
+3 -3
View File
@@ -445,10 +445,10 @@ struct tss_page {
DECLARE_PER_CPU(struct tss_page, tss_page);
#define IST_NONE 0UL
#define IST_DF 1UL
#define IST_MCE 1UL
#define IST_NMI 2UL
#define IST_MCE 3UL
#define IST_DB 4UL
#define IST_DB 3UL
#define IST_DF 4UL
#define IST_MAX 4UL
/* Set the Interrupt Stack Table used by a particular IDT entry. */
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.