diff options
-rw-r--r-- | ChangeLog | 67 | ||||
-rwxr-xr-x | configure | 11 | ||||
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | elf/Makefile | 2 | ||||
-rw-r--r-- | elf/dl-load.c | 60 | ||||
-rw-r--r-- | elf/dl-open.c | 3 | ||||
-rw-r--r-- | elf/rtld.c | 9 | ||||
-rw-r--r-- | sysdeps/generic/dl-prop.h | 54 | ||||
-rw-r--r-- | sysdeps/i386/dl-cet.c | 67 | ||||
-rw-r--r-- | sysdeps/i386/dl-trampoline.S | 72 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/x86/cpu-features.c | 38 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/x86/dl-cet.h | 37 | ||||
-rw-r--r-- | sysdeps/x86/Makefile | 42 | ||||
-rw-r--r-- | sysdeps/x86/cet-tunables.h | 29 | ||||
-rw-r--r-- | sysdeps/x86/check-cet.awk | 53 | ||||
-rw-r--r-- | sysdeps/x86/configure | 69 | ||||
-rw-r--r-- | sysdeps/x86/configure.ac | 46 | ||||
-rw-r--r-- | sysdeps/x86/cpu-features.c | 60 | ||||
-rw-r--r-- | sysdeps/x86/cpu-tunables.c | 48 | ||||
-rw-r--r-- | sysdeps/x86/dl-cet.c | 346 | ||||
-rw-r--r-- | sysdeps/x86/dl-procruntime.c | 68 | ||||
-rw-r--r-- | sysdeps/x86/dl-prop.h | 153 | ||||
-rw-r--r-- | sysdeps/x86/dl-tunables.list | 6 | ||||
-rw-r--r-- | sysdeps/x86/libc-start.h | 25 | ||||
-rw-r--r-- | sysdeps/x86/link_map.h | 26 | ||||
-rw-r--r-- | sysdeps/x86/sysdep.h | 8 | ||||
-rw-r--r-- | sysdeps/x86_64/dl-trampoline.h | 2 |
27 files changed, 1382 insertions, 25 deletions
@@ -1,3 +1,70 @@ +2018-07-16 H.J. Lu <hongjiu.lu@intel.com> + + [BZ #21598] + * configure.ac: Add --enable-cet. + * configure: Regenerated. + * elf/Makefille (all-built-dso): Add a comment. + * elf/dl-load.c (filebuf): Moved before "dynamic-link.h". + Include <dl-prop.h>. + (_dl_map_object_from_fd): Call _dl_process_pt_note on PT_NOTE + segment. + * elf/dl-open.c: Include <dl-prop.h>. + (dl_open_worker): Call _dl_open_check. + * elf/rtld.c: Include <dl-prop.h>. + (dl_main): Call _rtld_process_pt_note on PT_NOTE segment. Call + _rtld_main_check. + * sysdeps/generic/dl-prop.h: New file. + * sysdeps/i386/dl-cet.c: Likewise. + * sysdeps/unix/sysv/linux/x86/cpu-features.c: Likewise. + * sysdeps/unix/sysv/linux/x86/dl-cet.h: Likewise. + * sysdeps/x86/cet-tunables.h: Likewise. + * sysdeps/x86/check-cet.awk: Likewise. + * sysdeps/x86/configure: Likewise. + * sysdeps/x86/configure.ac: Likewise. + * sysdeps/x86/dl-cet.c: Likewise. + * sysdeps/x86/dl-procruntime.c: Likewise. + * sysdeps/x86/dl-prop.h: Likewise. + * sysdeps/x86/libc-start.h: Likewise. + * sysdeps/x86/link_map.h: Likewise. + * sysdeps/i386/dl-trampoline.S (_dl_runtime_resolve): Add + _CET_ENDBR. + (_dl_runtime_profile): Likewise. + (_dl_runtime_resolve_shstk): New. + (_dl_runtime_profile_shstk): Likewise. + * sysdeps/linux/x86/Makefile (sysdep-dl-routines): Add dl-cet + if CET is enabled. + (CFLAGS-.o): Add -fcf-protection if CET is enabled. + (CFLAGS-.os): Likewise. + (CFLAGS-.op): Likewise. + (CFLAGS-.oS): Likewise. + (asm-CPPFLAGS): Add -fcf-protection -include cet.h if CET + is enabled. + (tests-special): Add $(objpfx)check-cet.out. + (cet-built-dso): New. + (+$(cet-built-dso:=.note)): Likewise. + (common-generated): Add $(cet-built-dso:$(common-objpfx)%=%.note). + ($(objpfx)check-cet.out): New. + (generated): Add check-cet.out. + * sysdeps/x86/cpu-features.c: Include <dl-cet.h> and + <cet-tunables.h>. + (TUNABLE_CALLBACK (set_x86_ibt)): New prototype. + (TUNABLE_CALLBACK (set_x86_shstk)): Likewise. + (init_cpu_features): Call get_cet_status to check CET status + and update dl_x86_feature_1 with CET status. Call + TUNABLE_CALLBACK (set_x86_ibt) and TUNABLE_CALLBACK + (set_x86_shstk). Disable and lock CET in libc.a. + * sysdeps/x86/cpu-tunables.c: Include <cet-tunables.h>. + (TUNABLE_CALLBACK (set_x86_ibt)): New function. + (TUNABLE_CALLBACK (set_x86_shstk)): Likewise. + * sysdeps/x86/sysdep.h (_CET_NOTRACK): New. + (_CET_ENDBR): Define if not defined. + (ENTRY): Add _CET_ENDBR. + * sysdeps/x86/dl-tunables.list (glibc.tune): Add x86_ibt and + x86_shstk. + * sysdeps/x86_64/dl-trampoline.h (_dl_runtime_resolve): Add + _CET_ENDBR. + (_dl_runtime_profile): Likewise. + 2018-07-16 Rogerio A. Cardoso <rcardoso@linux.vnet.ibm.com> [BZ #21895] @@ -790,6 +790,7 @@ enable_nscd enable_pt_chown enable_tunables enable_mathvec +enable_cet with_cpu ' ac_precious_vars='build_alias @@ -1465,6 +1466,8 @@ Optional Features: 'no' and 'valstring' --enable-mathvec Enable building and installing mathvec [default depends on architecture] + --enable-cet enable Intel Control-flow Enforcement Technology + (CET), x86 only Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -3759,6 +3762,14 @@ else fi +# Check whether --enable-cet was given. +if test "${enable_cet+set}" = set; then : + enableval=$enable_cet; enable_cet=$enableval +else + enable_cet=no +fi + + # We keep the original values in `$config_*' and never modify them, so we # can write them unchanged into config.make. Everything else uses # $machine, $vendor, and $os, and changes them whenever convenient. diff --git a/configure.ac b/configure.ac index f41ed6decb..014e09a5d5 100644 --- a/configure.ac +++ b/configure.ac @@ -464,6 +464,12 @@ AC_ARG_ENABLE([mathvec], [build_mathvec=$enableval], [build_mathvec=notset]) +AC_ARG_ENABLE([cet], + AC_HELP_STRING([--enable-cet], + [enable Intel Control-flow Enforcement Technology (CET), x86 only]), + [enable_cet=$enableval], + [enable_cet=no]) + # We keep the original values in `$config_*' and never modify them, so we # can write them unchanged into config.make. Everything else uses # $machine, $vendor, and $os, and changes them whenever convenient. diff --git a/elf/Makefile b/elf/Makefile index 41cc3681be..84107f6dbb 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -1058,6 +1058,8 @@ $(objpfx)tst-piemod1.so: $(libsupport) $(objpfx)tst-pie1: $(objpfx)tst-piemod1.so ifeq (yes,$(build-shared)) +# NB: Please keep cet-built-dso in sysdeps/x86/Makefile in sync with +# all-built-dso here. all-built-dso := $(common-objpfx)elf/ld.so $(common-objpfx)libc.so \ $(filter-out $(common-objpfx)linkobj/libc.so, \ $(sort $(wildcard $(addprefix $(common-objpfx), \ diff --git a/elf/dl-load.c b/elf/dl-load.c index 09185ab68d..c51e4b3718 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -30,6 +30,32 @@ #include <sys/param.h> #include <sys/stat.h> #include <sys/types.h> + +/* Type for the buffer we put the ELF header and hopefully the program + header. This buffer does not really have to be too large. In most + cases the program header follows the ELF header directly. If this + is not the case all bets are off and we can make the header + arbitrarily large and still won't get it read. This means the only + question is how large are the ELF and program header combined. The + ELF header 32-bit files is 52 bytes long and in 64-bit files is 64 + bytes long. Each program header entry is again 32 and 56 bytes + long respectively. I.e., even with a file which has 10 program + header entries we only have to read 372B/624B respectively. Add to + this a bit of margin for program notes and reading 512B and 832B + for 32-bit and 64-bit files respecitvely is enough. If this + heuristic should really fail for some file the code in + `_dl_map_object_from_fd' knows how to recover. */ +struct filebuf +{ + ssize_t len; +#if __WORDSIZE == 32 +# define FILEBUF_SIZE 512 +#else +# define FILEBUF_SIZE 832 +#endif + char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr))))); +}; + #include "dynamic-link.h" #include <abi-tag.h> #include <stackinfo.h> @@ -44,6 +70,7 @@ #include <dl-unmap-segments.h> #include <dl-machine-reject-phdr.h> #include <dl-sysdep-open.h> +#include <dl-prop.h> #include <not-cancel.h> #include <endian.h> @@ -69,31 +96,6 @@ int __stack_prot attribute_hidden attribute_relro #endif -/* Type for the buffer we put the ELF header and hopefully the program - header. This buffer does not really have to be too large. In most - cases the program header follows the ELF header directly. If this - is not the case all bets are off and we can make the header - arbitrarily large and still won't get it read. This means the only - question is how large are the ELF and program header combined. The - ELF header 32-bit files is 52 bytes long and in 64-bit files is 64 - bytes long. Each program header entry is again 32 and 56 bytes - long respectively. I.e., even with a file which has 10 program - header entries we only have to read 372B/624B respectively. Add to - this a bit of margin for program notes and reading 512B and 832B - for 32-bit and 64-bit files respecitvely is enough. If this - heuristic should really fail for some file the code in - `_dl_map_object_from_fd' knows how to recover. */ -struct filebuf -{ - ssize_t len; -#if __WORDSIZE == 32 -# define FILEBUF_SIZE 512 -#else -# define FILEBUF_SIZE 832 -#endif - char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr))))); -}; - /* This is the decomposed LD_LIBRARY_PATH search path. */ static struct r_search_path_struct env_path_list attribute_relro; @@ -1152,6 +1154,14 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, l->l_relro_addr = ph->p_vaddr; l->l_relro_size = ph->p_memsz; break; + + case PT_NOTE: + if (_dl_process_pt_note (l, ph, fd, fbp)) + { + errstring = N_("cannot process note segment"); + goto call_lose; + } + break; } if (__glibc_unlikely (nloadcmds == 0)) diff --git a/elf/dl-open.c b/elf/dl-open.c index 9dde4acfbc..f6c8ef1043 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -35,6 +35,7 @@ #include <libc-internal.h> #include <dl-dst.h> +#include <dl-prop.h> /* We must be careful not to leave us in an inconsistent state. Thus we @@ -291,6 +292,8 @@ dl_open_worker (void *a) _dl_debug_state (); LIBC_PROBE (map_complete, 3, args->nsid, r, new); + _dl_open_check (new); + /* Print scope information. */ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) _dl_show_scope (new, 0); diff --git a/elf/rtld.c b/elf/rtld.c index 8c732adb68..1b0c74739f 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -38,6 +38,7 @@ #include <dl-cache.h> #include <dl-osinfo.h> #include <dl-procinfo.h> +#include <dl-prop.h> #include <tls.h> #include <stap-probe.h> #include <stackinfo.h> @@ -1241,6 +1242,12 @@ of this helper program; chances are you did not intend to run this program.\n\ main_map->l_relro_addr = ph->p_vaddr; main_map->l_relro_size = ph->p_memsz; break; + + case PT_NOTE: + if (_rtld_process_pt_note (main_map, ph)) + _dl_error_printf ("\ +ERROR: '%s': cannot process note segment.\n", _dl_argv[0]); + break; } /* Adjust the address of the TLS initialization image in case @@ -2110,6 +2117,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", _dl_show_scope (l, 0); } + _rtld_main_check (main_map, _dl_argv[0]); + if (prelinked) { if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL) diff --git a/sysdeps/generic/dl-prop.h b/sysdeps/generic/dl-prop.h new file mode 100644 index 0000000000..a2b1d38c79 --- /dev/null +++ b/sysdeps/generic/dl-prop.h @@ -0,0 +1,54 @@ +/* Support for GNU properties. Generic version. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _DL_PROP_H +#define _DL_PROP_H + +/* The following functions are used by the dynamic loader and the + dlopen machinery to process PT_NOTE entries in the binary or + shared object. The notes can be used to change the behaviour of + the loader, and as such offer a flexible mechanism for hooking in + various checks related to ABI tags or implementing "flag day" ABI + transitions. */ + +static inline void __attribute__ ((always_inline)) +_rtld_main_check (struct link_map *m, const char *program) +{ +} + +static inline void __attribute__ ((always_inline)) +_dl_open_check (struct link_map *m) +{ +} + +#ifdef FILEBUF_SIZE +static inline int __attribute__ ((always_inline)) +_dl_process_pt_note (struct link_map *l, const ElfW(Phdr) *ph, + int fd, struct filebuf *fbp) +{ + return 0; +} +#endif + +static inline int __attribute__ ((always_inline)) +_rtld_process_pt_note (struct link_map *l, const ElfW(Phdr) *ph) +{ + return 0; +} + +#endif /* _DL_PROP_H */ diff --git a/sysdeps/i386/dl-cet.c b/sysdeps/i386/dl-cet.c new file mode 100644 index 0000000000..5d9a4e8d51 --- /dev/null +++ b/sysdeps/i386/dl-cet.c @@ -0,0 +1,67 @@ +/* Linux/i386 CET initializers function. + Copyright (C) 2018 Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + + +#define LINKAGE static inline +#define _dl_cet_check cet_check +#include <sysdeps/x86/dl-cet.c> +#undef _dl_cet_check + +#ifdef SHARED +void +_dl_cet_check (struct link_map *main_map, const char *program) +{ + cet_check (main_map, program); + + if ((GL(dl_x86_feature_1)[0] & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) + { + /* Replace _dl_runtime_resolve and _dl_runtime_profile with + _dl_runtime_resolve_shstk and _dl_runtime_profile_shstk, + respectively if SHSTK is enabled. */ + extern void _dl_runtime_resolve (Elf32_Word) attribute_hidden; + extern void _dl_runtime_resolve_shstk (Elf32_Word) attribute_hidden; + extern void _dl_runtime_profile (Elf32_Word) attribute_hidden; + extern void _dl_runtime_profile_shstk (Elf32_Word) attribute_hidden; + unsigned int i; + struct link_map *l; + Elf32_Addr *got; + + if (main_map->l_info[DT_JMPREL]) + { + got = (Elf32_Addr *) D_PTR (main_map, l_info[DT_PLTGOT]); + if (got[2] == (Elf32_Addr) &_dl_runtime_resolve) + got[2] = (Elf32_Addr) &_dl_runtime_resolve_shstk; + else if (got[2] == (Elf32_Addr) &_dl_runtime_profile) + got[2] = (Elf32_Addr) &_dl_runtime_profile_shstk; + } + + i = main_map->l_searchlist.r_nlist; + while (i-- > 0) + { + l = main_map->l_initfini[i]; + if (l->l_info[DT_JMPREL]) + { + got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]); + if (got[2] == (Elf32_Addr) &_dl_runtime_resolve) + got[2] = (Elf32_Addr) &_dl_runtime_resolve_shstk; + else if (got[2] == (Elf32_Addr) &_dl_runtime_profile) + got[2] = (Elf32_Addr) &_dl_runtime_profile_shstk; + } + } + } +} +#endif diff --git a/sysdeps/i386/dl-trampoline.S b/sysdeps/i386/dl-trampoline.S index 8bf86f8fd9..6dc0319216 100644 --- a/sysdeps/i386/dl-trampoline.S +++ b/sysdeps/i386/dl-trampoline.S @@ -32,6 +32,7 @@ .align 16 _dl_runtime_resolve: cfi_adjust_cfa_offset (8) + _CET_ENDBR pushl %eax # Preserve registers otherwise clobbered. cfi_adjust_cfa_offset (4) pushl %ecx @@ -50,14 +51,85 @@ _dl_runtime_resolve: cfi_endproc .size _dl_runtime_resolve, .-_dl_runtime_resolve +# The SHSTK compatible version. + .text + .globl _dl_runtime_resolve_shstk + .type _dl_runtime_resolve_shstk, @function + cfi_startproc + .align 16 +_dl_runtime_resolve_shstk: + cfi_adjust_cfa_offset (8) + _CET_ENDBR + pushl %eax # Preserve registers otherwise clobbered. + cfi_adjust_cfa_offset (4) + pushl %edx + cfi_adjust_cfa_offset (4) + movl 12(%esp), %edx # Copy args pushed by PLT in register. Note + movl 8(%esp), %eax # that `fixup' takes its parameters in regs. + call _dl_fixup # Call resolver. + movl (%esp), %edx # Get register content back. + movl %eax, %ecx # Store the function address. + movl 4(%esp), %eax # Get register content back. + addl $16, %esp # Adjust stack: PLT1 + PLT2 + %eax + %edx + cfi_adjust_cfa_offset (-16) + jmp *%ecx # Jump to function address. + cfi_endproc + .size _dl_runtime_resolve_shstk, .-_dl_runtime_resolve_shstk #ifndef PROF +# The SHSTK compatible version. + .globl _dl_runtime_profile_shstk + .type _dl_runtime_profile_shstk, @function + cfi_startproc + .align 16 +_dl_runtime_profile_shstk: + cfi_adjust_cfa_offset (8) + _CET_ENDBR + pushl %esp + cfi_adjust_cfa_offset (4) + addl $8, (%esp) # Account for the pushed PLT data + pushl %ebp + cfi_adjust_cfa_offset (4) + pushl %eax # Preserve registers otherwise clobbered. + cfi_adjust_cfa_offset (4) + pushl %ecx + cfi_adjust_cfa_offset (4) + pushl %edx + cfi_adjust_cfa_offset (4) + movl %esp, %ecx + subl $8, %esp + cfi_adjust_cfa_offset (8) + movl $-1, 4(%esp) + leal 4(%esp), %edx + movl %edx, (%esp) + pushl %ecx # Address of the register structure + cfi_adjust_cfa_offset (4) + movl 40(%esp), %ecx # Load return address + movl 36(%esp), %edx # Copy args pushed by PLT in register. Note + movl 32(%esp), %eax # that `fixup' takes its parameters in regs. + call _dl_profile_fixup # Call resolver. + cfi_adjust_cfa_offset (-8) + movl (%esp), %edx + testl %edx, %edx + jns 1f + movl 4(%esp), %edx # Get register content back. + movl %eax, %ecx # Store the function address. + movl 12(%esp), %eax # Get register content back. + # Adjust stack: PLT1 + PLT2 + %esp + %ebp + %eax + %ecx + %edx + # + free. + addl $32, %esp + cfi_adjust_cfa_offset (-32) + jmp *%ecx # Jump to function address. + cfi_endproc + .size _dl_runtime_profile_shstk, .-_dl_runtime_profile_shstk + .globl _dl_runtime_profile .type _dl_runtime_profile, @function cfi_startproc .align 16 _dl_runtime_profile: cfi_adjust_cfa_offset (8) + _CET_ENDBR pushl %esp cfi_adjust_cfa_offset (4) addl $8, (%esp) # Account for the pushed PLT data diff --git a/sysdeps/unix/sysv/linux/x86/cpu-features.c b/sysdeps/unix/sysv/linux/x86/cpu-features.c new file mode 100644 index 0000000000..7c9df9b794 --- /dev/null +++ b/sysdeps/unix/sysv/linux/x86/cpu-features.c @@ -0,0 +1,38 @@ +/* Initialize CPU feature data for Linux/x86. + This file is part of the GNU C Library. + Copyright (C) 2018 Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#if CET_ENABLED +static inline int __attribute__ ((always_inline)) +get_cet_status (void) +{ + return 0; +} + +# ifndef SHARED +static inline void +x86_setup_tls (void) +{ + __libc_setup_tls (); + THREAD_SETMEM (THREAD_SELF, header.feature_1, GL(dl_x86_feature_1)[0]); +} + +# define ARCH_SETUP_TLS() x86_setup_tls () +# endif +#endif + +#include <sysdeps/x86/cpu-features.c> diff --git a/sysdeps/unix/sysv/linux/x86/dl-cet.h b/sysdeps/unix/sysv/linux/x86/dl-cet.h new file mode 100644 index 0000000000..ae81e2f2ca --- /dev/null +++ b/sysdeps/unix/sysv/linux/x86/dl-cet.h @@ -0,0 +1,37 @@ +/* Linux/x86 CET initializers function. + Copyright (C) 2018 Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +static inline int __attribute__ ((always_inline)) +dl_cet_allocate_legacy_bitmap (unsigned long *legacy_bitmap) +{ + /* FIXME: Need syscall support. */ + return -1; +} + +static inline int __attribute__ ((always_inline)) +dl_cet_disable_cet (unsigned int cet_feature) +{ + /* FIXME: Need syscall support. */ + return -1; +} + +static inline int __attribute__ ((always_inline)) +dl_cet_lock_cet (void) +{ + /* FIXME: Need syscall support. */ + return -1; +} diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile index 65292f4032..e9b2d0b35d 100644 --- a/sysdeps/x86/Makefile +++ b/sysdeps/x86/Makefile @@ -13,3 +13,45 @@ ifeq ($(subdir),setjmp) gen-as-const-headers += jmp_buf-ssp.sym sysdep_routines += __longjmp_cancel endif + +ifeq ($(enable-cet),yes) +ifeq ($(subdir),elf) +sysdep-dl-routines += dl-cet +endif + +# Add -fcf-protection to CFLAGS when CET is enabled. +CFLAGS-.o += -fcf-protection +CFLAGS-.os += -fcf-protection +CFLAGS-.op += -fcf-protection +CFLAGS-.oS += -fcf-protection + +# Compile assembly codes with <cet.h> when CET is enabled. +asm-CPPFLAGS += -fcf-protection -include cet.h + +ifeq ($(subdir),elf) +ifeq (yes,$(build-shared)) +tests-special += $(objpfx)check-cet.out +endif + +# FIXME: Can't use all-built-dso in elf/Makefile since this file is +# processed before elf/Makefile. Duplicate it here. +cet-built-dso := $(common-objpfx)elf/ld.so $(common-objpfx)libc.so \ + $(filter-out $(common-objpfx)linkobj/libc.so, \ + $(sort $(wildcard $(addprefix $(common-objpfx), \ + */lib*.so \ + iconvdata/*.so)))) + +$(cet-built-dso:=.note): %.note: % + @rm -f $@T + LC_ALL=C $(READELF) -n $< > $@T + test -s $@T + mv -f $@T $@ +common-generated += $(cet-built-dso:$(common-objpfx)%=%.note) + +$(objpfx)check-cet.out: $(..)sysdeps/x86/check-cet.awk \ + $(cet-built-dso:=.note) + LC_ALL=C $(AWK) -f $^ > $@; \ + $(evaluate-test) +generated += check-cet.out +endif +endif diff --git a/sysdeps/x86/cet-tunables.h b/sysdeps/x86/cet-tunables.h new file mode 100644 index 0000000000..ca023053ee --- /dev/null +++ b/sysdeps/x86/cet-tunables.h @@ -0,0 +1,29 @@ +/* x86 CET tuning. + This file is part of the GNU C Library. + Copyright (C) 2018 Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* Valid control values: + 0: Enable CET features based on ELF property note. + 1: Always disable CET features. + 2: Always enable CET features. + 3: Enable CET features permissively. + */ +#define CET_ELF_PROPERTY 0 +#define CET_ALWAYS_OFF 1 +#define CET_ALWAYS_ON 2 +#define CET_PERMISSIVE 3 +#define CET_MAX CET_PERMISSIVE diff --git a/sysdeps/x86/check-cet.awk b/sysdeps/x86/check-cet.awk new file mode 100644 index 0000000000..380d998caf --- /dev/null +++ b/sysdeps/x86/check-cet.awk @@ -0,0 +1,53 @@ +# Verify that all shared objects contain the CET property. +# Copyright (C) 2018 Free Software Foundation, Inc. +# This file is part of the GNU C Library. +# +# The GNU C Library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# The GNU C Library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with the GNU C Library; if not, see +# <http://www.gnu.org/licenses/>. + +# This awk script expects to get command-line files that are each +# the output of 'readelf -n' on a single shared object. +# It exits successfully (0) if all of them contained the CET property. +# It fails (1) if any didn't contain the CET property +# It fails (2) if the input did not take the expected form. + +BEGIN { result = cet = sanity = 0 } + +function check_one(name) { + if (!sanity) { + print name ": *** input did not look like readelf -n output"; + result = 2; + } else if (cet) { + print name ": OK"; + } else { + print name ": *** no CET property found"; + result = result ? result : 1; + } + + cet = sanity = 0; +} + +FILENAME != lastfile { + if (lastfile) + check_one(lastfile); + lastfile = FILENAME; +} + +index ($0, "Displaying notes") != 0 { sanity = 1 } +index ($0, "IBT") != 0 && index ($0, "SHSTK") != 0 { cet = 1 } + +END { + check_one(lastfile); + exit(result); +} diff --git a/sysdeps/x86/configure b/sysdeps/x86/configure new file mode 100644 index 0000000000..b1ff281249 --- /dev/null +++ b/sysdeps/x86/configure @@ -0,0 +1,69 @@ +# This file is generated from configure.ac by Autoconf. DO NOT EDIT! + # Local configure fragment for sysdeps/x86. + +if test x"$enable_cet" = xyes; then + # Check if CET can be enabled. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CET can be enabled" >&5 +$as_echo_n "checking whether CET can be enabled... " >&6; } +if ${libc_cv_x86_cet_available+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat > conftest.c <<EOF +#if !defined __CET__ || __CET__ != 3 +# error CET isn't available. +#endif +EOF + if { ac_try='${CC-cc} -c $CFLAGS -fcf-protection -include cet.h conftest.c 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + libc_cv_x86_cet_available=yes + else + libc_cv_x86_cet_available=no + fi + rm -rf conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_x86_cet_available" >&5 +$as_echo "$libc_cv_x86_cet_available" >&6; } + if test $libc_cv_x86_cet_available = yes; then + enable_cet=yes + else + if test x"$enable_cet" = xdefault; then + enable_cet=no + else + as_fn_error $? "$CC doesn't support CET" "$LINENO" 5 + fi + fi +fi +if test $enable_cet = yes; then + # Check if assembler supports CET. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $AS supports CET" >&5 +$as_echo_n "checking whether $AS supports CET... " >&6; } +if ${libc_cv_x86_cet_as+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat > conftest.s <<EOF + incsspd %ecx +EOF + if { ac_try='${CC-cc} -c $CFLAGS conftest.s -o conftest.o 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + libc_cv_x86_cet_as=yes + else + libc_cv_x86_cet_as=no + fi + rm -rf conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_cv_x86_cet_as" >&5 +$as_echo "$libc_cv_x86_cet_as" >&6; } + if test $libc_cv_x86_cet_as = no; then + as_fn_error $? "$AS doesn't support CET" "$LINENO" 5 + fi +fi +config_vars="$config_vars +enable-cet = $enable_cet" diff --git a/sysdeps/x86/configure.ac b/sysdeps/x86/configure.ac new file mode 100644 index 0000000000..a909b073af --- /dev/null +++ b/sysdeps/x86/configure.ac @@ -0,0 +1,46 @@ +GLIBC_PROVIDES dnl See aclocal.m4 in the top level source directory. +# Local configure fragment for sysdeps/x86. + +if test x"$enable_cet" = xyes; then + # Check if CET can be enabled. + AC_CACHE_CHECK(whether CET can be enabled, + libc_cv_x86_cet_available, [dnl +cat > conftest.c <<EOF +#if !defined __CET__ || __CET__ != 3 +# error CET isn't available. +#endif +EOF + if AC_TRY_COMMAND(${CC-cc} -c $CFLAGS -fcf-protection -include cet.h conftest.c 1>&AS_MESSAGE_LOG_FD); then + libc_cv_x86_cet_available=yes + else + libc_cv_x86_cet_available=no + fi + rm -rf conftest*]) + if test $libc_cv_x86_cet_available = yes; then + enable_cet=yes + else + if test x"$enable_cet" = xdefault; then + enable_cet=no + else + AC_MSG_ERROR([$CC doesn't support CET]) + fi + fi +fi +if test $enable_cet = yes; then + # Check if assembler supports CET. + AC_CACHE_CHECK(whether $AS supports CET, + libc_cv_x86_cet_as, [dnl +cat > conftest.s <<EOF + incsspd %ecx +EOF + if AC_TRY_COMMAND(${CC-cc} -c $CFLAGS conftest.s -o conftest.o 1>&AS_MESSAGE_LOG_FD); then + libc_cv_x86_cet_as=yes + else + libc_cv_x86_cet_as=no + fi + rm -rf conftest*]) + if test $libc_cv_x86_cet_as = no; then + AC_MSG_ERROR([$AS doesn't support CET]) + fi +fi +LIBC_CONFIG_VAR([enable-cet], [$enable_cet]) diff --git a/sysdeps/x86/cpu-features.c b/sysdeps/x86/cpu-features.c index d41ebde823..0627f145e0 100644 --- a/sysdeps/x86/cpu-features.c +++ b/sysdeps/x86/cpu-features.c @@ -28,6 +28,15 @@ extern void TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *) attribute_hidden; + +# if CET_ENABLED +# include <dl-cet.h> +# include <cet-tunables.h> +extern void TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *) + attribute_hidden; +extern void TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *) + attribute_hidden; +# endif #endif static void @@ -449,4 +458,55 @@ no_cpuid: else if (CPU_FEATURES_ARCH_P (cpu_features, I586)) GLRO(dl_platform) = "i586"; #endif + +#if CET_ENABLED +# if HAVE_TUNABLES + TUNABLE_GET (x86_ibt, tunable_val_t *, + TUNABLE_CALLBACK (set_x86_ibt)); + TUNABLE_GET (x86_shstk, tunable_val_t *, + TUNABLE_CALLBACK (set_x86_shstk)); +# endif + + /* Check CET status. */ + unsigned int cet_status = get_cet_status (); + + if (cet_status) + { + GL(dl_x86_feature_1)[0] = cet_status; + +# ifndef SHARED + /* Check if IBT and SHSTK are enabled by kernel. */ + if ((cet_status & GNU_PROPERTY_X86_FEATURE_1_IBT) + || (cet_status & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) + { + /* Disable IBT and/or SHSTK if they are enabled by kernel, but + disabled by environment variable: + + GLIBC_TUNABLES=glibc.tune.hwcaps=-IBT,-SHSTK + */ + unsigned int cet_feature = 0; + if (!HAS_CPU_FEATURE (IBT)) + cet_feature |= GNU_PROPERTY_X86_FEATURE_1_IBT; + if (!HAS_CPU_FEATURE (SHSTK)) + cet_feature |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; + + if (cet_feature) + { + int res = dl_cet_disable_cet (cet_feature); + + /* Clear the disabled bits in dl_x86_feature_1. */ + if (res == 0) + GL(dl_x86_feature_1)[0] &= ~cet_feature; + } + + /* Lock CET if IBT or SHSTK is enabled in executable. Don't + lock CET if SHSTK is enabled permissively. */ + if (((GL(dl_x86_feature_1)[1] >> CET_MAX) + & ((1 << CET_MAX) - 1)) + != CET_PERMISSIVE) + dl_cet_lock_cet (); + } +# endif + } +#endif } diff --git a/sysdeps/x86/cpu-tunables.c b/sysdeps/x86/cpu-tunables.c index af761dcbbc..69155a8f44 100644 --- a/sysdeps/x86/cpu-tunables.c +++ b/sysdeps/x86/cpu-tunables.c @@ -334,4 +334,52 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp) } while (*p != '\0'); } + +# if CET_ENABLED +# include <cet-tunables.h> + +attribute_hidden +void +TUNABLE_CALLBACK (set_x86_ibt) (tunable_val_t *valp) +{ + if (DEFAULT_MEMCMP (valp->strval, "on", sizeof ("on")) == 0) + { + GL(dl_x86_feature_1)[1] &= ~((1 << CET_MAX) - 1); + GL(dl_x86_feature_1)[1] |= CET_ALWAYS_ON; + } + else if (DEFAULT_MEMCMP (valp->strval, "off", sizeof ("off")) == 0) + { + GL(dl_x86_feature_1)[1] &= ~((1 << CET_MAX) - 1); + GL(dl_x86_feature_1)[1] |= CET_ALWAYS_OFF; + } + else if (DEFAULT_MEMCMP (valp->strval, "permissive", + sizeof ("permissive")) == 0) + { + GL(dl_x86_feature_1)[1] &= ~((1 << CET_MAX) - 1); + GL(dl_x86_feature_1)[1] |= CET_PERMISSIVE; + } +} + +attribute_hidden +void +TUNABLE_CALLBACK (set_x86_shstk) (tunable_val_t *valp) +{ + if (DEFAULT_MEMCMP (valp->strval, "on", sizeof ("on")) == 0) + { + GL(dl_x86_feature_1)[1] &= ~(((1 << CET_MAX) - 1) << CET_MAX); + GL(dl_x86_feature_1)[1] |= (CET_ALWAYS_ON << CET_MAX); + } + else if (DEFAULT_MEMCMP (valp->strval, "off", sizeof ("off")) == 0) + { + GL(dl_x86_feature_1)[1] &= ~(((1 << CET_MAX) - 1) << CET_MAX); + GL(dl_x86_feature_1)[1] |= (CET_ALWAYS_OFF << CET_MAX); + } + else if (DEFAULT_MEMCMP (valp->strval, "permissive", + sizeof ("permissive")) == 0) + { + GL(dl_x86_feature_1)[1] &= ~(((1 << CET_MAX) - 1) << CET_MAX); + GL(dl_x86_feature_1)[1] |= (CET_PERMISSIVE << CET_MAX); + } +} +# endif #endif diff --git a/sysdeps/x86/dl-cet.c b/sysdeps/x86/dl-cet.c new file mode 100644 index 0000000000..b82ba14e75 --- /dev/null +++ b/sysdeps/x86/dl-cet.c @@ -0,0 +1,346 @@ +/* x86 CET initializers function. + Copyright (C) 2018 Free Software Foundation, Inc. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#include <unistd.h> +#include <errno.h> +#include <libintl.h> +#include <ldsodefs.h> +#include <dl-cet.h> +#include <cet-tunables.h> + +/* GNU_PROPERTY_X86_FEATURE_1_IBT and GNU_PROPERTY_X86_FEATURE_1_SHSTK + are defined in <elf.h>, which are only available for C sources. + X86_FEATURE_1_IBT and X86_FEATURE_1_SHSTK are defined in <sysdep.h> + which are available for both C and asm sources. They must match. */ +#if GNU_PROPERTY_X86_FEATURE_1_IBT != X86_FEATURE_1_IBT +# error GNU_PROPERTY_X86_FEATURE_1_IBT != X86_FEATURE_1_IBT +#endif +#if GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK +# error GNU_PROPERTY_X86_FEATURE_1_SHSTK != X86_FEATURE_1_SHSTK +#endif + +static int +dl_cet_mark_legacy_region (struct link_map *l) +{ + /* Mark PT_LOAD segments with PF_X in legacy code page bitmap. */ + size_t i, phnum = l->l_phnum; + const ElfW(Phdr) *phdr = l->l_phdr; +#ifdef __x86_64__ + typedef unsigned long long word_t; +#else + typedef unsigned long word_t; +#endif + unsigned int bits_to_set; + word_t mask_to_set; +#define BITS_PER_WORD (sizeof (word_t) * 8) +#define BITMAP_FIRST_WORD_MASK(start) \ + (~((word_t) 0) << ((start) & (BITS_PER_WORD - 1))) +#define BITMAP_LAST_WORD_MASK(nbits) \ + (~((word_t) 0) >> (-(nbits) & (BITS_PER_WORD - 1))) + + word_t *bitmap = (word_t *) GL(dl_x86_legacy_bitmap)[0]; + word_t bitmap_size = GL(dl_x86_legacy_bitmap)[1]; + word_t *p; + size_t page_size = GLRO(dl_pagesize); + + for (i = 0; i < phnum; i++) + if (phdr[i].p_type == PT_LOAD && (phdr[i].p_flags & PF_X)) + { + /* One bit in legacy bitmap represents a page. */ + ElfW(Addr) start = (phdr[i].p_vaddr + l->l_addr) / page_size; + ElfW(Addr) len = (phdr[i].p_memsz + page_size - 1) / page_size; + ElfW(Addr) end = start + len; + + if ((end / 8) > bitmap_size) + return -EINVAL; + + p = bitmap + (start / BITS_PER_WORD); + bits_to_set = BITS_PER_WORD - (start % BITS_PER_WORD); + mask_to_set = BITMAP_FIRST_WORD_MASK (start); + + while (len >= bits_to_set) + { + *p |= mask_to_set; + len -= bits_to_set; + bits_to_set = BITS_PER_WORD; + mask_to_set = ~((word_t) 0); + p++; + } + if (len) + { + mask_to_set &= BITMAP_LAST_WORD_MASK (end); + *p |= mask_to_set; + } + } + + return 0; +} + +/* Check if object M is compatible with CET. */ + +static void +dl_cet_check (struct link_map *m, const char *program) +{ + /* Check how IBT should be enabled. */ + unsigned int enable_ibt_type + = GL(dl_x86_feature_1)[1] & ((1 << CET_MAX) - 1); + /* Check how SHSTK should be enabled. */ + unsigned int enable_shstk_type + = ((GL(dl_x86_feature_1)[1] >> CET_MAX) & ((1 << CET_MAX) - 1)); + + /* No legacy object check if both IBT and SHSTK are always on. */ + if (enable_ibt_type == CET_ALWAYS_ON + && enable_shstk_type == CET_ALWAYS_ON) + return; + + /* Check if IBT is enabled by kernel. */ + bool ibt_enabled + = (GL(dl_x86_feature_1)[0] & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0; + /* Check if SHSTK is enabled by kernel. */ + bool shstk_enabled + = (GL(dl_x86_feature_1)[0] & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0; + + if (ibt_enabled || shstk_enabled) + { + struct link_map *l = NULL; + + /* Check if IBT and SHSTK are enabled in object. */ + bool enable_ibt = (ibt_enabled + && enable_ibt_type != CET_ALWAYS_OFF); + bool enable_shstk = (shstk_enabled + && enable_shstk_type != CET_ALWAYS_OFF); + if (program) + { + /* Enable IBT and SHSTK only if they are enabled in executable. + NB: IBT and SHSTK may be disabled by environment variable: + + GLIBC_TUNABLES=glibc.tune.hwcaps=-IBT,-SHSTK + */ + enable_ibt &= (HAS_CPU_FEATURE (IBT) + && (enable_ibt_type == CET_ALWAYS_ON + || (m->l_cet & lc_ibt) != 0)); + enable_shstk &= (HAS_CPU_FEATURE (SHSTK) + && (enable_shstk_type == CET_ALWAYS_ON + || (m->l_cet & lc_shstk) != 0)); + } + + /* ld.so is CET-enabled by kernel. But shared objects may not + support IBT nor SHSTK. */ + if (enable_ibt || enable_shstk) + { + int res; + unsigned int i; + unsigned int first_legacy, last_legacy; + bool need_legacy_bitmap = false; + + i = m->l_searchlist.r_nlist; + while (i-- > 0) + { + /* Check each shared object to see if IBT and SHSTK are + enabled. */ + l = m->l_initfini[i]; + + if (l->l_init_called) + continue; + +#ifdef SHARED + /* Skip CET check for ld.so since ld.so is CET-enabled. + CET will be disabled later if CET isn't enabled in + executable. */ + if (l == &GL(dl_rtld_map) + || l->l_real == &GL(dl_rtld_map) + || (program && l == m)) + continue; +#endif + + if (enable_ibt + && enable_ibt_type != CET_ALWAYS_ON + && !(l->l_cet & lc_ibt)) + { + /* Remember the first and last legacy objects. */ + if (!need_legacy_bitmap) + last_legacy = i; + first_legacy = i; + need_legacy_bitmap = true; + } + + /* SHSTK is enabled only if it is enabled in executable as + well as all shared objects. */ + enable_shstk &= (enable_shstk_type == CET_ALWAYS_ON + || (l->l_cet & lc_shstk) != 0); + } + + if (need_legacy_bitmap) + { + if (GL(dl_x86_legacy_bitmap)[0]) + { + /* Change legacy bitmap to writable. */ + if (__mprotect ((void *) GL(dl_x86_legacy_bitmap)[0], + GL(dl_x86_legacy_bitmap)[1], + PROT_READ | PROT_WRITE) < 0) + { +mprotect_failure: + if (program) + _dl_fatal_printf ("%s: mprotect legacy bitmap failed\n", + l->l_name); + else + _dl_signal_error (EINVAL, l->l_name, "dlopen", + N_("mprotect legacy bitmap failed")); + } + } + else + { + /* Allocate legacy bitmap. */ + int res = dl_cet_allocate_legacy_bitmap + (GL(dl_x86_legacy_bitmap)); + if (res != 0) + { + if (program) + _dl_fatal_printf ("%s: legacy bitmap isn't available\n", + l->l_name); + else + _dl_signal_error (EINVAL, l->l_name, "dlopen", + N_("legacy bitmap isn't available")); + } + } + + /* Put legacy shared objects in legacy bitmap. */ + for (i = first_legacy; i <= last_legacy; i++) + { + l = m->l_initfini[i]; + + if (l->l_init_called || (l->l_cet & lc_ibt)) + continue; + +#ifdef SHARED + if (l == &GL(dl_rtld_map) + || l->l_real == &GL(dl_rtld_map) + || (program && l == m)) + continue; +#endif + + /* If IBT is enabled in executable and IBT isn't enabled + in this shard object, mark PT_LOAD segments with PF_X + in legacy code page bitmap. */ + res = dl_cet_mark_legacy_region (l); + if (res != 0) + { + if (program) + _dl_fatal_printf ("%s: failed to mark legacy code region\n", + l->l_name); + else + _dl_signal_error (-res, l->l_name, "dlopen", + N_("failed to mark legacy code region")); + } + } + + /* Change legacy bitmap to read-only. */ + if (__mprotect ((void *) GL(dl_x86_legacy_bitmap)[0], + GL(dl_x86_legacy_bitmap)[1], PROT_READ) < 0) + goto mprotect_failure; + } + } + + bool cet_feature_changed = false; + + if (enable_ibt != ibt_enabled || enable_shstk != shstk_enabled) + { + if (!program + && enable_shstk_type != CET_PERMISSIVE) + { + /* When SHSTK is enabled, we can't dlopening a shared + object without SHSTK. */ + if (enable_shstk != shstk_enabled) + _dl_signal_error (EINVAL, l->l_name, "dlopen", + N_("shadow stack isn't enabled")); + return; + } + + /* Disable IBT and/or SHSTK if they are enabled by kernel, but + disabled in executable or shared objects. */ + unsigned int cet_feature = 0; + + /* Disable IBT only during program startup. */ + if (program && !enable_ibt) + cet_feature |= GNU_PROPERTY_X86_FEATURE_1_IBT; + if (!enable_shstk) + cet_feature |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; + + int res = dl_cet_disable_cet (cet_feature); + if (res != 0) + { + if (program) + _dl_fatal_printf ("%s: can't disable CET\n", program); + else + _dl_signal_error (-res, l->l_name, "dlopen", + N_("can't disable CET")); + } + + /* Clear the disabled bits in dl_x86_feature_1. */ + GL(dl_x86_feature_1)[0] &= ~cet_feature; + + cet_feature_changed = true; + } + +#ifdef SHARED + if (program + && (!shstk_enabled + || enable_shstk_type != CET_PERMISSIVE) + && (ibt_enabled || shstk_enabled)) + { + /* Lock CET if IBT or SHSTK is enabled in executable. Don't + lock CET if SHSTK is enabled permissively. */ + int res = dl_cet_lock_cet (); + if (res != 0) + _dl_fatal_printf ("%s: can't lock CET\n", program); + + cet_feature_changed = true; + } +#endif + + if (cet_feature_changed) + { + unsigned int feature_1 = 0; + if (enable_ibt) + feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT; + if (enable_shstk) + feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; + struct pthread *self = THREAD_SELF; + THREAD_SETMEM (self, header.feature_1, feature_1); + } + } +} + +void +_dl_cet_open_check (struct link_map *l) +{ + dl_cet_check (l, NULL); +} + +#ifdef SHARED + +# ifndef LINKAGE +# define LINKAGE +# endif + +LINKAGE +void +_dl_cet_check (struct link_map *main_map, const char *program) +{ + dl_cet_check (main_map, program); +} +#endif /* SHARED */ diff --git a/sysdeps/x86/dl-procruntime.c b/sysdeps/x86/dl-procruntime.c new file mode 100644 index 0000000000..eddbde6a31 --- /dev/null +++ b/sysdeps/x86/dl-procruntime.c @@ -0,0 +1,68 @@ +/* Data for processor runtime information. x86 version. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* This information must be kept in sync with the _DL_HWCAP_COUNT, + HWCAP_PLATFORMS_START and HWCAP_PLATFORMS_COUNT definitions in + dl-hwcap.h. + + If anything should be added here check whether the size of each string + is still ok with the given array size. + + All the #ifdefs in the definitions are quite irritating but + necessary if we want to avoid duplicating the information. There + are three different modes: + + - PROCINFO_DECL is defined. This means we are only interested in + declarations. + + - PROCINFO_DECL is not defined: + + + if SHARED is defined the file is included in an array + initializer. The .element = { ... } syntax is needed. + + + if SHARED is not defined a normal array initialization is + needed. + */ + +#ifndef PROCINFO_CLASS +# define PROCINFO_CLASS +#endif + +#if !IS_IN (ldconfig) +# if !defined PROCINFO_DECL && defined SHARED + ._dl_x86_feature_1 +# else +PROCINFO_CLASS unsigned int _dl_x86_feature_1[2] +# endif +# if !defined SHARED || defined PROCINFO_DECL +; +# else +, +# endif + +# if !defined PROCINFO_DECL && defined SHARED + ._dl_x86_legacy_bitmap +# else +PROCINFO_CLASS unsigned long _dl_x86_legacy_bitmap[2] +# endif +# if !defined SHARED || defined PROCINFO_DECL +; +# else +, +# endif +#endif diff --git a/sysdeps/x86/dl-prop.h b/sysdeps/x86/dl-prop.h new file mode 100644 index 0000000000..d56e20a6dc --- /dev/null +++ b/sysdeps/x86/dl-prop.h @@ -0,0 +1,153 @@ +/* Support for GNU properties. x86 version. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef _DL_PROP_H +#define _DL_PROP_H + +#include <not-cancel.h> + +extern void _dl_cet_check (struct link_map *, const char *) + attribute_hidden; +extern void _dl_cet_open_check (struct link_map *) + attribute_hidden; + +static inline void __attribute__ ((always_inline)) +_rtld_main_check (struct link_map *m, const char *program) +{ +#if CET_ENABLED + _dl_cet_check (m, program); +#endif +} + +static inline void __attribute__ ((always_inline)) +_dl_open_check (struct link_map *m) +{ +#if CET_ENABLED + _dl_cet_open_check (m); +#endif +} + +static inline void __attribute__ ((unused)) +_dl_process_cet_property_note (struct link_map *l, + const ElfW(Nhdr) *note, + const ElfW(Addr) size, + const ElfW(Addr) align) +{ +#if CET_ENABLED + /* The NT_GNU_PROPERTY_TYPE_0 note must be aliged to 4 bytes in + 32-bit objects and to 8 bytes in 64-bit objects. Skip notes + with incorrect alignment. */ + if (align != (__ELF_NATIVE_CLASS / 8)) + return; + + const ElfW(Addr) start = (ElfW(Addr)) note; + + while ((ElfW(Addr)) (note + 1) - start < size) + { + /* Find the NT_GNU_PROPERTY_TYPE_0 note. */ + if (note->n_namesz == 4 + && note->n_type == NT_GNU_PROPERTY_TYPE_0 + && memcmp (note + 1, "GNU", 4) == 0) + { + /* Check for invalid property. */ + if (note->n_descsz < 8 + || (note->n_descsz % sizeof (ElfW(Addr))) != 0) + break; + + /* Start and end of property array. */ + unsigned char *ptr = (unsigned char *) (note + 1) + 4; + unsigned char *ptr_end = ptr + note->n_descsz; + + while (1) + { + unsigned int type = *(unsigned int *) ptr; + unsigned int datasz = *(unsigned int *) (ptr + 4); + + ptr += 8; + if ((ptr + datasz) > ptr_end) + break; + + if (type == GNU_PROPERTY_X86_FEATURE_1_AND + && datasz == 4) + { + unsigned int feature_1 = *(unsigned int *) ptr; + if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_IBT)) + l->l_cet |= lc_ibt; + if ((feature_1 & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) + l->l_cet |= lc_shstk; + break; + } + } + } + + /* NB: Note sections like .note.ABI-tag and .note.gnu.build-id are + aligned to 4 bytes in 64-bit ELF objects. */ + note = ((const void *) note + + ELF_NOTE_NEXT_OFFSET (note->n_namesz, note->n_descsz, + align)); + } +#endif +} + +#ifdef FILEBUF_SIZE +static inline int __attribute__ ((unused)) +_dl_process_pt_note (struct link_map *l, const ElfW(Phdr) *ph, + int fd, struct filebuf *fbp) +{ +# if CET_ENABLED + const ElfW(Nhdr) *note; + ElfW(Nhdr) *note_malloced = NULL; + ElfW(Addr) size = ph->p_filesz; + + if (ph->p_offset + size <= (size_t) fbp->len) + note = (const void *) (fbp->buf + ph->p_offset); + else + { + if (size < __MAX_ALLOCA_CUTOFF) + note = alloca (size); + else + { + note_malloced = malloc (size); + note = note_malloced; + } + __lseek (fd, ph->p_offset, SEEK_SET); + if (__read_nocancel (fd, (void *) note, size) != size) + { + if (note_malloced) + free (note_malloced); + return -1; + } + } + + _dl_process_cet_property_note (l, note, size, ph->p_align); + if (note_malloced) + free (note_malloced); +# endif + return 0; +} +#endif + +static inline int __attribute__ ((unused)) +_rtld_process_pt_note (struct link_map *l, const ElfW(Phdr) *ph) +{ + const ElfW(Nhdr) *note = (const void *) (ph->p_vaddr + l->l_addr); + _dl_process_cet_property_note (l, note, ph->p_memsz, ph->p_align); + return 0; +} + +#endif /* _DL_PROP_H */ diff --git a/sysdeps/x86/dl-tunables.list b/sysdeps/x86/dl-tunables.list index 7c3236a68f..73886b1352 100644 --- a/sysdeps/x86/dl-tunables.list +++ b/sysdeps/x86/dl-tunables.list @@ -21,6 +21,12 @@ glibc { hwcaps { type: STRING } + x86_ibt { + type: STRING + } + x86_shstk { + type: STRING + } x86_non_temporal_threshold { type: SIZE_T } diff --git a/sysdeps/x86/libc-start.h b/sysdeps/x86/libc-start.h new file mode 100644 index 0000000000..6f44262bf4 --- /dev/null +++ b/sysdeps/x86/libc-start.h @@ -0,0 +1,25 @@ +/* X86 definitions for libc main startup. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef SHARED +# define ARCH_SETUP_IREL() apply_irel () +# define ARCH_APPLY_IREL() +# ifndef ARCH_SETUP_TLS +# define ARCH_SETUP_TLS() __libc_setup_tls () +# endif +#endif /* !SHARED */ diff --git a/sysdeps/x86/link_map.h b/sysdeps/x86/link_map.h new file mode 100644 index 0000000000..ef1206a9d2 --- /dev/null +++ b/sysdeps/x86/link_map.h @@ -0,0 +1,26 @@ +/* Additional fields in struct link_map. Linux/x86 version. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +/* If this object is enabled with CET. */ +enum + { + lc_none = 0, /* Not enabled with CET. */ + lc_ibt = 1 << 0, /* Enabled with IBT. */ + lc_shstk = 1 << 1, /* Enabled with STSHK. */ + lc_ibt_and_shstk = lc_ibt | lc_shstk /* Enabled with both. */ + } l_cet:2; diff --git a/sysdeps/x86/sysdep.h b/sysdeps/x86/sysdep.h index 976566aa37..8776ad8374 100644 --- a/sysdeps/x86/sysdep.h +++ b/sysdeps/x86/sysdep.h @@ -52,6 +52,13 @@ enum cf_protection_level /* Syntactic details of assembler. */ +#ifdef _CET_ENDBR +# define _CET_NOTRACK notrack +#else +# define _CET_ENDBR +# define _CET_NOTRACK +#endif + /* ELF uses byte-counts for .align, most others use log2 of count of bytes. */ #define ALIGNARG(log2) 1<<log2 #define ASM_SIZE_DIRECTIVE(name) .size name,.-name; @@ -63,6 +70,7 @@ enum cf_protection_level .align ALIGNARG(4); \ C_LABEL(name) \ cfi_startproc; \ + _CET_ENDBR; \ CALL_MCOUNT #undef END diff --git a/sysdeps/x86_64/dl-trampoline.h b/sysdeps/x86_64/dl-trampoline.h index 298cfb3d99..a28b1e73a4 100644 --- a/sysdeps/x86_64/dl-trampoline.h +++ b/sysdeps/x86_64/dl-trampoline.h @@ -64,6 +64,7 @@ cfi_startproc _dl_runtime_resolve: cfi_adjust_cfa_offset(16) # Incorporate PLT + _CET_ENDBR # if DL_RUNTIME_RESOLVE_REALIGN_STACK # if LOCAL_STORAGE_AREA != 8 # error LOCAL_STORAGE_AREA must be 8 @@ -168,6 +169,7 @@ _dl_runtime_resolve: _dl_runtime_profile: cfi_startproc cfi_adjust_cfa_offset(16) # Incorporate PLT + _CET_ENDBR /* The La_x86_64_regs data structure pointed to by the fourth paramater must be VEC_SIZE-byte aligned. This must be explicitly enforced. We have the set up a dynamically |