From: John Marino Date: Sat, 21 Jan 2012 20:53:12 +0000 (+0100) Subject: rtld: Add stubs to support PT_GNU_STACK X-Git-Tag: v3.4.0rc~1352 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/e9de6dccf96c643b779ee1f2d5de6a19375e7fa8 rtld: Add stubs to support PT_GNU_STACK 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. --- diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c index 29f98d71ca..381c659b1a 100644 --- a/lib/libc/gen/dlfcn.c +++ b/lib/libc/gen/dlfcn.c @@ -26,6 +26,7 @@ * $FreeBSD: src/lib/libc/gen/dlfcn.c 217154 2011-01-08 17:13:43Z kib $ */ +#include #include #include #include @@ -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); +} diff --git a/libexec/rtld-elf/i386/rtld_machdep.h b/libexec/rtld-elf/i386/rtld_machdep.h index a69eb3ae51..2e47438f45 100644 --- a/libexec/rtld-elf/i386/rtld_machdep.h +++ b/libexec/rtld-elf/i386/rtld_machdep.h @@ -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 diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index cb85579579..f33c839c62 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -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); diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index a165c6402c..cb80a31246 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -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) { diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 9b58185434..7101a4fc5c 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -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 */ diff --git a/libexec/rtld-elf/x86_64/rtld_machdep.h b/libexec/rtld-elf/x86_64/rtld_machdep.h index b59fc18bb8..8531a2c478 100644 --- a/libexec/rtld-elf/x86_64/rtld_machdep.h +++ b/libexec/rtld-elf/x86_64/rtld_machdep.h @@ -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 diff --git a/sys/sys/link_elf.h b/sys/sys/link_elf.h index 4ec76eae56..86a1425617 100644 --- a/sys/sys/link_elf.h +++ b/sys/sys/link_elf.h @@ -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