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.
This commit is contained in:
Rich Felker 2022-11-30 18:59:08 -05:00
parent 377218cb96
commit f47a8cdd25

View File

@ -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);