x86: Remove ARCH_CET_LEGACY_BITMAP [BZ #25397]
authorH.J. Lu <hjl.tools@gmail.com>
Wed, 18 Mar 2020 11:35:54 +0000 (04:35 -0700)
committerH.J. Lu <hjl.tools@gmail.com>
Wed, 18 Mar 2020 11:35:54 +0000 (04:35 -0700)
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>
sysdeps/unix/sysv/linux/x86/dl-cet.h
sysdeps/unix/sysv/linux/x86/include/asm/prctl.h
sysdeps/x86/Makefile
sysdeps/x86/dl-cet.c
sysdeps/x86/dl-procruntime.c
sysdeps/x86/tst-cet-legacy-4.c
sysdeps/x86/tst-cet-legacy-5.c
sysdeps/x86/tst-cet-legacy-6.c
sysdeps/x86/tst-cet-legacy-7.c [new file with mode: 0644]
sysdeps/x86/tst-cet-legacy-8.c [new file with mode: 0644]

index 9b2aaa2..ae97a43 100644 (file)
 #include <asm/prctl.h>
 
 static inline int __attribute__ ((always_inline))
-dl_cet_allocate_legacy_bitmap (unsigned long *legacy_bitmap)
-{
-  /* Allocate legacy bitmap.  */
-#ifdef __LP64__
-  return (int) INTERNAL_SYSCALL_CALL (arch_prctl,
-                                     ARCH_CET_LEGACY_BITMAP, legacy_bitmap);
-#else
-  unsigned long long legacy_bitmap_u64[2];
-  int res = INTERNAL_SYSCALL_CALL (arch_prctl,
-                                  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)
 {
   return (int) INTERNAL_SYSCALL_CALL (arch_prctl, ARCH_CET_DISABLE,
index f67f329..45ad0b0 100644 (file)
@@ -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 */
index 95182a5..4ffa699 100644 (file)
@@ -20,7 +20,8 @@ sysdep-dl-routines += dl-cet
 
 tests += tst-cet-legacy-1 tst-cet-legacy-1a 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
 tst-cet-legacy-1a-ARGS = -- $(host-test-program-cmd)
 ifneq (no,$(have-tunables))
 tests += tst-cet-legacy-4a tst-cet-legacy-4b tst-cet-legacy-4c \
@@ -43,14 +44,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
index ca3b584..c7029f1 100644 (file)
 # 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
index fb36801..5e39a38 100644 (file)
@@ -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
index a77078a..ee54b87 100644 (file)
@@ -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);
 
index 6c9bba0..e2e95b6 100644 (file)
@@ -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,
                      err);
index 877e777..bdbbb90 100644 (file)
@@ -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,
                      err);
diff --git a/sysdeps/x86/tst-cet-legacy-7.c b/sysdeps/x86/tst-cet-legacy-7.c
new file mode 100644 (file)
index 0000000..58bcb29
--- /dev/null
@@ -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 (file)
index 0000000..11e8115
--- /dev/null
@@ -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>