| commit 1fabdb99084df004f7f4cdc7068d1be209a258be |
| Author: H.J. Lu <hjl.tools@gmail.com> |
| Date: Wed Mar 18 04:35:54 2020 -0700 |
| |
| x86: Remove ARCH_CET_LEGACY_BITMAP [BZ #25397] |
| |
| Since legacy bitmap doesn't cover jitted code generated by legacy JIT |
| engine, it isn't very useful. This patch removes ARCH_CET_LEGACY_BITMAP |
| and treats indirect branch tracking similar to shadow stack by removing |
| legacy bitmap support. |
| |
| Tested on CET Linux/x86-64 and non-CET Linux/x86-64. |
| |
| Reviewed-by: Carlos O'Donell <carlos@redhat.com> |
| |
| |
| diff --git a/sysdeps/unix/sysv/linux/x86/dl-cet.h b/sysdeps/unix/sysv/linux/x86/dl-cet.h |
| index 3fbcfeb..47c52e6 100644 |
| |
| |
| @@ -19,27 +19,6 @@ |
| #include <asm/prctl.h> |
| |
| static inline int __attribute__ ((always_inline)) |
| -dl_cet_allocate_legacy_bitmap (unsigned long *legacy_bitmap) |
| -{ |
| - /* Allocate legacy bitmap. */ |
| - INTERNAL_SYSCALL_DECL (err); |
| -#ifdef __LP64__ |
| - return (int) INTERNAL_SYSCALL (arch_prctl, err, 2, |
| - ARCH_CET_LEGACY_BITMAP, legacy_bitmap); |
| -#else |
| - unsigned long long legacy_bitmap_u64[2]; |
| - int res = INTERNAL_SYSCALL (arch_prctl, err, 2, |
| - ARCH_CET_LEGACY_BITMAP, legacy_bitmap_u64); |
| - if (res == 0) |
| - { |
| - legacy_bitmap[0] = legacy_bitmap_u64[0]; |
| - legacy_bitmap[1] = legacy_bitmap_u64[1]; |
| - } |
| - return res; |
| -#endif |
| -} |
| - |
| -static inline int __attribute__ ((always_inline)) |
| dl_cet_disable_cet (unsigned int cet_feature) |
| { |
| INTERNAL_SYSCALL_DECL (err); |
| diff --git a/sysdeps/unix/sysv/linux/x86/include/asm/prctl.h b/sysdeps/unix/sysv/linux/x86/include/asm/prctl.h |
| index f67f329..45ad0b0 100644 |
| |
| |
| @@ -24,9 +24,4 @@ |
| OUT: allocated shadow stack address: *addr. |
| */ |
| # define ARCH_CET_ALLOC_SHSTK 0x3004 |
| -/* Return legacy region bitmap info in unsigned long long *addr: |
| - address: addr[0]. |
| - size: addr[1]. |
| - */ |
| -# define ARCH_CET_LEGACY_BITMAP 0x3005 |
| #endif /* ARCH_CET_STATUS */ |
| diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile |
| index 43ad4a7..d9bdf0b 100644 |
| |
| |
| @@ -20,7 +20,8 @@ sysdep-dl-routines += dl-cet |
| |
| tests += tst-cet-legacy-1 tst-cet-legacy-2 tst-cet-legacy-2a \ |
| tst-cet-legacy-3 tst-cet-legacy-4 \ |
| - tst-cet-legacy-5a tst-cet-legacy-6a |
| + tst-cet-legacy-5a tst-cet-legacy-6a tst-cet-legacy-7 \ |
| + tst-cet-legacy-8 |
| ifneq (no,$(have-tunables)) |
| tests += tst-cet-legacy-4a tst-cet-legacy-4b tst-cet-legacy-4c \ |
| tst-cet-legacy-5b tst-cet-legacy-6b |
| @@ -42,14 +43,16 @@ CFLAGS-tst-cet-legacy-4b.c += -fcf-protection |
| CFLAGS-tst-cet-legacy-mod-4.c += -fcf-protection=none |
| CFLAGS-tst-cet-legacy-5a.c += -fcf-protection |
| CFLAGS-tst-cet-legacy-5b.c += -fcf-protection |
| -CFLAGS-tst-cet-legacy-mod-5a.c += -fcf-protection=none |
| +CFLAGS-tst-cet-legacy-mod-5a.c += -fcf-protection=branch |
| CFLAGS-tst-cet-legacy-mod-5b.c += -fcf-protection |
| CFLAGS-tst-cet-legacy-mod-5c.c += -fcf-protection |
| CFLAGS-tst-cet-legacy-6a.c += -fcf-protection |
| CFLAGS-tst-cet-legacy-6b.c += -fcf-protection |
| -CFLAGS-tst-cet-legacy-mod-6a.c += -fcf-protection=none |
| +CFLAGS-tst-cet-legacy-mod-6a.c += -fcf-protection=branch |
| CFLAGS-tst-cet-legacy-mod-6b.c += -fcf-protection |
| CFLAGS-tst-cet-legacy-mod-6c.c += -fcf-protection |
| +CFLAGS-tst-cet-legacy-7.c += -fcf-protection=none |
| +CFLAGS-tst-cet-legacy-8.c += -mshstk |
| |
| $(objpfx)tst-cet-legacy-1: $(objpfx)tst-cet-legacy-mod-1.so \ |
| $(objpfx)tst-cet-legacy-mod-2.so |
| diff --git a/sysdeps/x86/dl-cet.c b/sysdeps/x86/dl-cet.c |
| index b82ba14..627d937 100644 |
| |
| |
| @@ -33,63 +33,6 @@ |
| # 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 |
| @@ -117,6 +60,8 @@ dl_cet_check (struct link_map *m, const char *program) |
| if (ibt_enabled || shstk_enabled) |
| { |
| struct link_map *l = NULL; |
| + unsigned int ibt_legacy = 0, shstk_legacy = 0; |
| + bool found_ibt_legacy = false, found_shstk_legacy = false; |
| |
| /* Check if IBT and SHSTK are enabled in object. */ |
| bool enable_ibt = (ibt_enabled |
| @@ -142,10 +87,7 @@ dl_cet_check (struct link_map *m, const char *program) |
| 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) |
| @@ -167,91 +109,25 @@ dl_cet_check (struct link_map *m, const char *program) |
| continue; |
| #endif |
| |
| - if (enable_ibt |
| - && enable_ibt_type != CET_ALWAYS_ON |
| - && !(l->l_cet & lc_ibt)) |
| + /* IBT is enabled only if it is enabled in executable as |
| + well as all shared objects. */ |
| + enable_ibt &= (enable_ibt_type == CET_ALWAYS_ON |
| + || (l->l_cet & lc_ibt) != 0); |
| + if (!found_ibt_legacy && enable_ibt != ibt_enabled) |
| { |
| - /* Remember the first and last legacy objects. */ |
| - if (!need_legacy_bitmap) |
| - last_legacy = i; |
| - first_legacy = i; |
| - need_legacy_bitmap = true; |
| + found_ibt_legacy = true; |
| + ibt_legacy = i; |
| } |
| |
| /* 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 |
| + if (enable_shstk != shstk_enabled) |
| { |
| - /* 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")); |
| - } |
| + found_shstk_legacy = true; |
| + shstk_legacy = i; |
| } |
| - |
| - /* 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; |
| } |
| } |
| |
| @@ -259,23 +135,40 @@ mprotect_failure: |
| |
| if (enable_ibt != ibt_enabled || enable_shstk != shstk_enabled) |
| { |
| - if (!program |
| - && enable_shstk_type != CET_PERMISSIVE) |
| + if (!program) |
| { |
| - /* 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; |
| + if (enable_ibt_type != CET_PERMISSIVE) |
| + { |
| + /* When IBT is enabled, we cannot dlopen a shared |
| + object without IBT. */ |
| + if (found_ibt_legacy) |
| + _dl_signal_error (0, |
| + m->l_initfini[ibt_legacy]->l_name, |
| + "dlopen", |
| + N_("rebuild shared object with IBT support enabled")); |
| + } |
| + |
| + if (enable_shstk_type != CET_PERMISSIVE) |
| + { |
| + /* When SHSTK is enabled, we cannot dlopen a shared |
| + object without SHSTK. */ |
| + if (found_shstk_legacy) |
| + _dl_signal_error (0, |
| + m->l_initfini[shstk_legacy]->l_name, |
| + "dlopen", |
| + N_("rebuild shared object with SHSTK support enabled")); |
| + } |
| + |
| + if (enable_ibt_type != CET_PERMISSIVE |
| + && enable_shstk_type != CET_PERMISSIVE) |
| + 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) |
| + if (!enable_ibt) |
| cet_feature |= GNU_PROPERTY_X86_FEATURE_1_IBT; |
| if (!enable_shstk) |
| cet_feature |= GNU_PROPERTY_X86_FEATURE_1_SHSTK; |
| @@ -286,8 +179,14 @@ mprotect_failure: |
| 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")); |
| + { |
| + if (found_ibt_legacy) |
| + l = m->l_initfini[ibt_legacy]; |
| + else |
| + l = m->l_initfini[shstk_legacy]; |
| + _dl_signal_error (-res, l->l_name, "dlopen", |
| + N_("can't disable CET")); |
| + } |
| } |
| |
| /* Clear the disabled bits in dl_x86_feature_1. */ |
| @@ -297,17 +196,21 @@ mprotect_failure: |
| } |
| |
| #ifdef SHARED |
| - if (program |
| - && (!shstk_enabled |
| - || enable_shstk_type != CET_PERMISSIVE) |
| - && (ibt_enabled || shstk_enabled)) |
| + if (program && (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); |
| + if ((!ibt_enabled |
| + || enable_ibt_type != CET_PERMISSIVE) |
| + && (!shstk_enabled |
| + || enable_shstk_type != CET_PERMISSIVE)) |
| + { |
| + /* Lock CET if IBT or SHSTK is enabled in executable unless |
| + IBT or SHSTK is enabled permissively. */ |
| + int res = dl_cet_lock_cet (); |
| + if (res != 0) |
| + _dl_fatal_printf ("%s: can't lock CET\n", program); |
| + } |
| |
| + /* Set feature_1 if IBT or SHSTK is enabled in executable. */ |
| cet_feature_changed = true; |
| } |
| #endif |
| diff --git a/sysdeps/x86/dl-procruntime.c b/sysdeps/x86/dl-procruntime.c |
| index eddbde6..920bfe8 100644 |
| |
| |
| @@ -54,15 +54,4 @@ PROCINFO_CLASS unsigned int _dl_x86_feature_1[2] |
| # 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/tst-cet-legacy-4.c b/sysdeps/x86/tst-cet-legacy-4.c |
| index 3ead63d..f0ba326 100644 |
| |
| |
| @@ -20,6 +20,9 @@ |
| #include <dlfcn.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| +#include <string.h> |
| + |
| +#include <support/check.h> |
| |
| static int |
| do_test (void) |
| @@ -31,22 +34,18 @@ do_test (void) |
| h = dlopen (modname, RTLD_LAZY); |
| if (h == NULL) |
| { |
| - printf ("cannot open '%s': %s\n", modname, dlerror ()); |
| - exit (1); |
| + const char *err = dlerror (); |
| + if (!strstr (err, "rebuild shared object with IBT support enabled")) |
| + FAIL_EXIT1 ("incorrect dlopen '%s' error: %s\n", modname, err); |
| + return 0; |
| } |
| |
| fp = dlsym (h, "test"); |
| if (fp == NULL) |
| - { |
| - printf ("cannot get symbol 'test': %s\n", dlerror ()); |
| - exit (1); |
| - } |
| + FAIL_EXIT1 ("cannot get symbol 'test': %s\n", dlerror ()); |
| |
| if (fp () != 0) |
| - { |
| - puts ("test () != 0"); |
| - exit (1); |
| - } |
| + FAIL_EXIT1 ("test () != 0"); |
| |
| dlclose (h); |
| |
| diff --git a/sysdeps/x86/tst-cet-legacy-5.c b/sysdeps/x86/tst-cet-legacy-5.c |
| index fbf640f..e1ca09d 100644 |
| |
| |
| @@ -35,7 +35,8 @@ do_test_1 (const char *modname, bool fail) |
| if (fail) |
| { |
| const char *err = dlerror (); |
| - if (strstr (err, "shadow stack isn't enabled") == NULL) |
| + if (strstr (err, "rebuild shared object with SHSTK support enabled") |
| + == NULL) |
| { |
| printf ("incorrect dlopen '%s' error: %s\n", modname, |
| dlerror ()); |
| diff --git a/sysdeps/x86/tst-cet-legacy-6.c b/sysdeps/x86/tst-cet-legacy-6.c |
| index 9151225..184a35b 100644 |
| |
| |
| @@ -35,7 +35,8 @@ do_test_1 (const char *modname, bool fail) |
| if (fail) |
| { |
| const char *err = dlerror (); |
| - if (strstr (err, "shadow stack isn't enabled") == NULL) |
| + if (strstr (err, "rebuild shared object with SHSTK support enabled") |
| + == NULL) |
| { |
| printf ("incorrect dlopen '%s' error: %s\n", modname, |
| dlerror ()); |
| diff --git a/sysdeps/x86/tst-cet-legacy-7.c b/sysdeps/x86/tst-cet-legacy-7.c |
| new file mode 100644 |
| index 0000000..58bcb29 |
| |
| |
| @@ -0,0 +1,38 @@ |
| +/* Check compatibility of legacy executable with a JIT engine. |
| + Copyright (C) 2020 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 |
| + <https://www.gnu.org/licenses/>. */ |
| + |
| +#include <stdio.h> |
| +#include <sys/mman.h> |
| +#include <support/xunistd.h> |
| + |
| +/* Check that mmapped legacy code works with -fcf-protection=none. */ |
| + |
| +static int |
| +do_test (void) |
| +{ |
| + void (*funcp) (void); |
| + funcp = xmmap (NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, |
| + MAP_ANONYMOUS | MAP_PRIVATE, -1); |
| + printf ("mmap = %p\n", funcp); |
| + /* Write RET instruction. */ |
| + *(char *) funcp = 0xc3; |
| + funcp (); |
| + return 0; |
| +} |
| + |
| +#include <support/test-driver.c> |
| diff --git a/sysdeps/x86/tst-cet-legacy-8.c b/sysdeps/x86/tst-cet-legacy-8.c |
| new file mode 100644 |
| index 0000000..11e8115 |
| |
| |
| @@ -0,0 +1,48 @@ |
| +/* Check incompatibility with legacy JIT engine. |
| + Copyright (C) 2020 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 |
| + <https://www.gnu.org/licenses/>. */ |
| + |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <x86intrin.h> |
| +#include <sys/mman.h> |
| +#include <support/test-driver.h> |
| +#include <support/xsignal.h> |
| +#include <support/xunistd.h> |
| + |
| +/* Check that mmapped legacy code trigges segfault with -fcf-protection. */ |
| + |
| +static int |
| +do_test (void) |
| +{ |
| + /* NB: This test should trigger SIGSEGV on CET platforms. If SHSTK |
| + is disabled, assuming IBT is also disabled. */ |
| + if (_get_ssp () == 0) |
| + return EXIT_UNSUPPORTED; |
| + |
| + void (*funcp) (void); |
| + funcp = xmmap (NULL, 0x1000, PROT_EXEC | PROT_READ | PROT_WRITE, |
| + MAP_ANONYMOUS | MAP_PRIVATE, -1); |
| + printf ("mmap = %p\n", funcp); |
| + /* Write RET instruction. */ |
| + *(char *) funcp = 0xc3; |
| + funcp (); |
| + return EXIT_FAILURE; |
| +} |
| + |
| +#define EXPECTED_SIGNAL (_get_ssp () == 0 ? 0 : SIGSEGV) |
| +#include <support/test-driver.c> |
| |