diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2012-10-31 03:30:23 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2012-10-31 03:30:23 +0000 |
commit | ae838b87cfb9ec31994d844f5c2d450b1402a0b6 (patch) | |
tree | 18a76cb557d5e7fa2354b3c06fc5f9b7bc1443a0 /sys/arch/amd64 | |
parent | 07be4e2f33cfae1bb1e37a79b17512b65d630dec (diff) |
Add support for Intel's Supervisor Mode Access Prevention (SMAP) feature.
When enabled SMAP will generate page faults on the kernel attempting
to read/write user data pages unless an override flag is set.
Instructions that modify the flag are patched into copyin/copyout and
friends on boot if SMAP is enabled.
Those with access to hardware with SMAP can contact me for a test case.
joint work with deraadt@
ok miod@ deraadt@
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r-- | sys/arch/amd64/amd64/copy.S | 43 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/cpu.c | 74 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/identcpu.c | 7 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/trap.c | 7 |
4 files changed, 127 insertions, 4 deletions
diff --git a/sys/arch/amd64/amd64/copy.S b/sys/arch/amd64/amd64/copy.S index 36ca5ad4dab..32e18fc5b56 100644 --- a/sys/arch/amd64/amd64/copy.S +++ b/sys/arch/amd64/amd64/copy.S @@ -1,4 +1,4 @@ -/* $OpenBSD: copy.S,v 1.4 2008/06/09 20:43:41 miod Exp $ */ +/* $OpenBSD: copy.S,v 1.5 2012/10/31 03:30:22 jsg Exp $ */ /* $NetBSD: copy.S,v 1.1 2003/04/26 18:39:26 fvdl Exp $ */ /* @@ -44,6 +44,17 @@ #include <machine/asm.h> /* + * As stac/clac SMAP instructions are 3 bytes, we want the fastest + * 3 byte nop sequence possible here. This will be replaced by + * stac/clac instructions if SMAP is detected after booting. + * + * This would be 'nop (%rax)' if binutils could cope. + * Intel documents multi-byte NOP sequences as being available + * on all family 0x6 and 0xf processors (ie 686+) + */ +#define SMAP_NOP .byte 0x0f, 0x1f, 0x00 + +/* * Copy routines from and to userland, plus a few more. See the * section 9 manpages for info. Some cases can be optimized more. * @@ -99,6 +110,7 @@ ENTRY(kcopy) xorq %rax,%rax ret +.globl _C_LABEL(_copyout_stac), _C_LABEL(_copyout_clac) ENTRY(copyout) pushq $0 @@ -115,6 +127,8 @@ ENTRY(copyout) movq CPUVAR(CURPCB),%rdx leaq _C_LABEL(copy_fault)(%rip),%r11 movq %r11,PCB_ONFAULT(%rdx) +_C_LABEL(_copyout_stac): + SMAP_NOP cld movq %rax,%rcx @@ -126,15 +140,20 @@ ENTRY(copyout) rep movsb +_C_LABEL(_copyout_clac): + SMAP_NOP popq PCB_ONFAULT(%rdx) xorl %eax,%eax ret +.globl _C_LABEL(_copyin_stac), _C_LABEL(_copyin_clac) ENTRY(copyin) movq CPUVAR(CURPCB),%rax pushq $0 leaq _C_LABEL(copy_fault)(%rip),%r11 movq %r11,PCB_ONFAULT(%rax) +_C_LABEL(_copyin_stac): + SMAP_NOP xchgq %rdi,%rsi movq %rdx,%rax @@ -157,6 +176,8 @@ ENTRY(copyin) rep movsb +_C_LABEL(_copyin_clac): + SMAP_NOP movq CPUVAR(CURPCB),%rdx popq PCB_ONFAULT(%rdx) xorl %eax,%eax @@ -165,11 +186,15 @@ ENTRY(copyin) NENTRY(copy_efault) movq $EFAULT,%rax +.globl _C_LABEL(_copy_fault_clac) NENTRY(copy_fault) +_C_LABEL(_copy_fault_clac): + SMAP_NOP movq CPUVAR(CURPCB),%rdx popq PCB_ONFAULT(%rdx) ret +.globl _C_LABEL(_copyoutstr_stac) ENTRY(copyoutstr) xchgq %rdi,%rsi movq %rdx,%r8 @@ -178,6 +203,8 @@ ENTRY(copyoutstr) 5: movq CPUVAR(CURPCB),%rax leaq _C_LABEL(copystr_fault)(%rip),%r11 movq %r11,PCB_ONFAULT(%rax) +_C_LABEL(_copyoutstr_stac): + SMAP_NOP /* * Get min(%rdx, VM_MAXUSER_ADDRESS-%rdi). */ @@ -211,6 +238,7 @@ ENTRY(copyoutstr) movq $ENAMETOOLONG,%rax jmp copystr_return +.globl _C_LABEL(_copyinstr_stac) ENTRY(copyinstr) xchgq %rdi,%rsi movq %rdx,%r8 @@ -219,6 +247,8 @@ ENTRY(copyinstr) movq CPUVAR(CURPCB),%rcx leaq _C_LABEL(copystr_fault)(%rip),%r11 movq %r11,PCB_ONFAULT(%rcx) +_C_LABEL(_copyinstr_stac): + SMAP_NOP /* * Get min(%rdx, VM_MAXUSER_ADDRESS-%rsi). @@ -256,8 +286,11 @@ ENTRY(copyinstr) ENTRY(copystr_efault) movl $EFAULT,%eax +.globl _C_LABEL(_copystr_fault_clac) ENTRY(copystr_fault) copystr_return: +_C_LABEL(_copystr_fault_clac): + SMAP_NOP /* Set *lencopied and return %eax. */ movq CPUVAR(CURPCB),%rcx movq $0,PCB_ONFAULT(%rcx) @@ -297,3 +330,11 @@ ENTRY(copystr) movq %r8,(%rcx) 7: ret + +.globl _C_LABEL(_stac) +_C_LABEL(_stac): + stac + +.globl _C_LABEL(_clac) +_C_LABEL(_clac): + clac diff --git a/sys/arch/amd64/amd64/cpu.c b/sys/arch/amd64/amd64/cpu.c index f931c80771c..0b843d32dad 100644 --- a/sys/arch/amd64/amd64/cpu.c +++ b/sys/arch/amd64/amd64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.52 2012/10/09 04:40:36 jsg Exp $ */ +/* $OpenBSD: cpu.c,v 1.53 2012/10/31 03:30:22 jsg Exp $ */ /* $NetBSD: cpu.c,v 1.1 2003/04/26 18:39:26 fvdl Exp $ */ /*- @@ -112,6 +112,74 @@ struct cpu_softc { struct cpu_info *sc_info; /* pointer to CPU info */ }; +#ifndef SMALL_KERNEL +void replacesmap(void); + +extern long _copyout_stac; +extern long _copyout_clac; +extern long _copyin_stac; +extern long _copyin_clac; +extern long _copy_fault_clac; +extern long _copyoutstr_stac; +extern long _copyinstr_stac; +extern long _copystr_fault_clac; +extern long _stac; +extern long _clac; + +static const struct { + void *daddr; + void *saddr; +} ireplace[] = { + { &_copyout_stac, &_stac }, + { &_copyout_clac, &_clac }, + { &_copyin_stac, &_stac }, + { &_copyin_clac, &_clac }, + { &_copy_fault_clac, &_clac }, + { &_copyoutstr_stac, &_stac }, + { &_copyinstr_stac, &_stac }, + { &_copystr_fault_clac, &_clac }, +}; + +void +replacesmap(void) +{ + static int replacedone = 0; + int i, s; + vaddr_t nva; + + if (replacedone) + return; + replacedone = 1; + + s = splhigh(); + /* + * Create writeable aliases of memory we need + * to write to as kernel is mapped read-only + */ + nva = uvm_km_valloc(kernel_map, 2); + + for (i = 0; i < nitems(ireplace); i++) { + paddr_t kva = trunc_page((paddr_t)ireplace[i].daddr); + paddr_t po = (paddr_t)ireplace[i].daddr & PAGE_MASK; + paddr_t pa1, pa2; + + pmap_extract(pmap_kernel(), kva, &pa1); + pmap_extract(pmap_kernel(), kva + PAGE_SIZE, &pa2); + pmap_kenter_pa(nva, pa1, VM_PROT_READ | VM_PROT_WRITE); + pmap_kenter_pa(nva + PAGE_SIZE, pa2, VM_PROT_READ | + VM_PROT_WRITE); + pmap_update(pmap_kernel()); + + /* replace 3 byte nops with stac/clac instructions */ + bcopy(ireplace[i].saddr, (void *)(nva + po), 3); + } + + uvm_km_free(kernel_map, nva, 2); + + splx(s); +} +#endif /* !SMALL_KERNEL */ + #ifdef MULTIPROCESSOR int mp_cpu_start(struct cpu_info *); void mp_cpu_start_cleanup(struct cpu_info *); @@ -377,6 +445,10 @@ cpu_init(struct cpu_info *ci) lcr0(rcr0() | CR0_WP); lcr4(rcr4() | CR4_DEFAULT | (ci->ci_feature_sefflags & SEFF0EBX_SMEP ? CR4_SMEP : 0)); +#ifndef SMALL_KERNEL + if (ci->ci_feature_sefflags & SEFF0EBX_SMAP) + lcr4(rcr4() | CR4_SMAP); +#endif #ifdef MULTIPROCESSOR ci->ci_flags |= CPUF_RUNNING; diff --git a/sys/arch/amd64/amd64/identcpu.c b/sys/arch/amd64/amd64/identcpu.c index c597bb099f7..8812d0c26e1 100644 --- a/sys/arch/amd64/amd64/identcpu.c +++ b/sys/arch/amd64/amd64/identcpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: identcpu.c,v 1.41 2012/10/09 09:16:09 jsg Exp $ */ +/* $OpenBSD: identcpu.c,v 1.42 2012/10/31 03:30:22 jsg Exp $ */ /* $NetBSD: identcpu.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $ */ /* @@ -44,6 +44,8 @@ #include <machine/cpu.h> #include <machine/cpufunc.h> +void replacesmap(void); + /* sysctl wants this. */ char cpu_model[48]; int cpuspeed; @@ -445,6 +447,9 @@ identifycpu(struct cpu_info *ci) if (cpu_ecxfeature & CPUIDECX_RDRAND) has_rdrand = 1; + + if (ci->ci_feature_sefflags & SEFF0EBX_SMAP) + replacesmap(); } if (!strncmp(mycpu_model, "Intel", 5)) { u_int32_t cflushsz; diff --git a/sys/arch/amd64/amd64/trap.c b/sys/arch/amd64/amd64/trap.c index 27343ddca33..6bbeda1b2e8 100644 --- a/sys/arch/amd64/amd64/trap.c +++ b/sys/arch/amd64/amd64/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.30 2012/10/09 04:40:36 jsg Exp $ */ +/* $OpenBSD: trap.c,v 1.31 2012/10/31 03:30:22 jsg Exp $ */ /* $NetBSD: trap.c,v 1.2 2003/05/04 23:51:56 fvdl Exp $ */ /*- @@ -324,6 +324,11 @@ copyfault: if (cr2 <= VM_MAXUSER_ADDRESS && frame->tf_err & PGEX_I) panic("attempt to execute user address %p " "in supervisor mode", (void *)cr2); + /* This will only trigger if SMAP is enabled */ + if (pcb->pcb_onfault == NULL && cr2 <= VM_MAXUSER_ADDRESS && + frame->tf_err & PGEX_P) + panic("attempt to access user address %p " + "in supervisor mode", (void *)cr2); goto faultcommon; case T_PAGEFLT|T_USER: { /* page fault */ |