Explorar o código

libsanitizer: drop __pagesize, make _dl_pagesize the single source of truth

uClibc-ng's getpagesize() returned __pagesize, a libc-local variable
that is only initialised by libc.so.0's DT_INIT (__uClibc_init), which
runs *after* the DT_INIT of every earlier-loaded shared object.  In
particular it runs after the constructors of libasan/libbacktrace, whose
symbolizer calls getpagesize() to mmap() its first work page.  With
__pagesize still 0 the mmap fails with EINVAL, ASan loses symbolization
for every stack frame, and many ASan testsuite cases (e.g. pr69276.C)
fail spuriously.

The dynamic linker, however, already populates _dl_pagesize from
AT_PAGESZ before any DT_INIT runs, and exports it as a dynamic symbol.
glibc takes advantage of this and implements getpagesize() as simply
`return GLRO(dl_pagesize);' (sysdeps/unix/sysv/linux/getpagesize.c).
Do the same here.

While we are at it, get rid of __pagesize entirely.  Keeping it would
mean maintaining two parallel page-size variables with subtly different
lifetimes -- the long-standing source of this bug and the cause of a
second, more nasty failure mode:

   The original re-entry guard in __uClibc_init was

       if (__pagesize) return;
       __pagesize = PAGE_SIZE;

   so any compile-time initialiser other than 0 on __pagesize turns
   the whole libc init into a no-op (_locale_init, _stdio_init, ...
   are skipped, MB_CUR_MAX stays 0, setlocale/regexec crash).  We hit
   exactly this trap during an earlier revision of this patch and
   spent a long time chasing it -- replacing the sentinel with a
   dedicated `static int __uClibc_initialized' makes the guard
   obvious and removes that footgun forever.

The few in-tree readers of __pagesize (libc/inet/ifaddrs.c,
libc/misc/internals/parse_config.c, getpagesize.c itself) are
converted to _dl_pagesize.  Internal users of the compile-time
PAGE_SIZE constant in the ldso (ldso/ldso/ldso.c, ldso/libdl/libdl.c,
libc/misc/elf/dl-support.c, ldso/ldso/arm/elfinterp.c) likewise read
_dl_pagesize, leaving the AT_PAGESZ value from the auxiliary vector
as the single source of truth.  PAGE_SIZE and PAGE_MASK macros are
removed from <bits/uClibc_page.h>; PAGE_SHIFT is kept (still used as
a compile-time constant by libc/stdlib/malloc-standard/malloc.h's
PROTECT_PTR pointer-mangling macros, and by xtensa -- see below).

Xtensa is special: INIT_GOT and PERFORM_BOOTSTRAP_GOT in
ldso/ldso/xtensa/dl-sysdep.h and dl-startup.h need page-granular
mprotect() because the xtensa GOT lives interleaved with code
(literal pools) and must be temporarily flipped to RWX.  These
macros run before the GOT itself is patched, so _dl_pagesize is not
yet reachable.  They are rewritten to use (1UL << PAGE_SHIFT)
inline, which keeps PAGE_SIZE out of the xtensa code paths entirely
while still using a compile-time constant where one is required.

The static-link case is covered: ldso/libdl/libdl.c is compiled into
libc.a, _dl_pagesize is therefore present in static binaries, and
_dl_aux_init() runs from __uClibc_main() before any user constructors,
so getpagesize() returns the correct value from the very first call.

The sys/user.h NBPG/UPAGES cleanup that becomes necessary once
PAGE_SIZE is gone from <bits/uClibc_page.h> is split out into a
follow-up patch ("sys/user.h: remove obsolete BFD trad-core
NBPG/UPAGES defines").

Verified on arm-bbs-linux-uclibcgnueabihf:
  * mb_probe: MB_CUR_MAX == 1 (was 0 with the broken sentinel)
  * regex_repro (REG_STARTEND with embedded NUL): match found
  * pagesize_static, mb_probe_static, regex_repro_static: identical
    behaviour shared vs. static
  * gcc.dg/asan/pr69276.C: ASan stack frames are symbolised

Xtensa is not exercised on the build host; the change is textually
mechanical: PAGE_SIZE -> (1UL << PAGE_SHIFT).

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
Ramin Moussavi hai 2 meses
pai
achega
487af14988

+ 0 - 1
include/unistd.h

@@ -1272,7 +1272,6 @@ __END_DECLS
 #endif
 typedef signed smallint_type smallint;
 typedef unsigned smallint_type smalluint;
-extern size_t __pagesize attribute_hidden;
 #endif
 
 

+ 2 - 2
ldso/ldso/arm/elfinterp.c

@@ -213,7 +213,7 @@ fix_bad_pc24 (unsigned long *const reloc_addr, unsigned long value)
   unsigned int *fix_address;
   if (! fix_page)
     {
-      fix_page = _dl_mmap (NULL,  PAGE_SIZE   , PROT_READ | PROT_WRITE | PROT_EXEC,
+      fix_page = _dl_mmap (NULL,  _dl_pagesize   , PROT_READ | PROT_WRITE | PROT_EXEC,
                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
       fix_offset = 0;
     }
@@ -223,7 +223,7 @@ fix_bad_pc24 (unsigned long *const reloc_addr, unsigned long value)
   fix_address[1] = value;
 
   fix_offset += 8;
-  if (fix_offset >= PAGE_SIZE)
+  if (fix_offset >= _dl_pagesize)
     fix_page = NULL;
 
   return (unsigned long)fix_address;

+ 1 - 1
ldso/ldso/ldso.c

@@ -461,7 +461,7 @@ void *_dl_get_ready_to_run(struct elf_resolve *tpnt, DL_LOADADDR_TYPE load_addr,
 	_dl_memset(app_tpnt, 0, sizeof(*app_tpnt));
 
 	/* Store the page size for later use */
-	_dl_pagesize = (_dl_auxvt[AT_PAGESZ].a_un.a_val) ? (size_t) _dl_auxvt[AT_PAGESZ].a_un.a_val : PAGE_SIZE;
+	_dl_pagesize = (_dl_auxvt[AT_PAGESZ].a_un.a_val) ? (size_t) _dl_auxvt[AT_PAGESZ].a_un.a_val : 0;
 	/* Make it so _dl_malloc can use the page of memory we have already
 	 * allocated.  We shouldn't need to grab any more memory.  This must
 	 * be first since things like _dl_dprintf() use _dl_malloc()...

+ 6 - 3
ldso/ldso/xtensa/dl-startup.h

@@ -7,6 +7,8 @@
  * Parts taken from glibc/sysdeps/xtensa/dl-machine.h.
  */
 
+#include <bits/uClibc_page.h>
+
 #if defined(__FDPIC__)
 __asm__ (
     "	.text\n"
@@ -166,9 +168,10 @@ do { \
 \
 	for (x = 0; x < tpnt->dynamic_info[DT_XTENSA (GOT_LOC_SZ)]; x++) { \
 		Elf32_Addr got_start, got_end; \
-		got_start = got_loc[x].offset & ~(PAGE_SIZE - 1); \
-		got_end = ((got_loc[x].offset + got_loc[x].length + PAGE_SIZE - 1) \
-				   & ~(PAGE_SIZE - 1)); \
+		got_start = got_loc[x].offset & ~((1UL << PAGE_SHIFT) - 1); \
+		got_end = ((got_loc[x].offset + got_loc[x].length \
+			    + (1UL << PAGE_SHIFT) - 1) \
+			   & ~((1UL << PAGE_SHIFT) - 1)); \
 		if (got_end >= prev_got_start && got_start <= prev_got_end) { \
 			if (got_end > prev_got_end) \
 				prev_got_end = got_end; \

+ 5 - 3
ldso/ldso/xtensa/dl-sysdep.h

@@ -21,6 +21,7 @@
 #define ELF_USES_RELOCA
 #include <elf.h>
 #include <link.h>
+#include <bits/uClibc_page.h>
 
 /* Translate a processor specific dynamic tag to the index
    in l_info array.  */
@@ -46,9 +47,10 @@ typedef struct xtensa_got_location_struct {
     for (x = 0; x < MODULE->dynamic_info[DT_XTENSA (GOT_LOC_SZ)]; x++)	      \
       {									      \
 	Elf32_Addr got_start, got_end;					      \
-	got_start = got_loc[x].offset & ~(PAGE_SIZE - 1);		      \
-	got_end = ((got_loc[x].offset + got_loc[x].length + PAGE_SIZE - 1)    \
-		   & ~(PAGE_SIZE - 1));					      \
+	got_start = got_loc[x].offset & ~((1UL << PAGE_SHIFT) - 1);	      \
+	got_end = ((got_loc[x].offset + got_loc[x].length			      \
+		    + (1UL << PAGE_SHIFT) - 1)				      \
+		   & ~((1UL << PAGE_SHIFT) - 1));			      \
 	if (got_end >= prev_got_start && got_start <= prev_got_end)	      \
 	  {								      \
 	    if (got_end > prev_got_end)					      \

+ 1 - 1
ldso/libdl/libdl.c

@@ -109,7 +109,7 @@ void (*_dl_free_function) (void *p);
 char *_dl_library_path         = NULL;         /* Where we look for libraries */
 #endif
 int _dl_errno                  = 0;         /* We can't use the real errno in ldso */
-size_t _dl_pagesize            = PAGE_SIZE; /* Store the page size for use later */
+size_t _dl_pagesize            = 0; /* Store the page size for use later */
 /* This global variable is also to communicate with debuggers such as gdb. */
 struct r_debug *_dl_debug_addr = NULL;
 

+ 3 - 7
libc/inet/ifaddrs.c

@@ -35,6 +35,7 @@
 
 #include "netlinkaccess.h"
 
+extern size_t _dl_pagesize;
 
 #ifndef __libc_use_alloca
 # define __libc_use_alloca(x) (x < __MAX_ALLOCA_CUTOFF)
@@ -129,13 +130,8 @@ __netlink_request (struct netlink_handle *h, int type)
 
   if (buf_size)
 	  this_buf_size = buf_size;
-  else {
-#ifdef PAGE_SIZE
-	  this_buf_size = PAGE_SIZE;
-#else
-	  this_buf_size = __pagesize;
-#endif
-  }
+  else
+	  this_buf_size = _dl_pagesize;
   if (__libc_use_alloca (this_buf_size))
     buf = alloca (this_buf_size);
   else

+ 1 - 1
libc/misc/elf/dl-support.c

@@ -53,7 +53,7 @@ void internal_function _dl_aux_init (ElfW(auxv_t) *av)
    _dl_phnum = (size_t) _dl_auxvt[AT_PHNUM].a_un.a_val;
 
    /* Get the pagesize from the aux vect */
-   _dl_pagesize = (_dl_auxvt[AT_PAGESZ].a_un.a_val) ? (size_t) _dl_auxvt[AT_PAGESZ].a_un.a_val : PAGE_SIZE;
+   _dl_pagesize = (_dl_auxvt[AT_PAGESZ].a_un.a_val) ? (size_t) _dl_auxvt[AT_PAGESZ].a_un.a_val : 0;
 }
 
 #if defined(USE_TLS) && USE_TLS

+ 3 - 10
libc/misc/internals/__uClibc_main.c

@@ -212,8 +212,6 @@ weak_alias (__progname_full, program_invocation_name)
 char **__environ = 0;
 weak_alias(__environ, environ)
 
-size_t __pagesize = 0;
-
 #ifndef O_NOFOLLOW
 # define O_NOFOLLOW	0
 #endif
@@ -272,12 +270,10 @@ extern void __uClibc_init(void) attribute_hidden;
 void __uClibc_init(void)
 {
     /* Don't recurse */
-    if (__pagesize)
+    static int __uClibc_initialized;
+    if (__uClibc_initialized)
 	return;
-
-    /* Setup an initial value.  This may not be perfect, but is
-     * better than  malloc using __pagesize=0 for atexit, ctors, etc.  */
-    __pagesize = PAGE_SIZE;
+    __uClibc_initialized = 1;
 
 #ifdef __UCLIBC_HAS_THREADS__
 
@@ -416,9 +412,6 @@ void __uClibc_main(int (*main)(int, char **, char **), int argc,
     __uClibc_init();
 
 #ifndef __ARCH_HAS_NO_LDSO__
-    /* Make certain getpagesize() gives the correct answer.
-     * _dl_pagesize is defined into ld.so if SHARED or into libc.a otherwise. */
-    __pagesize = _dl_pagesize;
 
 #ifndef SHARED
     /* Prevent starting SUID binaries where the stdin. stdout, and

+ 2 - 1
libc/misc/internals/parse_config.c

@@ -45,6 +45,7 @@ int parse_main(int argc UNUSED_PARAM, char **argv)
 # include <malloc.h>
 # include <bits/uClibc_page.h>
 # include "internal/parse_config.h"
+extern size_t _dl_pagesize;
 # ifndef FAST_FUNC
 #  define FAST_FUNC
 # endif
@@ -74,7 +75,7 @@ static off_t bb_get_chunk_with_continuation(parser_t* parsr)
 			else
 				break;
 		} else if (parsr->allocated) {
-			parsr->line_len += PAGE_SIZE;
+			parsr->line_len += _dl_pagesize;
 			parsr->data = realloc(parsr->data,
 								   parsr->data_len + parsr->line_len);
 			parsr->line = parsr->data + parsr->data_len;

+ 0 - 2
libc/sysdeps/linux/common/bits/uClibc_page.h

@@ -22,7 +22,5 @@
 
 /* PAGE_SHIFT determines the page size -- in this case 4096 */
 #define PAGE_SHIFT	12
-#define PAGE_SIZE	(1UL << PAGE_SHIFT)
-#define PAGE_MASK	(~(PAGE_SIZE-1))
 
 #endif /* _UCLIBC_PAGE_H */

+ 3 - 1
libc/sysdeps/linux/common/getpagesize.c

@@ -17,12 +17,14 @@
 
 #include <unistd.h>
 
+extern size_t _dl_pagesize;
+
 /* Return the system page size.  */
 /* couldn't make __getpagesize hidden, because shm.h uses it in a macro */
 extern __typeof(getpagesize) __getpagesize;
 int __getpagesize(void)
 {
-    return __pagesize;
+    return (int)_dl_pagesize;
 }
 strong_alias(__getpagesize,getpagesize)
 libc_hidden_def(getpagesize)