[03/16] Do not stack-protect ifunc resolvers.

Message ID 1456677695-29778-4-git-send-email-nix@esperi.org.uk
State New, archived
Headers

Commit Message

Nix Feb. 28, 2016, 4:41 p.m. UTC
  From: Nick Alcock <nick.alcock@oracle.com>

When dynamically linking, ifunc resolvers are called before TLS is
initialized, so they cannot be safely stack-protected.

We avoid disabling stack-protection on large numbers of files by
depending on the availability of __attribute__ ((__optimize__
("-fno-stack-protector"))), and turning it off just for
the resolvers themselves.  (We provide the attribute even when
statically linking, because we will later use it elsewhere too.)

v4: New.
---
 config.h.in                                    |  4 ++++
 configure.ac                                   | 15 +++++++++++++++
 elf/ifuncdep2.c                                |  3 +++
 elf/ifuncmain6pie.c                            |  1 +
 elf/ifuncmain7.c                               |  1 +
 elf/ifuncmod1.c                                |  3 +++
 elf/ifuncmod5.c                                |  3 +++
 include/libc-symbols.h                         | 19 +++++++++++++++++--
 nptl/pt-fork.c                                 |  1 +
 nptl/pt-longjmp.c                              |  1 +
 nptl/pt-system.c                               |  1 +
 nptl/pt-vfork.c                                |  1 +
 rt/clock-compat.c                              |  4 +++-
 sysdeps/generic/ifunc-sel.h                    |  2 ++
 sysdeps/nacl/nacl_interface_query.c            |  1 +
 sysdeps/powerpc/ifunc-sel.h                    |  2 ++
 sysdeps/unix/make-syscalls.sh                  |  1 +
 sysdeps/unix/sysv/linux/powerpc/gettimeofday.c |  1 +
 sysdeps/unix/sysv/linux/powerpc/time.c         |  1 +
 sysdeps/unix/sysv/linux/x86/gettimeofday.c     |  1 +
 sysdeps/unix/sysv/linux/x86/time.c             |  1 +
 sysdeps/unix/sysv/linux/x86_64/x32/getcpu.c    |  1 +
 sysdeps/x86_64/ifuncmod8.c                     |  1 +
 23 files changed, 66 insertions(+), 3 deletions(-)
  

Patch

diff --git a/config.h.in b/config.h.in
index ec9c8bc..7bbc148 100644
--- a/config.h.in
+++ b/config.h.in
@@ -43,6 +43,10 @@ 
 /* Define if compiler accepts -ftree-loop-distribute-patterns.  */
 #undef  HAVE_CC_INHIBIT_LOOP_TO_LIBCALL
 
+/* Define if compiler accepts -fno-stack-protector in an
+   __attribute__((__optimize__)).  */
+#undef	HAVE_CC_NO_STACK_PROTECTOR
+
 /* Define if the regparm attribute shall be used for local functions
    (gcc on ix86 only).  */
 #undef	USE_REGPARMS
diff --git a/configure.ac b/configure.ac
index 7b81049..a920f89 100644
--- a/configure.ac
+++ b/configure.ac
@@ -632,10 +632,25 @@  LIBC_TRY_CC_OPTION([$CFLAGS $CPPFLAGS -Werror -fstack-protector-all],
 		   [libc_cv_ssp_all=no])
 ])
 
+AC_CACHE_CHECK(if $CC accepts -fno-stack-protector with \
+__attribute__ ((__optimize__)), libc_cv_cc_no_stack_protector, [dnl
+cat > conftest.c <<EOF
+void
+__attribute__ ((__optimize__ ("-fno-stack-protector")))
+foo (void) {}
+EOF
+libc_cv_cc_no_stack_protector=no
+if AC_TRY_COMMAND([${CC-cc} $CFLAGS $CPPFLAGS -c conftest.c])
+then
+  libc_cv_cc_no_stack_protector=yes
+fi
+rm -f conftest*])
+
 stack_protector=
 no_stack_protector=
 if test x$libc_cv_ssp = xyes; then
     no_stack_protector=-fno-stack-protector
+    AC_DEFINE(HAVE_CC_NO_STACK_PROTECTOR)
 fi
 
 if test x$enable_stack_protector = xyes && test $libc_cv_ssp = yes; then
diff --git a/elf/ifuncdep2.c b/elf/ifuncdep2.c
index 6e66d31..d87d61d 100644
--- a/elf/ifuncdep2.c
+++ b/elf/ifuncdep2.c
@@ -32,6 +32,7 @@  void * foo1_ifunc (void) __asm__ ("foo1");
 __asm__(".type foo1, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo1_ifunc (void)
 {
   return ifunc_sel (one, minus_one, zero);
@@ -41,6 +42,7 @@  void * foo2_ifunc (void) __asm__ ("foo2");
 __asm__(".type foo2, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo2_ifunc (void)
 {
   return ifunc_sel (minus_one, one, zero);
@@ -50,6 +52,7 @@  void * foo3_ifunc (void) __asm__ ("foo3");
 __asm__(".type foo3, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo3_ifunc (void)
 {
   return ifunc_sel (one, zero, minus_one);
diff --git a/elf/ifuncmain6pie.c b/elf/ifuncmain6pie.c
index 8478d4c..04faeb8 100644
--- a/elf/ifuncmain6pie.c
+++ b/elf/ifuncmain6pie.c
@@ -21,6 +21,7 @@  void * foo_ifunc (void) __asm__ ("foo");
 __asm__(".type foo, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo_ifunc (void)
 {
   return ifunc_one (one);
diff --git a/elf/ifuncmain7.c b/elf/ifuncmain7.c
index 617a596..1e8f7ea 100644
--- a/elf/ifuncmain7.c
+++ b/elf/ifuncmain7.c
@@ -20,6 +20,7 @@  __asm__(".type foo, %gnu_indirect_function");
 
 static void *
 __attribute__ ((used))
+inhibit_stack_protector
 foo_ifunc (void)
 {
   return ifunc_one (one);
diff --git a/elf/ifuncmod1.c b/elf/ifuncmod1.c
index 0b61380..f0bf5fb 100644
--- a/elf/ifuncmod1.c
+++ b/elf/ifuncmod1.c
@@ -36,6 +36,7 @@  void * foo_ifunc (void) __asm__ ("foo");
 __asm__(".type foo, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo_ifunc (void)
 {
   return ifunc_sel (one, minus_one, zero);
@@ -45,6 +46,7 @@  void * foo_hidden_ifunc (void) __asm__ ("foo_hidden");
 __asm__(".type foo_hidden, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo_hidden_ifunc (void)
 {
   return ifunc_sel (minus_one, one, zero);
@@ -54,6 +56,7 @@  void * foo_protected_ifunc (void) __asm__ ("foo_protected");
 __asm__(".type foo_protected, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo_protected_ifunc (void)
 {
   return ifunc_sel (one, zero, minus_one);
diff --git a/elf/ifuncmod5.c b/elf/ifuncmod5.c
index 0e65a63..5a95780 100644
--- a/elf/ifuncmod5.c
+++ b/elf/ifuncmod5.c
@@ -31,6 +31,7 @@  void * foo_ifunc (void) __asm__ ("foo");
 __asm__(".type foo, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo_ifunc (void)
 {
   return ifunc_sel (one, minus_one, zero);
@@ -40,6 +41,7 @@  void * foo_hidden_ifunc (void) __asm__ ("foo_hidden");
 __asm__(".type foo_hidden, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo_hidden_ifunc (void)
 {
   return ifunc_sel (minus_one, one, zero);
@@ -49,6 +51,7 @@  void * foo_protected_ifunc (void) __asm__ ("foo_protected");
 __asm__(".type foo_protected, %gnu_indirect_function");
 
 void *
+inhibit_stack_protector
 foo_protected_ifunc (void)
 {
   return ifunc_sel (one, zero, minus_one);
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index 4548e09..a865511 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -314,6 +314,17 @@  for linking")
 
 #define attribute_relro __attribute__ ((section (".data.rel.ro")))
 
+
+/* Add the compiler optimization to inhibit stack-protection, so that
+   we can disable stack protection in sensitive places, like ifunc
+   resolvers and early static TLS init.  */
+#ifdef HAVE_CC_NO_STACK_PROTECTOR
+# define inhibit_stack_protector \
+    __attribute__ ((__optimize__ ("-fno-stack-protector")))
+#else
+# define inhibit_stack_protector
+#endif
+
 /* The following macros are used for PLT bypassing within libc.so
    (and if needed other libraries similarly).
    First of all, you need to have the function prototyped somewhere,
@@ -716,7 +727,9 @@  for linking")
 /* Marker used for indirection function symbols.  */
 #define libc_ifunc(name, expr)						\
   extern void *name##_ifunc (void) __asm__ (#name);			\
-  void *name##_ifunc (void)						\
+  void *								\
+  inhibit_stack_protector						\
+  name##_ifunc (void)							\
   {									\
     INIT_ARCH ();							\
     __typeof (name) *res = expr;					\
@@ -728,7 +741,9 @@  for linking")
    which will, if necessary, initialize the data first.  */
 #define libm_ifunc(name, expr)						\
   extern void *name##_ifunc (void) __asm__ (#name);			\
-  void *name##_ifunc (void)						\
+  void *								\
+  inhibit_stack_protector						\
+  name##_ifunc (void)							\
   {									\
     __typeof (name) *res = expr;					\
     return res;								\
diff --git a/nptl/pt-fork.c b/nptl/pt-fork.c
index b65d6b4..4178af8 100644
--- a/nptl/pt-fork.c
+++ b/nptl/pt-fork.c
@@ -34,6 +34,7 @@ 
 
 static __typeof (fork) *
 __attribute__ ((used))
+inhibit_stack_protector
 fork_resolve (void)
 {
   return &__libc_fork;
diff --git a/nptl/pt-longjmp.c b/nptl/pt-longjmp.c
index a1cc286..8a33cb4 100644
--- a/nptl/pt-longjmp.c
+++ b/nptl/pt-longjmp.c
@@ -34,6 +34,7 @@ 
 
 static __typeof (longjmp) *
 __attribute__ ((used))
+inhibit_stack_protector
 longjmp_resolve (void)
 {
   return &__libc_longjmp;
diff --git a/nptl/pt-system.c b/nptl/pt-system.c
index 56f2a89..a481ab3 100644
--- a/nptl/pt-system.c
+++ b/nptl/pt-system.c
@@ -34,6 +34,7 @@ 
 
 static __typeof (system) *
 __attribute__ ((used))
+inhibit_stack_protector
 system_resolve (void)
 {
   return &__libc_system;
diff --git a/nptl/pt-vfork.c b/nptl/pt-vfork.c
index 8f4be0c..8f3c2c0 100644
--- a/nptl/pt-vfork.c
+++ b/nptl/pt-vfork.c
@@ -48,6 +48,7 @@  extern __typeof (vfork) __libc_vfork;   /* Defined in libc.  */
 
 static __typeof (vfork) *
 __attribute__ ((used))
+inhibit_stack_protector
 vfork_resolve (void)
 {
   return &__libc_vfork;
diff --git a/rt/clock-compat.c b/rt/clock-compat.c
index dc69e4a..b0fdd8b 100644
--- a/rt/clock-compat.c
+++ b/rt/clock-compat.c
@@ -30,7 +30,9 @@ 
 #if HAVE_IFUNC
 # define COMPAT_REDIRECT(name, proto, arglist)				      \
   __typeof (name) *name##_ifunc (void) asm (#name);			      \
-  __typeof (name) *name##_ifunc (void)					      \
+  __typeof (name) *							      \
+  inhibit_stack_protector						      \
+  name##_ifunc (void)							      \
   {									      \
     return &__##name;							      \
   }									      \
diff --git a/sysdeps/generic/ifunc-sel.h b/sysdeps/generic/ifunc-sel.h
index 6a27b69..1fff405 100644
--- a/sysdeps/generic/ifunc-sel.h
+++ b/sysdeps/generic/ifunc-sel.h
@@ -5,6 +5,7 @@ 
 extern int global;
 
 static inline void *
+inhibit_stack_protector
 ifunc_sel (int (*f1) (void), int (*f2) (void), int (*f3) (void))
 {
  switch (global)
@@ -19,6 +20,7 @@  ifunc_sel (int (*f1) (void), int (*f2) (void), int (*f3) (void))
 }
 
 static inline void *
+inhibit_stack_protector
 ifunc_one (int (*f1) (void))
 {
   return f1;
diff --git a/sysdeps/nacl/nacl_interface_query.c b/sysdeps/nacl/nacl_interface_query.c
index adf1dd4..dbaa88b 100644
--- a/sysdeps/nacl/nacl_interface_query.c
+++ b/sysdeps/nacl/nacl_interface_query.c
@@ -29,6 +29,7 @@  extern TYPE_nacl_irt_query nacl_interface_query_ifunc (void)
   asm ("nacl_interface_query");
 
 TYPE_nacl_irt_query
+inhibit_stack_protector
 nacl_interface_query_ifunc (void)
 {
   return &__nacl_irt_query;
diff --git a/sysdeps/powerpc/ifunc-sel.h b/sysdeps/powerpc/ifunc-sel.h
index 526d8ed..598b125 100644
--- a/sysdeps/powerpc/ifunc-sel.h
+++ b/sysdeps/powerpc/ifunc-sel.h
@@ -5,6 +5,7 @@ 
 extern int global;
 
 static inline void *
+inhibit_stack_protector
 ifunc_sel (int (*f1) (void), int (*f2) (void), int (*f3) (void))
 {
   register void *ret __asm__ ("r3");
@@ -30,6 +31,7 @@  ifunc_sel (int (*f1) (void), int (*f2) (void), int (*f3) (void))
 }
 
 static inline void *
+inhibit_stack_protector
 ifunc_one (int (*f1) (void))
 {
   register void *ret __asm__ ("r3");
diff --git a/sysdeps/unix/make-syscalls.sh b/sysdeps/unix/make-syscalls.sh
index 58d165e..123553c 100644
--- a/sysdeps/unix/make-syscalls.sh
+++ b/sysdeps/unix/make-syscalls.sh
@@ -287,6 +287,7 @@  while read file srcfile caller syscall args strong weak; do
 	(echo '#include <dl-vdso.h>'; \\
 	 echo 'extern void *${strong}_ifunc (void) __asm ("${strong}");'; \\
 	 echo 'void *'; \\
+	 echo 'inhibit_stack_protector'; \\
 	 echo '${strong}_ifunc (void)'; \\
 	 echo '{'; \\
 	 echo '  PREPARE_VERSION_KNOWN (symver, ${vdso_symver});'; \\
diff --git a/sysdeps/unix/sysv/linux/powerpc/gettimeofday.c b/sysdeps/unix/sysv/linux/powerpc/gettimeofday.c
index 25a4e7c..a8b6cfa 100644
--- a/sysdeps/unix/sysv/linux/powerpc/gettimeofday.c
+++ b/sysdeps/unix/sysv/linux/powerpc/gettimeofday.c
@@ -33,6 +33,7 @@  __gettimeofday_syscall (struct timeval *tv, struct timezone *tz)
 }
 
 void *
+inhibit_stack_protector
 gettimeofday_ifunc (void)
 {
   PREPARE_VERSION (linux2615, "LINUX_2.6.15", 123718565);
diff --git a/sysdeps/unix/sysv/linux/powerpc/time.c b/sysdeps/unix/sysv/linux/powerpc/time.c
index 7973419..4b89b38 100644
--- a/sysdeps/unix/sysv/linux/powerpc/time.c
+++ b/sysdeps/unix/sysv/linux/powerpc/time.c
@@ -43,6 +43,7 @@  time_syscall (time_t *t)
 }
 
 void *
+inhibit_stack_protector
 time_ifunc (void)
 {
   PREPARE_VERSION (linux2615, "LINUX_2.6.15", 123718565);
diff --git a/sysdeps/unix/sysv/linux/x86/gettimeofday.c b/sysdeps/unix/sysv/linux/x86/gettimeofday.c
index 36f7c26..e05ad53 100644
--- a/sysdeps/unix/sysv/linux/x86/gettimeofday.c
+++ b/sysdeps/unix/sysv/linux/x86/gettimeofday.c
@@ -32,6 +32,7 @@  __gettimeofday_syscall (struct timeval *tv, struct timezone *tz)
 void *gettimeofday_ifunc (void) __asm__ ("__gettimeofday");
 
 void *
+inhibit_stack_protector
 gettimeofday_ifunc (void)
 {
   PREPARE_VERSION_KNOWN (linux26, LINUX_2_6);
diff --git a/sysdeps/unix/sysv/linux/x86/time.c b/sysdeps/unix/sysv/linux/x86/time.c
index f5f7f91..c5bd8dc 100644
--- a/sysdeps/unix/sysv/linux/x86/time.c
+++ b/sysdeps/unix/sysv/linux/x86/time.c
@@ -33,6 +33,7 @@  __time_syscall (time_t *t)
 void *time_ifunc (void) __asm__ ("time");
 
 void *
+inhibit_stack_protector
 time_ifunc (void)
 {
   PREPARE_VERSION_KNOWN (linux26, LINUX_2_6);
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/getcpu.c b/sysdeps/unix/sysv/linux/x86_64/x32/getcpu.c
index cbac4b3..8436f9d 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/getcpu.c
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/getcpu.c
@@ -21,6 +21,7 @@ 
 void *getcpu_ifunc (void) __asm__ ("__getcpu");
 
 void *
+inhibit_stack_protector
 getcpu_ifunc (void)
 {
   PREPARE_VERSION (linux26, "LINUX_2.6", 61765110);
diff --git a/sysdeps/x86_64/ifuncmod8.c b/sysdeps/x86_64/ifuncmod8.c
index c004367..7c06562 100644
--- a/sysdeps/x86_64/ifuncmod8.c
+++ b/sysdeps/x86_64/ifuncmod8.c
@@ -28,6 +28,7 @@  foo_impl (float x)
 }
 
 void *
+inhibit_stack_protector
 foo_ifunc (void)
 {
   __m128i xmm = _mm_set1_epi32 (-1);