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
This commit is contained in:
Konstantin Belousov 2018-12-03 20:03:43 +00:00
parent d7a570379b
commit 4903c73faf
2 changed files with 50 additions and 42 deletions

View File

@ -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,8 +2244,6 @@ 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);
/* Add the object to the global fini list in the reverse order. */
@ -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)
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)
{

View File

@ -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 */