From a1bbee9fd17a84d4b550f8405d5e4d31ff24f87d Mon Sep 17 00:00:00 2001 From: "H.J. Lu" <hjl.tools@gmail.com> Date: Tue, 9 Jan 2024 12:23:25 -0800 Subject: x86-64/cet: Move dl-cet.[ch] to x86_64 directories Since CET is only enabled for x86-64, move dl-cet.[ch] to x86_64 directories. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> --- sysdeps/unix/sysv/linux/x86/dl-cet.h | 63 ------ sysdeps/unix/sysv/linux/x86_64/dl-cet.h | 47 ++++- sysdeps/x86/dl-cet.c | 364 -------------------------------- sysdeps/x86_64/dl-cet.c | 364 ++++++++++++++++++++++++++++++++ 4 files changed, 410 insertions(+), 428 deletions(-) delete mode 100644 sysdeps/unix/sysv/linux/x86/dl-cet.h delete mode 100644 sysdeps/x86/dl-cet.c create mode 100644 sysdeps/x86_64/dl-cet.c (limited to 'sysdeps') diff --git a/sysdeps/unix/sysv/linux/x86/dl-cet.h b/sysdeps/unix/sysv/linux/x86/dl-cet.h deleted file mode 100644 index c10773a..0000000 --- a/sysdeps/unix/sysv/linux/x86/dl-cet.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Linux/x86 CET initializers function. - Copyright (C) 2018-2024 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 - <https://www.gnu.org/licenses/>. */ - -#include <sys/prctl.h> -#include <asm/prctl.h> - -static __always_inline int -dl_cet_disable_cet (unsigned int cet_feature) -{ - if (cet_feature != GNU_PROPERTY_X86_FEATURE_1_SHSTK) - return -1; - long long int kernel_feature = ARCH_SHSTK_SHSTK; - return (int) INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_SHSTK_DISABLE, - kernel_feature); -} - -static __always_inline int -dl_cet_lock_cet (unsigned int cet_feature) -{ - if (cet_feature != GNU_PROPERTY_X86_FEATURE_1_SHSTK) - return -1; - /* Lock all SHSTK features. */ - long long int kernel_feature = -1; - return (int) INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_SHSTK_LOCK, - kernel_feature); -} - -static __always_inline unsigned int -dl_cet_get_cet_status (void) -{ - unsigned long long kernel_feature; - unsigned int status = 0; - if (INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_SHSTK_STATUS, - &kernel_feature) == 0) - { - if ((kernel_feature & ARCH_SHSTK_SHSTK) != 0) - status = GNU_PROPERTY_X86_FEATURE_1_SHSTK; - } - return status; -} - -/* Enable shadow stack with a macro to avoid shadow stack underflow. */ -#define ENABLE_X86_CET(cet_feature) \ - if ((cet_feature & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) \ - { \ - long long int kernel_feature = ARCH_SHSTK_SHSTK; \ - INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_SHSTK_ENABLE, \ - kernel_feature); \ - } diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-cet.h b/sysdeps/unix/sysv/linux/x86_64/dl-cet.h index c89dd6b..94e9b9a 100644 --- a/sysdeps/unix/sysv/linux/x86_64/dl-cet.h +++ b/sysdeps/unix/sysv/linux/x86_64/dl-cet.h @@ -15,8 +15,53 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ +#include <sys/prctl.h> +#include <asm/prctl.h> #include <cpu-features-offsets.h> -#include_next <dl-cet.h> + +static __always_inline int +dl_cet_disable_cet (unsigned int cet_feature) +{ + if (cet_feature != GNU_PROPERTY_X86_FEATURE_1_SHSTK) + return -1; + long long int kernel_feature = ARCH_SHSTK_SHSTK; + return (int) INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_SHSTK_DISABLE, + kernel_feature); +} + +static __always_inline int +dl_cet_lock_cet (unsigned int cet_feature) +{ + if (cet_feature != GNU_PROPERTY_X86_FEATURE_1_SHSTK) + return -1; + /* Lock all SHSTK features. */ + long long int kernel_feature = -1; + return (int) INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_SHSTK_LOCK, + kernel_feature); +} + +static __always_inline unsigned int +dl_cet_get_cet_status (void) +{ + unsigned long long kernel_feature; + unsigned int status = 0; + if (INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_SHSTK_STATUS, + &kernel_feature) == 0) + { + if ((kernel_feature & ARCH_SHSTK_SHSTK) != 0) + status = GNU_PROPERTY_X86_FEATURE_1_SHSTK; + } + return status; +} + +/* Enable shadow stack with a macro to avoid shadow stack underflow. */ +#define ENABLE_X86_CET(cet_feature) \ + if ((cet_feature & GNU_PROPERTY_X86_FEATURE_1_SHSTK)) \ + { \ + long long int kernel_feature = ARCH_SHSTK_SHSTK; \ + INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_SHSTK_ENABLE, \ + kernel_feature); \ + } #define X86_STRINGIFY_1(x) #x #define X86_STRINGIFY(x) X86_STRINGIFY_1 (x) diff --git a/sysdeps/x86/dl-cet.c b/sysdeps/x86/dl-cet.c deleted file mode 100644 index d1d42ab..0000000 --- a/sysdeps/x86/dl-cet.c +++ /dev/null @@ -1,364 +0,0 @@ -/* x86 CET initializers function. - Copyright (C) 2018-2024 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 - <https://www.gnu.org/licenses/>. */ - -#include <unistd.h> -#include <errno.h> -#include <libintl.h> -#include <ldsodefs.h> -#include <dl-cet.h> -#include <sys/single_threaded.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 - -struct dl_cet_info -{ - const char *program; - - /* Check how IBT and SHSTK should be enabled. */ - enum dl_x86_cet_control enable_ibt_type; - enum dl_x86_cet_control enable_shstk_type; - - /* If IBT and SHSTK were previously enabled. */ - unsigned int feature_1_enabled; - - /* If IBT and SHSTK should be enabled. */ - unsigned int enable_feature_1; - - /* If there are any legacy shared object. */ - unsigned int feature_1_legacy; - - /* Which shared object is the first legacy shared object. */ - unsigned int feature_1_legacy_ibt; - unsigned int feature_1_legacy_shstk; -}; - -/* Check if the object M and its dependencies are legacy object. */ - -static void -dl_check_legacy_object (struct link_map *m, - struct dl_cet_info *info) -{ - unsigned int i; - struct link_map *l = NULL; - - 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 check for ld.so since it has the features enabled. The - features will be disabled later if they are not enabled in - executable. */ - if (l == &GL(dl_rtld_map) - || l->l_real == &GL(dl_rtld_map) - || (info->program != NULL && l == m)) - continue; -#endif - - /* IBT and SHSTK set only if enabled in executable and all DSOs. - NB: cet_always_on is handled outside of the loop. */ - info->enable_feature_1 &= ((l->l_x86_feature_1_and - & (GNU_PROPERTY_X86_FEATURE_1_IBT - | GNU_PROPERTY_X86_FEATURE_1_SHSTK)) - | ~(GNU_PROPERTY_X86_FEATURE_1_IBT - | GNU_PROPERTY_X86_FEATURE_1_SHSTK)); - if ((info->feature_1_legacy - & GNU_PROPERTY_X86_FEATURE_1_IBT) == 0 - && ((info->enable_feature_1 - & GNU_PROPERTY_X86_FEATURE_1_IBT) - != (info->feature_1_enabled - & GNU_PROPERTY_X86_FEATURE_1_IBT))) - { - info->feature_1_legacy_ibt = i; - info->feature_1_legacy |= GNU_PROPERTY_X86_FEATURE_1_IBT; - } - - if ((info->feature_1_legacy - & GNU_PROPERTY_X86_FEATURE_1_SHSTK) == 0 - && ((info->enable_feature_1 - & GNU_PROPERTY_X86_FEATURE_1_SHSTK) - != (info->feature_1_enabled - & GNU_PROPERTY_X86_FEATURE_1_SHSTK))) - { - info->feature_1_legacy_shstk = i; - info->feature_1_legacy |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; - } - } - - /* Handle cet_always_on. */ - if ((info->feature_1_enabled - & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0 - && info->enable_ibt_type == cet_always_on) - { - info->feature_1_legacy &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; - info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT; - } - - if ((info->feature_1_enabled - & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0 - && info->enable_shstk_type == cet_always_on) - { - info->feature_1_legacy &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK; - info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; - } -} - -#ifdef SHARED -/* Enable IBT and SHSTK only if they are enabled in executable. Set - feature bits properly at the start of the program. */ - -static void -dl_cet_check_startup (struct link_map *m, struct dl_cet_info *info) -{ - /* NB: IBT and SHSTK may be disabled by environment variable: - - GLIBC_TUNABLES=glibc.cpu.hwcaps=-IBT,-SHSTK. - */ - if (CPU_FEATURE_USABLE (IBT)) - { - if (info->enable_ibt_type == cet_always_on) - info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT; - else - info->enable_feature_1 &= ((m->l_x86_feature_1_and - & GNU_PROPERTY_X86_FEATURE_1_IBT) - | ~GNU_PROPERTY_X86_FEATURE_1_IBT); - } - else - info->enable_feature_1 &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; - - if (CPU_FEATURE_USABLE (SHSTK)) - { - if (info->enable_shstk_type == cet_always_on) - info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; - else - info->enable_feature_1 &= ((m->l_x86_feature_1_and - & GNU_PROPERTY_X86_FEATURE_1_SHSTK) - | ~GNU_PROPERTY_X86_FEATURE_1_SHSTK); - } - else - info->enable_feature_1 &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK; - - if (info->enable_feature_1 != 0) - dl_check_legacy_object (m, info); - - unsigned int disable_feature_1 - = info->enable_feature_1 ^ info->feature_1_enabled; - if (disable_feature_1 != 0) - { - /* Clear the disabled bits. Sync dl_x86_feature_1 and - info->feature_1_enabled with info->enable_feature_1. */ - info->feature_1_enabled = info->enable_feature_1; - GL(dl_x86_feature_1) = info->enable_feature_1; - } -} -#endif - -/* Check feature bits when dlopening the shared object M. */ - -static void -dl_cet_check_dlopen (struct link_map *m, struct dl_cet_info *info) -{ - /* Check if there are any legacy objects loaded. */ - if (info->enable_feature_1 != 0) - { - dl_check_legacy_object (m, info); - - /* Skip if there are no legacy shared objects loaded. */ - if (info->feature_1_legacy == 0) - return; - } - - unsigned int disable_feature_1 = 0; - unsigned int legacy_obj = 0; - const char *msg = NULL; - - if ((info->feature_1_enabled - & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0 - && (info->feature_1_legacy - & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0) - { - /* Don't disable IBT if not single threaded since IBT may be still - enabled in other threads. */ - if (info->enable_ibt_type != cet_permissive - || !SINGLE_THREAD_P) - { - legacy_obj = info->feature_1_legacy_ibt; - msg = N_("rebuild shared object with IBT support enabled"); - } - else - disable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT; - } - - /* Check the next feature only if there is no error. */ - if (msg == NULL - && (info->feature_1_enabled - & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0 - && (info->feature_1_legacy - & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0) - { - /* Don't disable SHSTK if not single threaded since SHSTK may be - still enabled in other threads. */ - if (info->enable_shstk_type != cet_permissive - || !SINGLE_THREAD_P) - { - legacy_obj = info->feature_1_legacy_shstk; - msg = N_("rebuild shared object with SHSTK support enabled"); - } - else - disable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; - } - - /* If there is an error, long jump back to the caller. */ - if (msg != NULL) - _dl_signal_error (0, m->l_initfini[legacy_obj]->l_name, "dlopen", - msg); - - if (disable_feature_1 != 0) - { - int res = dl_cet_disable_cet (disable_feature_1); - if (res) - { - if ((disable_feature_1 - & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0) - msg = N_("can't disable IBT"); - else - msg = N_("can't disable SHSTK"); - /* Long jump back to the caller on error. */ - _dl_signal_error (-res, m->l_initfini[legacy_obj]->l_name, - "dlopen", msg); - } - - /* Clear the disabled bits in dl_x86_feature_1. */ - GL(dl_x86_feature_1) &= ~disable_feature_1; - - THREAD_SETMEM (THREAD_SELF, header.feature_1, - GL(dl_x86_feature_1)); - } -} - -static void -dl_cet_check (struct link_map *m, const char *program) -{ - struct dl_cet_info info; - - /* CET is enabled only if RTLD_START_ENABLE_X86_FEATURES is defined. */ -#if defined SHARED && defined RTLD_START_ENABLE_X86_FEATURES - /* Set dl_x86_feature_1 to features enabled in the executable. */ - if (program != NULL) - GL(dl_x86_feature_1) = (m->l_x86_feature_1_and - & (X86_FEATURE_1_IBT - | X86_FEATURE_1_SHSTK)); -#endif - - /* Check how IBT and SHSTK should be enabled. */ - info.enable_ibt_type = GL(dl_x86_feature_control).ibt; - info.enable_shstk_type = GL(dl_x86_feature_control).shstk; - - info.feature_1_enabled = GL(dl_x86_feature_1); - - /* No legacy object check if IBT and SHSTK are always on. */ - if (info.enable_ibt_type == cet_always_on - && info.enable_shstk_type == cet_always_on) - return; - - /* Check if IBT and SHSTK were enabled. */ - if (info.feature_1_enabled == 0) - return; - - info.program = program; - - /* Check which features should be enabled. */ - info.enable_feature_1 = 0; - if (info.enable_ibt_type != cet_always_off) - info.enable_feature_1 |= (info.feature_1_enabled - & GNU_PROPERTY_X86_FEATURE_1_IBT); - if (info.enable_shstk_type != cet_always_off) - info.enable_feature_1 |= (info.feature_1_enabled - & GNU_PROPERTY_X86_FEATURE_1_SHSTK); - - /* Start with no legacy objects. */ - info.feature_1_legacy = 0; - info.feature_1_legacy_ibt = 0; - info.feature_1_legacy_shstk = 0; - -#ifdef SHARED - if (program) - dl_cet_check_startup (m, &info); - else -#endif - dl_cet_check_dlopen (m, &info); -} - -void -_dl_cet_open_check (struct link_map *l) -{ - dl_cet_check (l, NULL); -} - -/* Set GL(dl_x86_feature_1) to the enabled features and clear the - active bits of the disabled features. */ - -attribute_hidden void -_dl_cet_setup_features (unsigned int cet_feature) -{ - /* NB: cet_feature == GL(dl_x86_feature_1) which is set to features - enabled from executable, not necessarily supported by kernel. */ - if (cet_feature != 0) - { - cet_feature = dl_cet_get_cet_status (); - if (cet_feature != 0) - { - THREAD_SETMEM (THREAD_SELF, header.feature_1, cet_feature); - - /* Lock CET if IBT or SHSTK is enabled in executable. Don't - lock CET if IBT or SHSTK is enabled permissively. */ - if (GL(dl_x86_feature_control).ibt != cet_permissive - && (GL(dl_x86_feature_control).shstk != cet_permissive)) - dl_cet_lock_cet (cet_feature); - } - /* Sync GL(dl_x86_feature_1) with kernel. */ - GL(dl_x86_feature_1) = cet_feature; - } -} - -#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_64/dl-cet.c b/sysdeps/x86_64/dl-cet.c new file mode 100644 index 0000000..1297c09 --- /dev/null +++ b/sysdeps/x86_64/dl-cet.c @@ -0,0 +1,364 @@ +/* x86-64 CET initializers function. + Copyright (C) 2018-2024 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 + <https://www.gnu.org/licenses/>. */ + +#include <unistd.h> +#include <errno.h> +#include <libintl.h> +#include <ldsodefs.h> +#include <dl-cet.h> +#include <sys/single_threaded.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 + +struct dl_cet_info +{ + const char *program; + + /* Check how IBT and SHSTK should be enabled. */ + enum dl_x86_cet_control enable_ibt_type; + enum dl_x86_cet_control enable_shstk_type; + + /* If IBT and SHSTK were previously enabled. */ + unsigned int feature_1_enabled; + + /* If IBT and SHSTK should be enabled. */ + unsigned int enable_feature_1; + + /* If there are any legacy shared object. */ + unsigned int feature_1_legacy; + + /* Which shared object is the first legacy shared object. */ + unsigned int feature_1_legacy_ibt; + unsigned int feature_1_legacy_shstk; +}; + +/* Check if the object M and its dependencies are legacy object. */ + +static void +dl_check_legacy_object (struct link_map *m, + struct dl_cet_info *info) +{ + unsigned int i; + struct link_map *l = NULL; + + 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 check for ld.so since it has the features enabled. The + features will be disabled later if they are not enabled in + executable. */ + if (l == &GL(dl_rtld_map) + || l->l_real == &GL(dl_rtld_map) + || (info->program != NULL && l == m)) + continue; +#endif + + /* IBT and SHSTK set only if enabled in executable and all DSOs. + NB: cet_always_on is handled outside of the loop. */ + info->enable_feature_1 &= ((l->l_x86_feature_1_and + & (GNU_PROPERTY_X86_FEATURE_1_IBT + | GNU_PROPERTY_X86_FEATURE_1_SHSTK)) + | ~(GNU_PROPERTY_X86_FEATURE_1_IBT + | GNU_PROPERTY_X86_FEATURE_1_SHSTK)); + if ((info->feature_1_legacy + & GNU_PROPERTY_X86_FEATURE_1_IBT) == 0 + && ((info->enable_feature_1 + & GNU_PROPERTY_X86_FEATURE_1_IBT) + != (info->feature_1_enabled + & GNU_PROPERTY_X86_FEATURE_1_IBT))) + { + info->feature_1_legacy_ibt = i; + info->feature_1_legacy |= GNU_PROPERTY_X86_FEATURE_1_IBT; + } + + if ((info->feature_1_legacy + & GNU_PROPERTY_X86_FEATURE_1_SHSTK) == 0 + && ((info->enable_feature_1 + & GNU_PROPERTY_X86_FEATURE_1_SHSTK) + != (info->feature_1_enabled + & GNU_PROPERTY_X86_FEATURE_1_SHSTK))) + { + info->feature_1_legacy_shstk = i; + info->feature_1_legacy |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; + } + } + + /* Handle cet_always_on. */ + if ((info->feature_1_enabled + & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0 + && info->enable_ibt_type == cet_always_on) + { + info->feature_1_legacy &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; + info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT; + } + + if ((info->feature_1_enabled + & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0 + && info->enable_shstk_type == cet_always_on) + { + info->feature_1_legacy &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK; + info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; + } +} + +#ifdef SHARED +/* Enable IBT and SHSTK only if they are enabled in executable. Set + feature bits properly at the start of the program. */ + +static void +dl_cet_check_startup (struct link_map *m, struct dl_cet_info *info) +{ + /* NB: IBT and SHSTK may be disabled by environment variable: + + GLIBC_TUNABLES=glibc.cpu.hwcaps=-IBT,-SHSTK. + */ + if (CPU_FEATURE_USABLE (IBT)) + { + if (info->enable_ibt_type == cet_always_on) + info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT; + else + info->enable_feature_1 &= ((m->l_x86_feature_1_and + & GNU_PROPERTY_X86_FEATURE_1_IBT) + | ~GNU_PROPERTY_X86_FEATURE_1_IBT); + } + else + info->enable_feature_1 &= ~GNU_PROPERTY_X86_FEATURE_1_IBT; + + if (CPU_FEATURE_USABLE (SHSTK)) + { + if (info->enable_shstk_type == cet_always_on) + info->enable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; + else + info->enable_feature_1 &= ((m->l_x86_feature_1_and + & GNU_PROPERTY_X86_FEATURE_1_SHSTK) + | ~GNU_PROPERTY_X86_FEATURE_1_SHSTK); + } + else + info->enable_feature_1 &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK; + + if (info->enable_feature_1 != 0) + dl_check_legacy_object (m, info); + + unsigned int disable_feature_1 + = info->enable_feature_1 ^ info->feature_1_enabled; + if (disable_feature_1 != 0) + { + /* Clear the disabled bits. Sync dl_x86_feature_1 and + info->feature_1_enabled with info->enable_feature_1. */ + info->feature_1_enabled = info->enable_feature_1; + GL(dl_x86_feature_1) = info->enable_feature_1; + } +} +#endif + +/* Check feature bits when dlopening the shared object M. */ + +static void +dl_cet_check_dlopen (struct link_map *m, struct dl_cet_info *info) +{ + /* Check if there are any legacy objects loaded. */ + if (info->enable_feature_1 != 0) + { + dl_check_legacy_object (m, info); + + /* Skip if there are no legacy shared objects loaded. */ + if (info->feature_1_legacy == 0) + return; + } + + unsigned int disable_feature_1 = 0; + unsigned int legacy_obj = 0; + const char *msg = NULL; + + if ((info->feature_1_enabled + & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0 + && (info->feature_1_legacy + & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0) + { + /* Don't disable IBT if not single threaded since IBT may be still + enabled in other threads. */ + if (info->enable_ibt_type != cet_permissive + || !SINGLE_THREAD_P) + { + legacy_obj = info->feature_1_legacy_ibt; + msg = N_("rebuild shared object with IBT support enabled"); + } + else + disable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_IBT; + } + + /* Check the next feature only if there is no error. */ + if (msg == NULL + && (info->feature_1_enabled + & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0 + && (info->feature_1_legacy + & GNU_PROPERTY_X86_FEATURE_1_SHSTK) != 0) + { + /* Don't disable SHSTK if not single threaded since SHSTK may be + still enabled in other threads. */ + if (info->enable_shstk_type != cet_permissive + || !SINGLE_THREAD_P) + { + legacy_obj = info->feature_1_legacy_shstk; + msg = N_("rebuild shared object with SHSTK support enabled"); + } + else + disable_feature_1 |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; + } + + /* If there is an error, long jump back to the caller. */ + if (msg != NULL) + _dl_signal_error (0, m->l_initfini[legacy_obj]->l_name, "dlopen", + msg); + + if (disable_feature_1 != 0) + { + int res = dl_cet_disable_cet (disable_feature_1); + if (res) + { + if ((disable_feature_1 + & GNU_PROPERTY_X86_FEATURE_1_IBT) != 0) + msg = N_("can't disable IBT"); + else + msg = N_("can't disable SHSTK"); + /* Long jump back to the caller on error. */ + _dl_signal_error (-res, m->l_initfini[legacy_obj]->l_name, + "dlopen", msg); + } + + /* Clear the disabled bits in dl_x86_feature_1. */ + GL(dl_x86_feature_1) &= ~disable_feature_1; + + THREAD_SETMEM (THREAD_SELF, header.feature_1, + GL(dl_x86_feature_1)); + } +} + +static void +dl_cet_check (struct link_map *m, const char *program) +{ + struct dl_cet_info info; + + /* CET is enabled only if RTLD_START_ENABLE_X86_FEATURES is defined. */ +#if defined SHARED && defined RTLD_START_ENABLE_X86_FEATURES + /* Set dl_x86_feature_1 to features enabled in the executable. */ + if (program != NULL) + GL(dl_x86_feature_1) = (m->l_x86_feature_1_and + & (X86_FEATURE_1_IBT + | X86_FEATURE_1_SHSTK)); +#endif + + /* Check how IBT and SHSTK should be enabled. */ + info.enable_ibt_type = GL(dl_x86_feature_control).ibt; + info.enable_shstk_type = GL(dl_x86_feature_control).shstk; + + info.feature_1_enabled = GL(dl_x86_feature_1); + + /* No legacy object check if IBT and SHSTK are always on. */ + if (info.enable_ibt_type == cet_always_on + && info.enable_shstk_type == cet_always_on) + return; + + /* Check if IBT and SHSTK were enabled. */ + if (info.feature_1_enabled == 0) + return; + + info.program = program; + + /* Check which features should be enabled. */ + info.enable_feature_1 = 0; + if (info.enable_ibt_type != cet_always_off) + info.enable_feature_1 |= (info.feature_1_enabled + & GNU_PROPERTY_X86_FEATURE_1_IBT); + if (info.enable_shstk_type != cet_always_off) + info.enable_feature_1 |= (info.feature_1_enabled + & GNU_PROPERTY_X86_FEATURE_1_SHSTK); + + /* Start with no legacy objects. */ + info.feature_1_legacy = 0; + info.feature_1_legacy_ibt = 0; + info.feature_1_legacy_shstk = 0; + +#ifdef SHARED + if (program) + dl_cet_check_startup (m, &info); + else +#endif + dl_cet_check_dlopen (m, &info); +} + +void +_dl_cet_open_check (struct link_map *l) +{ + dl_cet_check (l, NULL); +} + +/* Set GL(dl_x86_feature_1) to the enabled features and clear the + active bits of the disabled features. */ + +attribute_hidden void +_dl_cet_setup_features (unsigned int cet_feature) +{ + /* NB: cet_feature == GL(dl_x86_feature_1) which is set to features + enabled from executable, not necessarily supported by kernel. */ + if (cet_feature != 0) + { + cet_feature = dl_cet_get_cet_status (); + if (cet_feature != 0) + { + THREAD_SETMEM (THREAD_SELF, header.feature_1, cet_feature); + + /* Lock CET if IBT or SHSTK is enabled in executable. Don't + lock CET if IBT or SHSTK is enabled permissively. */ + if (GL(dl_x86_feature_control).ibt != cet_permissive + && (GL(dl_x86_feature_control).shstk != cet_permissive)) + dl_cet_lock_cet (cet_feature); + } + /* Sync GL(dl_x86_feature_1) with kernel. */ + GL(dl_x86_feature_1) = cet_feature; + } +} + +#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 */ -- cgit v1.1