OSDN Git Service

Support GNU_RELRO for static executables.
authorNick Kralevich <nnk@google.com>
Sat, 10 Nov 2012 00:51:36 +0000 (16:51 -0800)
committerNick Kralevich <nnk@google.com>
Thu, 15 Nov 2012 20:52:06 +0000 (12:52 -0800)
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

index a73bb71..ba7f008 100644 (file)
 
 #include <bionic_tls.h>
 #include <errno.h>
+#include <sys/mman.h>
+
+// 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));
 }