From ac3de8d080745e62c77fdf03e3923726d0a6b50d Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Fri, 9 Nov 2012 16:51:36 -0800 Subject: [PATCH] Support GNU_RELRO for static executables. In 9ec0f03a0d0b17bbb94ac0b9fef6add28a133c3a, we added dynamic linker support for GNU_RELRO protections. These protections make certain regions of memory read-only, helping protect certain data structures from accidental or deliberate modifications. This change adds GNU_RELRO support to STATIC executables. We can determine if we're compiled with relro protections by examining our own program headers, which is passed to us by the kernel in the AT_PHDR and AT_PHNUM auxiliary vectors. Parts of this code were stolen from the dynamic linker. Change-Id: Ic17eb5f932218538ec25347ece314d4dc7549de1 --- libc/bionic/libc_init_static.c | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/libc/bionic/libc_init_static.c b/libc/bionic/libc_init_static.c index a73bb711b..ba7f008e9 100644 --- a/libc/bionic/libc_init_static.c +++ b/libc/bionic/libc_init_static.c @@ -48,6 +48,14 @@ #include #include +#include + +// Returns the address of the page containing address 'x'. +#define PAGE_START(x) ((x) & PAGE_MASK) + +// Returns the address of the next page after address 'x', unless 'x' is +// itself at the start of a page. +#define PAGE_END(x) PAGE_START((x) + (PAGE_SIZE-1)) static void call_array(void(**list)()) { @@ -57,6 +65,42 @@ static void call_array(void(**list)()) } } +/* + * Find the value of the AT_* variable passed to us by the kernel. + */ +static unsigned find_aux(unsigned *vecs, unsigned type) { + while (vecs[0]) { + if (vecs[0] == type) { + return vecs[1]; + } + vecs += 2; + } + + return 0; // should never happen +} + +static void apply_gnu_relro(unsigned *vecs) { + Elf32_Phdr *phdr_start; + unsigned phdr_ct; + Elf32_Phdr *phdr; + + phdr_start = (Elf32_Phdr *) find_aux(vecs, AT_PHDR); + phdr_ct = find_aux(vecs, AT_PHNUM); + + for (phdr = phdr_start; phdr < (phdr_start + phdr_ct); phdr++) { + if (phdr->p_type != PT_GNU_RELRO) + continue; + + Elf32_Addr seg_page_start = PAGE_START(phdr->p_vaddr); + Elf32_Addr seg_page_end = PAGE_END(phdr->p_vaddr + phdr->p_memsz); + + // Check return value here? What do we do if we fail? + mprotect((void *) seg_page_start, + seg_page_end - seg_page_start, + PROT_READ); + } +} + __noreturn void __libc_init(uintptr_t *elfdata, void (*onexit)(void), int (*slingshot)(int, char**, char**), @@ -64,6 +108,7 @@ __noreturn void __libc_init(uintptr_t *elfdata, { int argc; char **argv, **envp; + unsigned *vecs; __libc_init_tls(NULL); @@ -84,6 +129,14 @@ __noreturn void __libc_init(uintptr_t *elfdata, argv = (char**)(elfdata + 1); envp = argv + argc + 1; + // The auxiliary vector is at the end of the environment block + vecs = (unsigned *) envp; + while (vecs[0] != 0) { + vecs++; + } + /* The end of the environment block is marked by two NULL pointers */ + vecs++; + /* The executable may have its own destructors listed in its .fini_array * so we need to ensure that these are called when the program exits * normally. @@ -91,5 +144,6 @@ __noreturn void __libc_init(uintptr_t *elfdata, if (structors->fini_array) __cxa_atexit(__libc_fini,structors->fini_array,NULL); + apply_gnu_relro(vecs); exit(slingshot(argc, argv, envp)); } -- 2.11.0