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:
Andrew Cooper committed 2018-02-15 11:08:27 +00:00
1 parent b7dce29d9f
commit 422588e885
5 files changed
+72 -11

No files matched your search

-3
View File
@@ -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
View File
@@ -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 )
+5
View File
@@ -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 */
+9 -2
View File
@@ -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
+7
View File
@@ -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)
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.