linamh/sys-libs/glibc/files/2.6/glibc-2.6-hardened-inittls-nosysenter.patch

274 lines
8.5 KiB
Diff
Raw Normal View History

When building glibc PIE (which is not something upstream support),
several modifications are necessary to the glibc build process.
First, any syscalls in PIEs must be of the PIC variant, otherwise
textrels ensue. Then, any syscalls made before the initialisation
of the TLS will fail on i386, as the sysenter variant on i386 uses
the TLS, giving rise to a chicken-and-egg situation. This patch
defines a PIC syscall variant that doesn't use sysenter, even when the sysenter
version is normally used, and uses the non-sysenter version for the brk
syscall that is performed by the TLS initialisation. Further, the TLS
initialisation is moved in this case prior to the initialisation of
dl_osversion, as that requires further syscalls.
csu/libc-start.c: Move initial TLS initialization to before the
initialisation of dl_osversion, when INTERNAL_SYSCALL_NOSYSENTER is defined
csu/libc-tls.c: Use the no-sysenter version of sbrk when
INTERNAL_SYSCALL_NOSYSENTER is defined.
misc/sbrk.c: Define a no-sysenter version of sbrk, using the no-sysenter
version of brk - if INTERNAL_SYSCALL_NOSYSENTER is defined.
misc/brk.c: Define a no-sysenter version of brk if
INTERNAL_SYSCALL_NOSYSENTER is defined.
sysdeps/unix/sysv/linux/i386/sysdep.h: Define INTERNAL_SYSCALL_NOSYSENTER
Make INTERNAL_SYSCALL always use the PIC variant, even if not SHARED.
Patch by Kevin F. Quinn <kevquinn@gentoo.org>
--- csu/libc-start.c
+++ csu/libc-start.c
@@ -28,6 +28,7 @@
extern int __libc_multiple_libcs;
#include <tls.h>
+#include <sysdep.h>
#ifndef SHARED
# include <dl-osinfo.h>
extern void __pthread_initialize_minimal (void);
@@ -129,6 +130,11 @@
# endif
_dl_aux_init (auxvec);
# endif
+# ifdef INTERNAL_SYSCALL_NOSYSENTER
+ /* Do the initial TLS initialization before _dl_osversion,
+ since the latter uses the uname syscall. */
+ __pthread_initialize_minimal ();
+# endif
# ifdef DL_SYSDEP_OSCHECK
if (!__libc_multiple_libcs)
{
@@ -138,10 +144,12 @@
}
# endif
+# ifndef INTERNAL_SYSCALL_NOSYSENTER
/* Initialize the thread library at least a bit since the libgcc
functions are using thread functions if these are available and
we need to setup errno. */
__pthread_initialize_minimal ();
+# endif
#endif
# ifndef SHARED
--- csu/libc-tls.c
+++ csu/libc-tls.c
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <stdio.h>
#include <sys/param.h>
+#include <sysdep.h>
#ifdef SHARED
@@ -29,6 +30,9 @@
#error makefile bug, this file is for static only
#endif
+#ifdef INTERNAL_SYSCALL_NOSYSENTER
+extern void *__sbrk_nosysenter (intptr_t __delta);
+#endif
extern ElfW(Phdr) *_dl_phdr;
extern size_t _dl_phnum;
@@ -141,14 +145,26 @@
The initialized value of _dl_tls_static_size is provided by dl-open.c
to request some surplus that permits dynamic loading of modules with
- IE-model TLS. */
+ IE-model TLS.
+
+ Where the normal sbrk would use a syscall that needs the TLS (i386)
+ use the special non-sysenter version instead. */
#if TLS_TCB_AT_TP
tcb_offset = roundup (memsz + GL(dl_tls_static_size), tcbalign);
+# ifdef INTERNAL_SYSCALL_NOSYSENTER
+ tlsblock = __sbrk_nosysenter (tcb_offset + tcbsize + max_align);
+# else
tlsblock = __sbrk (tcb_offset + tcbsize + max_align);
+# endif
#elif TLS_DTV_AT_TP
tcb_offset = roundup (tcbsize, align ?: 1);
+# ifdef INTERNAL_SYSCALL_NOSYSENTER
+ tlsblock = __sbrk_nosysenter (tcb_offset + memsz + max_align
+ + TLS_PRE_TCB_SIZE + GL(dl_tls_static_size));
+# else
tlsblock = __sbrk (tcb_offset + memsz + max_align
+ TLS_PRE_TCB_SIZE + GL(dl_tls_static_size));
+# endif
tlsblock += TLS_PRE_TCB_SIZE;
#else
/* In case a model with a different layout for the TCB and DTV
--- misc/sbrk.c
+++ misc/sbrk.c
@@ -18,6 +18,7 @@
#include <unistd.h>
#include <errno.h>
+#include <sysdep.h>
/* Defined in brk.c. */
extern void *__curbrk;
@@ -29,6 +30,35 @@
/* Extend the process's data space by INCREMENT.
If INCREMENT is negative, shrink data space by - INCREMENT.
Return start of new space allocated, or -1 for errors. */
+#ifdef INTERNAL_SYSCALL_NOSYSENTER
+/* This version is used by csu/libc-tls.c whem initialising the TLS
+ if the SYSENTER version requires the TLS (which it does on i386).
+ Obviously using the TLS before it is initialised is broken. */
+extern int __brk_nosysenter (void *addr);
+void *
+__sbrk_nosysenter (intptr_t increment)
+{
+ void *oldbrk;
+
+ /* If this is not part of the dynamic library or the library is used
+ via dynamic loading in a statically linked program update
+ __curbrk from the kernel's brk value. That way two separate
+ instances of __brk and __sbrk can share the heap, returning
+ interleaved pieces of it. */
+ if (__curbrk == NULL || __libc_multiple_libcs)
+ if (__brk_nosysenter (0) < 0) /* Initialize the break. */
+ return (void *) -1;
+
+ if (increment == 0)
+ return __curbrk;
+
+ oldbrk = __curbrk;
+ if (__brk_nosysenter (oldbrk + increment) < 0)
+ return (void *) -1;
+
+ return oldbrk;
+}
+#endif
void *
__sbrk (intptr_t increment)
{
--- sysdeps/unix/sysv/linux/i386/brk.c
+++ sysdeps/unix/sysv/linux/i386/brk.c
@@ -31,6 +31,30 @@
linker. */
weak_alias (__curbrk, ___brk_addr)
+#ifdef INTERNAL_SYSCALL_NOSYSENTER
+/* This version is used by csu/libc-tls.c whem initialising the TLS
+ * if the SYSENTER version requires the TLS (which it does on i386).
+ * Obviously using the TLS before it is initialised is broken. */
+int
+__brk_nosysenter (void *addr)
+{
+ void *__unbounded newbrk;
+
+ INTERNAL_SYSCALL_DECL (err);
+ newbrk = (void *__unbounded) INTERNAL_SYSCALL_NOSYSENTER (brk, err, 1,
+ __ptrvalue (addr));
+
+ __curbrk = newbrk;
+
+ if (newbrk < addr)
+ {
+ __set_errno (ENOMEM);
+ return -1;
+ }
+
+ return 0;
+}
+#endif
int
__brk (void *addr)
{
--- sysdeps/unix/sysv/linux/i386/sysdep.h
+++ sysdeps/unix/sysv/linux/i386/sysdep.h
@@ -187,7 +187,7 @@
/* The original calling convention for system calls on Linux/i386 is
to use int $0x80. */
#ifdef I386_USE_SYSENTER
-# ifdef SHARED
+# if defined SHARED || defined __PIC__
# define ENTER_KERNEL call *%gs:SYSINFO_OFFSET
# else
# define ENTER_KERNEL call *_dl_sysinfo
@@ -358,7 +358,7 @@
possible to use more than four parameters. */
#undef INTERNAL_SYSCALL
#ifdef I386_USE_SYSENTER
-# ifdef SHARED
+# if defined SHARED || defined __PIC__
# define INTERNAL_SYSCALL(name, err, nr, args...) \
({ \
register unsigned int resultvar; \
@@ -384,6 +384,18 @@
: "0" (name), "i" (offsetof (tcbhead_t, sysinfo)) \
ASMFMT_##nr(args) : "memory", "cc"); \
(int) resultvar; })
+# define INTERNAL_SYSCALL_NOSYSENTER(name, err, nr, args...) \
+ ({ \
+ register unsigned int resultvar; \
+ EXTRAVAR_##nr \
+ asm volatile ( \
+ LOADARGS_NOSYSENTER_##nr \
+ "movl %1, %%eax\n\t" \
+ "int $0x80\n\t" \
+ RESTOREARGS_NOSYSENTER_##nr \
+ : "=a" (resultvar) \
+ : "i" (__NR_##name) ASMFMT_##nr(args) : "memory", "cc"); \
+ (int) resultvar; })
# else
# define INTERNAL_SYSCALL(name, err, nr, args...) \
({ \
@@ -447,12 +459,20 @@
#define LOADARGS_0
#ifdef __PIC__
-# if defined I386_USE_SYSENTER && defined SHARED
+# if defined I386_USE_SYSENTER && ( defined SHARED || defined __PIC__ )
# define LOADARGS_1 \
"bpushl .L__X'%k3, %k3\n\t"
# define LOADARGS_5 \
"movl %%ebx, %4\n\t" \
"movl %3, %%ebx\n\t"
+# define LOADARGS_NOSYSENTER_1 \
+ "bpushl .L__X'%k2, %k2\n\t"
+# define LOADARGS_NOSYSENTER_2 LOADARGS_NOSYSENTER_1
+# define LOADARGS_NOSYSENTER_3 LOADARGS_3
+# define LOADARGS_NOSYSENTER_4 LOADARGS_3
+# define LOADARGS_NOSYSENTER_5 \
+ "movl %%ebx, %3\n\t" \
+ "movl %2, %%ebx\n\t"
# else
# define LOADARGS_1 \
"bpushl .L__X'%k2, %k2\n\t"
@@ -474,11 +495,18 @@
#define RESTOREARGS_0
#ifdef __PIC__
-# if defined I386_USE_SYSENTER && defined SHARED
+# if defined I386_USE_SYSENTER && ( defined SHARED || defined __PIC__ )
# define RESTOREARGS_1 \
"bpopl .L__X'%k3, %k3\n\t"
# define RESTOREARGS_5 \
"movl %4, %%ebx"
+# define RESTOREARGS_NOSYSENTER_1 \
+ "bpopl .L__X'%k2, %k2\n\t"
+# define RESTOREARGS_NOSYSENTER_2 RESTOREARGS_NOSYSENTER_1
+# define RESTOREARGS_NOSYSENTER_3 RESTOREARGS_3
+# define RESTOREARGS_NOSYSENTER_4 RESTOREARGS_3
+# define RESTOREARGS_NOSYSENTER_5 \
+ "movl %3, %%ebx"
# else
# define RESTOREARGS_1 \
"bpopl .L__X'%k2, %k2\n\t"