about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2022-11-30 18:59:08 -0500
committerRich Felker <dalias@aerifal.cx>2022-11-30 19:07:34 -0500
commitf47a8cdd250d9163fcfb39bf4e9d813957c0b187 (patch)
tree3df0e5326cf562ddb34024cc4270c31ff359617f
parent377218cb963aa20c6eb91781b0c79ad606631e6f (diff)
downloadmusl-f47a8cdd250d9163fcfb39bf4e9d813957c0b187.tar.gz
musl-f47a8cdd250d9163fcfb39bf4e9d813957c0b187.tar.xz
musl-f47a8cdd250d9163fcfb39bf4e9d813957c0b187.zip
ldso: fix invalid early references to extern-linkage libc.page_size
when PAGE_SIZE is not constant, internal/libc.h defines it to expand
to libc.page_size. however, kernel_mapped_dso, reachable from stage 2
of the dynamic linker bootstrap (__dls2), needs PAGE_SIZE to interpret
the relro range. at this point the libc object is both uninitialized
and invalid to access according to our model for bootstrapping, which
does not assume any external-linkage objects are accessible until
stages 2b/3. in practice it likely worked because hidden visibility
tends to behave like internal linkage, but this is not a property that
the dynamic linker was designed to rely upon.

this bug likely manifested as relro malfunction on archs with variable
page size, due to incorrect mask when aligning the relro bounds to
page boundaries.

while there are certainly more direct ways to fix the known problem
point here, a maximally future-proof way is to just bypass the libc.h
PAGE_SIZE definition in the dynamic linker and instead have dynlink.c
define its own internal-linkage object for variable page size. then,
if anything else in stage 2 ever ends up referencing PAGE_SIZE, it
will just automatically work right.
-rw-r--r--ldso/dynlink.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 8068fb37..09f3b0a8 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -21,9 +21,15 @@
 #include <sys/membarrier.h>
 #include "pthread_impl.h"
 #include "fork_impl.h"
-#include "libc.h"
 #include "dynlink.h"
 
+static size_t ldso_page_size;
+#ifndef PAGE_SIZE
+#define PAGE_SIZE ldso_page_size
+#endif
+
+#include "libc.h"
+
 #define malloc __libc_malloc
 #define calloc __libc_calloc
 #define realloc __libc_realloc
@@ -1723,6 +1729,7 @@ hidden void __dls2(unsigned char *base, size_t *sp)
 	ldso.phnum = ehdr->e_phnum;
 	ldso.phdr = laddr(&ldso, ehdr->e_phoff);
 	ldso.phentsize = ehdr->e_phentsize;
+	search_vec(auxv, &ldso_page_size, AT_PAGESZ);
 	kernel_mapped_dso(&ldso);
 	decode_dyn(&ldso);