rtld: Add stubs to support PT_GNU_STACK
authorJohn Marino <draco@marino.st>
Sat, 21 Jan 2012 20:53:12 +0000 (21:53 +0100)
committerJohn Marino <draco@marino.st>
Mon, 23 Jan 2012 16:13:17 +0000 (17:13 +0100)
This functionality requires kernel support to determine the initial stack
access mode.  This is how it's supposed to work:

If the loaded DSO requires an executable stack as specified by the PF_X
bit or p_flags on PT_GNU_STACK phdr, yet the current stack protection does
not permit execution: The map_stacks_exec function should change the
stack protection mode of all thread stacks.

The PT_GNU_STACK phdr parser has been implemented as well as a private
interface to _rtld_get_stack_prot() in order to export the stack access
mode calculated by rtld.

Left to do:
1) Implement stack protection in the kernel
2) Add PT_GNU_STACK functionality in the kernel
3) Add a method to change stack protection mode in libc or libthread
4) Implement map_stacks_exec function in rtld.

lib/libc/gen/dlfcn.c
libexec/rtld-elf/i386/rtld_machdep.h
libexec/rtld-elf/map_object.c
libexec/rtld-elf/rtld.c
libexec/rtld-elf/rtld.h
libexec/rtld-elf/x86_64/rtld_machdep.h
sys/sys/link_elf.h

index 29f98d7..381c659 100644 (file)
@@ -26,6 +26,7 @@
  * $FreeBSD: src/lib/libc/gen/dlfcn.c 217154 2011-01-08 17:13:43Z kib $
  */
 
+#include <sys/mman.h>
 #include <dlfcn.h>
 #include <link.h>
 #include <stddef.h>
@@ -131,3 +132,9 @@ _rtld_addr_phdr(const void *addr, struct dl_phdr_info *phdr_info)
        return (0);
 }
 
+#pragma weak _rtld_get_stack_prot
+int
+_rtld_get_stack_prot(void)
+{
+       return (PROT_EXEC | PROT_READ | PROT_WRITE);
+}
index a69eb3a..2e47438 100644 (file)
@@ -79,4 +79,7 @@ extern void *___tls_get_addr(tls_index *ti) __attribute__((__regparm__(1)));
 extern void *__tls_get_addr(tls_index *ti);
 extern void *__tls_get_addr_tcb(struct tls_tcb *tcb, tls_index *ti);
 
+#define        RTLD_DEFAULT_STACK_PF_EXEC      PF_X
+#define        RTLD_DEFAULT_STACK_EXEC         PROT_EXEC
+
 #endif
index cb85579..f33c839 100644 (file)
@@ -83,6 +83,7 @@ map_object(int fd, const char *path, const struct stat *sb)
     Elf_Addr bss_vaddr;
     Elf_Addr bss_vlimit;
     caddr_t bss_addr;
+    Elf_Word stack_flags;
     Elf_Addr relro_page;
     size_t relro_size;
 
@@ -104,6 +105,7 @@ map_object(int fd, const char *path, const struct stat *sb)
     relro_page = 0;
     relro_size = 0;
     segs = alloca(sizeof(segs[0]) * hdr->e_phnum);
+    stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W;
     while (phdr < phlimit) {
        switch (phdr->p_type) {
 
@@ -133,6 +135,10 @@ map_object(int fd, const char *path, const struct stat *sb)
            phtls = phdr;
            break;
 
+       case PT_GNU_STACK:
+           stack_flags = phdr->p_flags;
+           break;
+
        case PT_GNU_RELRO:
            relro_page = phdr->p_vaddr;
            relro_size = phdr->p_memsz;
@@ -276,7 +282,7 @@ map_object(int fd, const char *path, const struct stat *sb)
        obj->tlsinitsize = phtls->p_filesz;
        obj->tlsinit = mapbase + phtls->p_vaddr;
     }
-
+    obj->stack_flags = stack_flags;
     if (relro_size) {
         obj->relro_page = obj->relocbase + trunc_page(relro_page);
         obj->relro_size = round_page(relro_size);
index a165c64..cb80a31 100644 (file)
@@ -106,6 +106,7 @@ static void unload_filtees(Obj_Entry *);
 static int load_needed_objects(Obj_Entry *, int);
 static int load_preload_objects(void);
 static Obj_Entry *load_object(const char *, const Obj_Entry *, int);
+static void map_stacks_exec(RtldLockState *);
 static Obj_Entry *obj_from_addr(const void *);
 static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *);
 static void objlist_call_init(Objlist *, RtldLockState *);
@@ -205,6 +206,9 @@ extern Elf_Dyn _DYNAMIC;
 int osreldate;
 #endif
 
+static int stack_prot = PROT_READ | PROT_WRITE | RTLD_DEFAULT_STACK_EXEC;
+static int max_stack_flags;
+
 /*
  * These are the functions the dynamic linker exports to application
  * programs.  They are the only symbols the dynamic linker is willing
@@ -231,6 +235,7 @@ static func_ptr_type exports[] = {
     (func_ptr_type) &_rtld_call_init,
     (func_ptr_type) &_rtld_thread_init,
     (func_ptr_type) &_rtld_addr_phdr,
+    (func_ptr_type) &_rtld_get_stack_prot,
     NULL
 };
 
@@ -462,6 +467,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
        close(fd);
        if (obj_main == NULL)
            die();
+       max_stack_flags = obj->stack_flags;
     } else {                           /* Main program already loaded. */
        const Elf_Phdr *phdr;
        int phnum;
@@ -516,6 +522,10 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
     dbg("obj_main path %s", obj_main->path);
     obj_main->mainprog = true;
 
+    if (aux_info[AT_STACKPROT] != NULL &&
+      aux_info[AT_STACKPROT]->a_un.a_val != 0)
+           stack_prot = aux_info[AT_STACKPROT]->a_un.a_val;
+
     /*
      * Get the actual dynamic linker pathname from the executable if
      * possible.  (It should always be possible.)  That ensures that
@@ -647,6 +657,8 @@ resident_skip2:
 
     r_debug_state(NULL, &obj_main->linkmap); /* say hello to gdb! */
 
+    map_stacks_exec(NULL);
+
     /*
      * Do NOT call the initlist here, give libc a chance to set up
      * the initial TLS segment.  crt1 will then call _rtld_call_init().
@@ -1166,6 +1178,8 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
        break;
     }
 
+    obj->stack_flags = PF_X | PF_R | PF_W;
+
     for (ph = phdr;  ph < phlimit;  ph++) {
        switch (ph->p_type) {
 
@@ -1198,6 +1212,10 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
            obj->tlsinit = (void*)(ph->p_vaddr + obj->relocbase);
            break;
 
+       case PT_GNU_STACK:
+           obj->stack_flags = ph->p_flags;
+           break;
+
        case PT_GNU_RELRO:
            obj->relro_page = obj->relocbase + trunc_page(ph->p_vaddr);
            obj->relro_size = round_page(ph->p_memsz);
@@ -1842,6 +1860,7 @@ do_load_object(int fd, const char *name, char *path, struct stat *sbp,
     obj_count++;
     obj_loads++;
     linkmap_add(obj);  /* for GDB & dlinfo() */
+    max_stack_flags |= obj->stack_flags;
 
     dbg("  %p .. %p: %s", obj->mapbase,
          obj->mapbase + obj->mapsize - 1, obj->path);
@@ -2359,6 +2378,8 @@ dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode)
        name);
     GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL);
 
+    map_stacks_exec(&lockstate);
+
     /* Call the init functions. */
     objlist_call_init(&initlist, &lockstate);
     objlist_clear(&initlist);
@@ -3974,6 +3995,34 @@ fetch_ventry(const Obj_Entry *obj, unsigned long symnum)
     return NULL;
 }
 
+int
+_rtld_get_stack_prot(void)
+{
+
+       return (stack_prot);
+}
+
+static void
+map_stacks_exec(RtldLockState *lockstate)
+{
+       return;
+       /*
+        * Stack protection must be implemented in the kernel before the dynamic
+        * linker can handle PT_GNU_STACK sections.
+        * The following is the FreeBSD implementation of map_stacks_exec()
+        * void (*thr_map_stacks_exec)(void);
+        *
+        * if ((max_stack_flags & PF_X) == 0 || (stack_prot & PROT_EXEC) != 0)
+        *     return;
+        * thr_map_stacks_exec = (void (*)(void))(uintptr_t)
+        *     get_program_var_addr("__pthread_map_stacks_exec", lockstate);
+        * if (thr_map_stacks_exec != NULL) {
+        *     stack_prot |= PROT_EXEC;
+        *     thr_map_stacks_exec();
+        * }
+        */
+}
+
 void
 symlook_init(SymLook *dst, const char *name)
 {
index 9b58185..7101a4f 100644 (file)
@@ -148,6 +148,7 @@ typedef struct Struct_Obj_Entry {
     const char *interp;                /* Pathname of the interpreter, if any */
     caddr_t relro_page;        /* Address of first page of read-only data */
     size_t relro_size;         /* Size of relro page(s) in bytes */
+    Elf_Word stack_flags;
 
     /* TLS information */
     int tlsindex;              /* Index in DTV for this module */
index b59fc18..8531a2c 100644 (file)
@@ -78,4 +78,7 @@ struct tls_tcb;
 extern void *__tls_get_addr(tls_index *ti);
 extern void *__tls_get_addr_tcb(struct tls_tcb *tcb, tls_index *ti);
 
+#define        RTLD_DEFAULT_STACK_PF_EXEC      PF_X
+#define        RTLD_DEFAULT_STACK_EXEC         PROT_EXEC
+
 #endif
index 4ec76ea..86a1425 100644 (file)
@@ -93,6 +93,7 @@ __BEGIN_DECLS
 typedef int (*__dl_iterate_hdr_callback)(struct dl_phdr_info *, size_t, void *);
 extern int dl_iterate_phdr(__dl_iterate_hdr_callback, void *);
 int _rtld_addr_phdr(const void *, struct dl_phdr_info *);
+int _rtld_get_stack_prot(void);
 
 __END_DECLS