From afa4092620919b39fcfa2d0c299410b18bcd0349 Mon Sep 17 00:00:00 2001 From: kib Date: Mon, 3 Dec 2018 20:03:43 +0000 Subject: [PATCH] Some fixes for LD_BIND_NOW + ifuncs. - Do not perform ifunc relocations together with other PLT relocations in PLT. Instead, do it during an additional pass over the init list, so that ifuncs are resolved in the order of dso dependencies. This allows the ifuncs resolvers to call into depended libs. Init list now includes all objects instead of only objects with init/fini callables. - Disable relro protection around bind_now ifunc relocations. I considered calling ifunc resolvers of dso after initializers of all dependencies are processed, and decided that this is wrong/should not be supported. The order now is normal relocations for all objects->ifunc resolution in init order->initializers, where each step does complete pass over all loaded objects before moving to the next step. Reported, tested and reviewed by: emaste Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D18400 --- libexec/rtld-elf/rtld.c | 91 ++++++++++++++++++++++------------------- libexec/rtld-elf/rtld.h | 1 + 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 5d0a182bcf4..80c78d9f21c 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -111,6 +111,7 @@ static void init_pagesizes(Elf_Auxinfo **aux_info); static void init_rtld(caddr_t, Elf_Auxinfo **); static void initlist_add_neededs(Needed_Entry *, Objlist *); static void initlist_add_objects(Obj_Entry *, Obj_Entry *, Objlist *); +static int initlist_objects_ifunc(Objlist *, bool, int, RtldLockState *); static void linkmap_add(Obj_Entry *); static void linkmap_delete(Obj_Entry *); static void load_filtees(Obj_Entry *, int flags, RtldLockState *); @@ -119,6 +120,7 @@ static int load_needed_objects(Obj_Entry *, int); static int load_preload_objects(void); static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int); static void map_stacks_exec(RtldLockState *); +static int obj_disable_relro(Obj_Entry *); static int obj_enforce_relro(Obj_Entry *); static Obj_Entry *obj_from_addr(const void *); static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *); @@ -143,8 +145,6 @@ static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, int, RtldLockState *); static int resolve_object_ifunc(Obj_Entry *, bool, int, RtldLockState *); -static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now, - int flags, RtldLockState *lockstate); static int rtld_dirname(const char *, char *); static int rtld_dirname_abs(const char *, char *); static void *rtld_dlopen(const char *name, int fd, int mode); @@ -730,16 +730,6 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) map_stacks_exec(NULL); - dbg("resolving ifuncs"); - if (resolve_objects_ifunc(obj_main, - ld_bind_now != NULL && *ld_bind_now != '\0', SYMLOOK_EARLY, - NULL) == -1) - rtld_die(); - - dbg("enforcing main obj relro"); - if (obj_enforce_relro(obj_main) == -1) - rtld_die(); - if (!obj_main->crt_no_init) { /* * Make sure we don't call the main program's init and fini @@ -758,6 +748,12 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) pre_init(); wlock_acquire(rtld_bind_lock, &lockstate); + + dbg("resolving ifuncs"); + if (initlist_objects_ifunc(&initlist, ld_bind_now != NULL && + *ld_bind_now != '\0', SYMLOOK_EARLY, &lockstate) == -1) + rtld_die(); + if (obj_main->crt_no_init) preinit_main(); objlist_call_init(&initlist, &lockstate); @@ -770,6 +766,11 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) if (ld_loadfltr || obj->z_loadfltr) load_filtees(obj, 0, &lockstate); } + + dbg("enforcing main obj relro"); + if (obj_enforce_relro(obj_main) == -1) + rtld_die(); + lock_release(rtld_bind_lock, &lockstate); dbg("transferring control to program entry point = %p", obj_main->entry); @@ -2243,9 +2244,7 @@ initlist_add_objects(Obj_Entry *obj, Obj_Entry *tail, Objlist *list) initlist_add_neededs(obj->needed_aux_filtees, list); /* Add the object to the init list. */ - if (obj->preinit_array != (Elf_Addr)NULL || obj->init != (Elf_Addr)NULL || - obj->init_array != (Elf_Addr)NULL) - objlist_push_tail(list, obj); + objlist_push_tail(list, obj); /* Add the object to the global fini list in the reverse order. */ if ((obj->fini != (Elf_Addr)NULL || obj->fini_array != (Elf_Addr)NULL) @@ -2894,11 +2893,9 @@ relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, if (reloc_plt(obj) == -1) return (-1); /* Relocate the jump slots if we are doing immediate binding. */ - if (obj->bind_now || bind_now) { - if (reloc_jmpslots(obj, flags, lockstate) == -1 || - resolve_object_ifunc(obj, true, flags, lockstate) == -1) - return (-1); - } + if ((obj->bind_now || bind_now) && reloc_jmpslots(obj, flags, + lockstate) == -1) + return (-1); /* * Process the non-PLT IFUNC relocations. The relocations are @@ -2964,24 +2961,16 @@ static int resolve_object_ifunc(Obj_Entry *obj, bool bind_now, int flags, RtldLockState *lockstate) { + + if (obj->ifuncs_resolved) + return (0); + obj->ifuncs_resolved = true; if (obj->irelative && reloc_iresolve(obj, lockstate) == -1) return (-1); - if ((obj->bind_now || bind_now) && obj->gnu_ifunc && - reloc_gnu_ifunc(obj, flags, lockstate) == -1) - return (-1); - return (0); -} - -static int -resolve_objects_ifunc(Obj_Entry *first, bool bind_now, int flags, - RtldLockState *lockstate) -{ - Obj_Entry *obj; - - for (obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) { - if (obj->marker) - continue; - if (resolve_object_ifunc(obj, bind_now, flags, lockstate) == -1) + if ((obj->bind_now || bind_now) && obj->gnu_ifunc) { + if (obj_disable_relro(obj) || + reloc_gnu_ifunc(obj, flags, lockstate) == -1 || + obj_enforce_relro(obj)) return (-1); } return (0); @@ -2992,9 +2981,13 @@ initlist_objects_ifunc(Objlist *list, bool bind_now, int flags, RtldLockState *lockstate) { Objlist_Entry *elm; + Obj_Entry *obj; STAILQ_FOREACH(elm, list, link) { - if (resolve_object_ifunc(elm->obj, bind_now, flags, + obj = elm->obj; + if (obj->marker) + continue; + if (resolve_object_ifunc(obj, bind_now, flags, lockstate) == -1) return (-1); } @@ -5354,19 +5347,33 @@ _rtld_is_dlopened(void *arg) return (res); } -int -obj_enforce_relro(Obj_Entry *obj) +static int +obj_remap_relro(Obj_Entry *obj, int prot) { if (obj->relro_size > 0 && mprotect(obj->relro_page, obj->relro_size, - PROT_READ) == -1) { - _rtld_error("%s: Cannot enforce relro protection: %s", - obj->path, rtld_strerror(errno)); + prot) == -1) { + _rtld_error("%s: Cannot set relro protection to %#x: %s", + obj->path, prot, rtld_strerror(errno)); return (-1); } return (0); } +static int +obj_disable_relro(Obj_Entry *obj) +{ + + return (obj_remap_relro(obj, PROT_READ | PROT_WRITE)); +} + +static int +obj_enforce_relro(Obj_Entry *obj) +{ + + return (obj_remap_relro(obj, PROT_READ)); +} + static void map_stacks_exec(RtldLockState *lockstate) { diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 335971eb13e..c7b2b7a896d 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -264,6 +264,7 @@ typedef struct Struct_Obj_Entry { bool irelative : 1; /* Object has R_MACHDEP_IRELATIVE relocs */ bool gnu_ifunc : 1; /* Object has references to STT_GNU_IFUNC */ bool non_plt_gnu_ifunc : 1; /* Object has non-plt IFUNC references */ + bool ifuncs_resolved : 1; /* Object ifuncs were already resolved */ bool crt_no_init : 1; /* Object' crt does not call _init/_fini */ bool valid_hash_sysv : 1; /* A valid System V hash hash tag is available */ bool valid_hash_gnu : 1; /* A valid GNU hash tag is available */ -- 2.45.0