mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
x86/xpti: Hide almost all of .text and all .data/.rodata/.bss mappings
The current XPTI implementation isolates the directmap (and therefore a lot of guest data), but a large quantity of CPU0's state (including its stack) remains visible. Furthermore, an attacker able to read .text is in a vastly superior position to normal when it comes to fingerprinting Xen for known vulnerabilities, or scanning for ROP/Spectre gadgets. Collect together the entrypoints in .text.entry (currently 3x4k frames, but can almost certainly be slimmed down), and create a common mapping which is inserted into each per-cpu shadow. The stubs are also inserted into this mapping by pointing at the in-use L2. This allows stubs allocated later (SMP boot, or CPU hotplug) to work without further changes to the common mappings. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
b7dce29d9f
commit
422588e885
5 files changed
+72
-11
No files matched your search
@@ -1935,9 +1935,6 @@ mode.
|
||||
Override default selection of whether to isolate 64-bit PV guest page
|
||||
tables.
|
||||
|
||||
** WARNING: Not yet a complete isolation implementation, but better than
|
||||
nothing. **
|
||||
|
||||
### xsave
|
||||
> `= <boolean>`
|
||||
|
||||
|
||||
+51
-6
@@ -622,6 +622,9 @@ unsigned long alloc_stub_page(unsigned int cpu, unsigned long *mfn)
|
||||
unmap_domain_page(memset(__map_domain_page(pg), 0xcc, PAGE_SIZE));
|
||||
}
|
||||
|
||||
/* Confirm that all stubs fit in a single L3 entry. */
|
||||
BUILD_BUG_ON(NR_CPUS * PAGE_SIZE > (1u << L3_PAGETABLE_SHIFT));
|
||||
|
||||
stub_va = XEN_VIRT_END - (cpu + 1) * PAGE_SIZE;
|
||||
if ( map_pages_to_xen(stub_va, mfn_x(page_to_mfn(pg)), 1,
|
||||
PAGE_HYPERVISOR_RX | MAP_SMALL_PAGES) )
|
||||
@@ -646,13 +649,24 @@ static int clone_mapping(const void *ptr, root_pgentry_t *rpt)
|
||||
{
|
||||
unsigned long linear = (unsigned long)ptr, pfn;
|
||||
unsigned int flags;
|
||||
l3_pgentry_t *pl3e = l4e_to_l3e(idle_pg_table[root_table_offset(linear)]) +
|
||||
l3_table_offset(linear);
|
||||
l3_pgentry_t *pl3e;
|
||||
l2_pgentry_t *pl2e;
|
||||
l1_pgentry_t *pl1e;
|
||||
|
||||
if ( linear < DIRECTMAP_VIRT_START )
|
||||
return 0;
|
||||
/*
|
||||
* Sanity check 'linear'. We only allow cloning from the Xen virtual
|
||||
* range, and in particular, only from the directmap and .text ranges.
|
||||
*/
|
||||
if ( root_table_offset(linear) > ROOT_PAGETABLE_LAST_XEN_SLOT ||
|
||||
root_table_offset(linear) < ROOT_PAGETABLE_FIRST_XEN_SLOT )
|
||||
return -EINVAL;
|
||||
|
||||
if ( linear < XEN_VIRT_START ||
|
||||
(linear >= XEN_VIRT_END && linear < DIRECTMAP_VIRT_START) )
|
||||
return -EINVAL;
|
||||
|
||||
pl3e = l4e_to_l3e(idle_pg_table[root_table_offset(linear)]) +
|
||||
l3_table_offset(linear);
|
||||
|
||||
flags = l3e_get_flags(*pl3e);
|
||||
ASSERT(flags & _PAGE_PRESENT);
|
||||
@@ -744,8 +758,12 @@ static __read_mostly int8_t opt_xpti = -1;
|
||||
boolean_param("xpti", opt_xpti);
|
||||
DEFINE_PER_CPU(root_pgentry_t *, root_pgt);
|
||||
|
||||
extern const char _stextentry[], _etextentry[];
|
||||
|
||||
static int setup_cpu_root_pgt(unsigned int cpu)
|
||||
{
|
||||
static root_pgentry_t common_pgt;
|
||||
|
||||
root_pgentry_t *rpt;
|
||||
unsigned int off;
|
||||
int rc;
|
||||
@@ -764,8 +782,35 @@ static int setup_cpu_root_pgt(unsigned int cpu)
|
||||
idle_pg_table[root_table_offset(RO_MPT_VIRT_START)];
|
||||
/* SH_LINEAR_PT inserted together with guest mappings. */
|
||||
/* PERDOMAIN inserted during context switch. */
|
||||
rpt[root_table_offset(XEN_VIRT_START)] =
|
||||
idle_pg_table[root_table_offset(XEN_VIRT_START)];
|
||||
|
||||
/* One-time setup of common_pgt, which maps .text.entry and the stubs. */
|
||||
if ( unlikely(!root_get_intpte(common_pgt)) )
|
||||
{
|
||||
unsigned long stubs_linear = XEN_VIRT_END - 1;
|
||||
l3_pgentry_t *stubs_main, *stubs_shadow;
|
||||
const char *ptr;
|
||||
|
||||
for ( rc = 0, ptr = _stextentry;
|
||||
!rc && ptr < _etextentry; ptr += PAGE_SIZE )
|
||||
rc = clone_mapping(ptr, rpt);
|
||||
|
||||
if ( rc )
|
||||
return rc;
|
||||
|
||||
/* Confirm that all stubs fit in a single L3 entry. */
|
||||
BUILD_BUG_ON(NR_CPUS * PAGE_SIZE > (1u << L3_PAGETABLE_SHIFT));
|
||||
|
||||
stubs_main = l4e_to_l3e(idle_pg_table[l4_table_offset(stubs_linear)]);
|
||||
stubs_shadow = l4e_to_l3e(rpt[l4_table_offset(stubs_linear)]);
|
||||
|
||||
/* Splice into the regular L2 mapping the stubs. */
|
||||
stubs_shadow[l3_table_offset(stubs_linear)] =
|
||||
stubs_main[l3_table_offset(stubs_linear)];
|
||||
|
||||
common_pgt = rpt[root_table_offset(XEN_VIRT_START)];
|
||||
}
|
||||
|
||||
rpt[root_table_offset(XEN_VIRT_START)] = common_pgt;
|
||||
|
||||
/* Install direct map page table entries for stack, IDT, and TSS. */
|
||||
for ( off = rc = 0; !rc && off < STACK_SIZE; off += PAGE_SIZE )
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <public/xen.h>
|
||||
#include <irq_vectors.h>
|
||||
|
||||
.section .text.entry, "ax", @progbits
|
||||
|
||||
ENTRY(entry_int82)
|
||||
ASM_CLAC
|
||||
pushq $0
|
||||
@@ -270,6 +272,9 @@ ENTRY(compat_int80_direct_trap)
|
||||
call compat_create_bounce_frame
|
||||
jmp compat_test_all_events
|
||||
|
||||
/* compat_create_bounce_frame & helpers don't need to be in .text.entry */
|
||||
.text
|
||||
|
||||
/* CREATE A BASIC EXCEPTION FRAME ON GUEST OS (RING-1) STACK: */
|
||||
/* {[ERRCODE,] EIP, CS, EFLAGS, [ESP, SS]} */
|
||||
/* %rdx: trap_bounce, %rbx: struct vcpu */
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <public/xen.h>
|
||||
#include <irq_vectors.h>
|
||||
|
||||
.section .text.entry, "ax", @progbits
|
||||
|
||||
/* %rbx: struct vcpu */
|
||||
ENTRY(switch_to_kernel)
|
||||
leaq VCPU_trap_bounce(%rbx),%rdx
|
||||
@@ -357,6 +359,9 @@ int80_slow_path:
|
||||
subq $2,UREGS_rip(%rsp)
|
||||
jmp handle_exception_saved
|
||||
|
||||
/* create_bounce_frame & helpers don't need to be in .text.entry */
|
||||
.text
|
||||
|
||||
/* CREATE A BASIC EXCEPTION FRAME ON GUEST OS STACK: */
|
||||
/* { RCX, R11, [ERRCODE,] RIP, CS, RFLAGS, RSP, SS } */
|
||||
/* %rdx: trap_bounce, %rbx: struct vcpu */
|
||||
@@ -487,6 +492,8 @@ ENTRY(dom_crash_sync_extable)
|
||||
jmp asm_domain_crash_synchronous /* Does not return */
|
||||
.popsection
|
||||
|
||||
.section .text.entry, "ax", @progbits
|
||||
|
||||
ENTRY(common_interrupt)
|
||||
SAVE_ALL CLAC
|
||||
|
||||
@@ -845,7 +852,7 @@ GLOBAL(trap_nop)
|
||||
iretq
|
||||
|
||||
/* Table of automatically generated entry points. One per vector. */
|
||||
.section .init.rodata, "a", @progbits
|
||||
.pushsection .init.rodata, "a", @progbits
|
||||
GLOBAL(autogen_entrypoints)
|
||||
/* pop into the .init.rodata section and record an entry point. */
|
||||
.macro entrypoint ent
|
||||
@@ -854,7 +861,7 @@ GLOBAL(autogen_entrypoints)
|
||||
.popsection
|
||||
.endm
|
||||
|
||||
.text
|
||||
.popsection
|
||||
autogen_stubs: /* Automatically generated stubs. */
|
||||
|
||||
vec = 0
|
||||
|
||||
@@ -67,6 +67,13 @@ SECTIONS
|
||||
*(.text)
|
||||
*(.text.__x86_indirect_thunk_*)
|
||||
*(.text.page_aligned)
|
||||
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
_stextentry = .;
|
||||
*(.text.entry)
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
_etextentry = .;
|
||||
|
||||
*(.text.cold)
|
||||
*(.text.unlikely)
|
||||
*(.fixup)
|
||||
|
||||