From 48c77f2b85f9ed807532e99b7187e6f5b5aa2975 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 16 May 2019 17:14:58 -0700 Subject: [PATCH] kernel - Implement support for SMAP and SMEP security * Implement support for SMAP security. This prevents accidental accesses to user address space from the kernel. When available, we wrap intentional user-space accesses from the kernel with the 'stac' and 'clac' instructions. We use a NOP replacement policy to implement the feature. The wrapper is initially a 'nop %eax' (3-byte NOP), and is replaced by 'stac' and 'clac' via a .section iteration when the feature is supported. * Implement support for SMEP security. This prevents accidental execution of user code from the kernel and simply requires turning the bit on in CR4. * Reports support in dmesg via the 'CPU Special Features Installed:' line. --- sys/cpu/x86_64/include/asmacros.h | 22 ++++++++++++++++ sys/cpu/x86_64/include/specialreg.h | 4 +++ sys/platform/pc64/x86_64/identcpu.c | 18 ++++++++----- sys/platform/pc64/x86_64/initcpu.c | 4 ++- sys/platform/pc64/x86_64/machdep.c | 27 +++++++++++++++++++ sys/platform/pc64/x86_64/pmap.c | 11 ++++++++ sys/platform/pc64/x86_64/support.s | 40 ++++++++++++++++++++++++++--- 7 files changed, 115 insertions(+), 11 deletions(-) diff --git a/sys/cpu/x86_64/include/asmacros.h b/sys/cpu/x86_64/include/asmacros.h index 00c65f59b9..c1c0ceea9c 100644 --- a/sys/cpu/x86_64/include/asmacros.h +++ b/sys/cpu/x86_64/include/asmacros.h @@ -457,6 +457,28 @@ movq %gs:PC_PRVSPACE, reg ; \ addq $PC_ ## member, reg +/* + * This adds a code placemarker (the 3-byte NOP) and references + * it from a section, allowing it to be replaced with a STAC or CLAC + * instruction (also 3-byte instructions) when the system supports + * SMAP. + */ +#define SMAP_OPEN \ + .globl __start_set_smap_open ; \ + .globl __stop_set_smap_open ; \ +60: nop %eax ; \ + .section set_smap_open,"aw" ; \ + .quad 60b ; \ + .previous \ + +#define SMAP_CLOSE \ + .globl __start_set_smap_close ; \ + .globl __stop_set_smap_close ; \ +60: nop %eax ; \ + .section set_smap_close,"aw" ; \ + .quad 60b ; \ + .previous \ + #endif /* LOCORE */ #endif /* !_CPU_ASMACROS_H_ */ diff --git a/sys/cpu/x86_64/include/specialreg.h b/sys/cpu/x86_64/include/specialreg.h index 5f03bbfc9b..656c2ce864 100644 --- a/sys/cpu/x86_64/include/specialreg.h +++ b/sys/cpu/x86_64/include/specialreg.h @@ -68,6 +68,10 @@ #define CR4_XMM 0x00000400 /* Enable SIMD/MMX2 to use except 16 */ #define CR4_VMXE 0x00002000 /* Enables VMX - Intel specific */ #define CR4_XSAVE 0x00040000 /* Enable XSave (for AVX Instructions)*/ +#define CR4_SMEP 0x00100000 /* Supervisor-Mode Execution Prevent */ +#define CR4_SMAP 0x00200000 /* Supervisor-Mode Access Prevent */ +#define CR4_PKE 0x00400000 /* Protection Keys Enable */ + /* * Bits in x86_64 special registers. EFER is 64 bits wide. diff --git a/sys/platform/pc64/x86_64/identcpu.c b/sys/platform/pc64/x86_64/identcpu.c index dae28f0b64..11f9dbb2b0 100644 --- a/sys/platform/pc64/x86_64/identcpu.c +++ b/sys/platform/pc64/x86_64/identcpu.c @@ -474,13 +474,19 @@ printcpuinfo(void) if (*cpu_vendor || cpu_id) kprintf("\n"); - if (!bootverbose) - return; - - if (cpu_vendor_id == CPU_VENDOR_AMD) - print_AMD_info(); + if (cpu_stdext_feature & (CPUID_STDEXT_SMAP | CPUID_STDEXT_SMEP)) { + kprintf("CPU Special Features Installed:"); + if (cpu_stdext_feature & CPUID_STDEXT_SMAP) + kprintf(" SMAP"); + if (cpu_stdext_feature & CPUID_STDEXT_SMEP) + kprintf(" SMEP"); + kprintf("\n"); + } - kprintf("npx mask: 0x%8.8x\n", npx_mxcsr_mask); + if (bootverbose) { + if (cpu_vendor_id == CPU_VENDOR_AMD) + print_AMD_info(); + } } void diff --git a/sys/platform/pc64/x86_64/initcpu.c b/sys/platform/pc64/x86_64/initcpu.c index 25b60bd2da..fd20657b99 100644 --- a/sys/platform/pc64/x86_64/initcpu.c +++ b/sys/platform/pc64/x86_64/initcpu.c @@ -207,7 +207,9 @@ initializecpu(int cpu) { uint64_t msr; - /*Check for FXSR and SSE support and enable if available.*/ + /* + * Check for FXSR and SSE support and enable if available + */ if ((cpu_feature & CPUID_XMM) && (cpu_feature & CPUID_FXSR)) { load_cr4(rcr4() | CR4_FXSR | CR4_XMM); cpu_fxsr = hw_instruction_sse = 1; diff --git a/sys/platform/pc64/x86_64/machdep.c b/sys/platform/pc64/x86_64/machdep.c index 7fea86a970..0e62f23078 100644 --- a/sys/platform/pc64/x86_64/machdep.c +++ b/sys/platform/pc64/x86_64/machdep.c @@ -316,6 +316,8 @@ vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; static struct trapframe proc0_tf; +static void cpu_implement_smap(void); + static void cpu_startup(void *dummy) { @@ -330,6 +332,9 @@ cpu_startup(void *dummy) startrtclock(); printcpuinfo(); panicifcpuunsupported(); + if (cpu_stdext_feature & CPUID_STDEXT_SMAP) + cpu_implement_smap(); + kprintf("real memory = %ju (%ju MB)\n", (intmax_t)Realmem, (intmax_t)Realmem / 1024 / 1024); @@ -3491,3 +3496,25 @@ pcpu_timer_always(struct intrframe *frame) #endif #endif } + +SET_DECLARE(smap_open, char); +SET_DECLARE(smap_close, char); + +static void +cpu_implement_smap(void) +{ + char **scan; + + for (scan = SET_BEGIN(smap_open); + scan < SET_LIMIT(smap_open); ++scan) { + (*scan)[0] = 0x0F; + (*scan)[1] = 0x01; + (*scan)[2] = 0xCB; + } + for (scan = SET_BEGIN(smap_close); + scan < SET_LIMIT(smap_close); ++scan) { + (*scan)[0] = 0x0F; + (*scan)[1] = 0x01; + (*scan)[2] = 0xCA; + } +} diff --git a/sys/platform/pc64/x86_64/pmap.c b/sys/platform/pc64/x86_64/pmap.c index f7e1225601..12bf0c394e 100644 --- a/sys/platform/pc64/x86_64/pmap.c +++ b/sys/platform/pc64/x86_64/pmap.c @@ -1115,6 +1115,17 @@ pmap_bootstrap(vm_paddr_t *firstaddr) */ x86_64_protection_init(); + /* + * Check for SMAP support and enable if available. Must be done + * after cr3 is loaded. + */ + if (cpu_stdext_feature & CPUID_STDEXT_SMAP) { + load_cr4(rcr4() | CR4_SMAP); + } + if (cpu_stdext_feature & CPUID_STDEXT_SMEP) { + load_cr4(rcr4() | CR4_SMEP); + } + /* * The kernel's pmap is statically allocated so we don't have to use * pmap_create, which is unlikely to work correctly at this part of diff --git a/sys/platform/pc64/x86_64/support.s b/sys/platform/pc64/x86_64/support.s index 13958c0df1..6434c13b85 100644 --- a/sys/platform/pc64/x86_64/support.s +++ b/sys/platform/pc64/x86_64/support.s @@ -299,16 +299,18 @@ END(fillw) * Read kernel or user memory with fault protection. */ ENTRY(kreadmem64) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $kreadmem64fault,PCB_ONFAULT(%rcx) movq %rsp,PCB_ONFAULT_SP(%rcx) - movq (%rdi),%rax movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret kreadmem64fault: + SMAP_CLOSE movq PCPU(curthread),%rcx xorl %eax,%eax movq TD_PCB(%rcx),%rcx @@ -322,6 +324,7 @@ END(kreadmem64) * %rdi, %rsi, %rdx */ ENTRY(std_copyout) + SMAP_OPEN movq PCPU(curthread),%rax movq TD_PCB(%rax), %rax movq $copyout_fault,PCB_ONFAULT(%rax) @@ -367,6 +370,7 @@ ENTRY(std_copyout) movsb done_copyout: + SMAP_CLOSE xorl %eax,%eax movq PCPU(curthread),%rdx movq TD_PCB(%rdx), %rdx @@ -375,6 +379,7 @@ done_copyout: ALIGN_TEXT copyout_fault: + SMAP_CLOSE movq PCPU(curthread),%rdx movq TD_PCB(%rdx), %rdx movq $0,PCB_ONFAULT(%rdx) @@ -387,6 +392,7 @@ END(std_copyout) * %rdi, %rsi, %rdx */ ENTRY(std_copyin) + SMAP_OPEN movq PCPU(curthread),%rax movq TD_PCB(%rax), %rax movq $copyin_fault,PCB_ONFAULT(%rax) @@ -417,6 +423,7 @@ ENTRY(std_copyin) movsb done_copyin: + SMAP_CLOSE xorl %eax,%eax movq PCPU(curthread),%rdx movq TD_PCB(%rdx), %rdx @@ -425,6 +432,7 @@ done_copyin: ALIGN_TEXT copyin_fault: + SMAP_CLOSE movq PCPU(curthread),%rdx movq TD_PCB(%rdx), %rdx movq $0,PCB_ONFAULT(%rdx) @@ -437,6 +445,7 @@ END(std_copyin) * dst = %rdi, old = %rsi, new = %rdx */ ENTRY(casu32) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -459,6 +468,7 @@ ENTRY(casu32) movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(casu32) @@ -466,6 +476,7 @@ END(casu32) * swapu32 - Swap int in user space. ptr = %rdi, val = %rsi */ ENTRY(std_swapu32) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -487,10 +498,12 @@ ENTRY(std_swapu32) movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_swapu32) ENTRY(std_fuwordadd32) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -508,10 +521,10 @@ ENTRY(std_fuwordadd32) * value we expected (old) from before the store, otherwise it will * be the current value. */ - movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_fuwordadd32) @@ -520,6 +533,7 @@ END(std_fuwordadd32) * dst = %rdi, old = %rsi, new = %rdx */ ENTRY(casu64) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -542,6 +556,7 @@ ENTRY(casu64) movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(casu64) @@ -549,6 +564,7 @@ END(casu64) * swapu64 - Swap long in user space. ptr = %rdi, val = %rsi */ ENTRY(std_swapu64) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -570,10 +586,12 @@ ENTRY(std_swapu64) movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_swapu64) ENTRY(std_fuwordadd64) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -595,6 +613,7 @@ ENTRY(std_fuwordadd64) movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_fuwordadd64) @@ -605,6 +624,7 @@ END(std_fuwordadd64) */ ENTRY(std_fuword64) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -616,10 +636,12 @@ ENTRY(std_fuword64) movq (%rdi),%rax movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_fuword64) ENTRY(std_fuword32) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -631,10 +653,12 @@ ENTRY(std_fuword32) movl (%rdi),%eax movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_fuword32) ENTRY(std_fubyte) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -646,6 +670,7 @@ ENTRY(std_fubyte) movzbl (%rdi),%eax movq $0,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret ALIGN_TEXT @@ -655,6 +680,7 @@ fusufault: movq TD_PCB(%rcx), %rcx movq %rax,PCB_ONFAULT(%rcx) decq %rax + SMAP_CLOSE ret END(std_fubyte) @@ -667,6 +693,7 @@ END(std_fubyte) * Write a long */ ENTRY(std_suword64) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -681,6 +708,7 @@ ENTRY(std_suword64) movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq %rax,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_suword64) @@ -688,6 +716,7 @@ END(std_suword64) * Write an int */ ENTRY(std_suword32) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -702,10 +731,12 @@ ENTRY(std_suword32) movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq %rax,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_suword32) ENTRY(std_subyte) + SMAP_OPEN movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx movq $fusufault,PCB_ONFAULT(%rcx) @@ -721,6 +752,7 @@ ENTRY(std_subyte) movq PCPU(curthread),%rcx /* restore trashed register */ movq TD_PCB(%rcx), %rcx movq %rax,PCB_ONFAULT(%rcx) + SMAP_CLOSE ret END(std_subyte) @@ -734,6 +766,7 @@ END(std_subyte) * return the actual length in *lencopied. */ ENTRY(std_copyinstr) + SMAP_OPEN movq %rdx,%r8 /* %r8 = maxlen */ movq %rcx,%r9 /* %r9 = *len */ movq PCPU(curthread),%rcx @@ -783,6 +816,7 @@ cpystrflt: movq $EFAULT,%rax cpystrflt_x: + SMAP_CLOSE /* set *lencopied and return %eax */ movq PCPU(curthread),%rcx movq TD_PCB(%rcx), %rcx @@ -802,7 +836,6 @@ END(std_copyinstr) */ ENTRY(copystr) movq %rdx,%r8 /* %r8 = maxlen */ - incq %rdx 1: decq %rdx @@ -824,7 +857,6 @@ ENTRY(copystr) movq $ENAMETOOLONG,%rax 6: - testq %rcx,%rcx jz 7f /* set *lencopied and return %rax */ -- 2.41.0