Propagate the current state of rtld_bind_lock to dlopen_object() calls

through the filter loading call chain. This fixes attempts to
write-lock the already locked rtld_bind_lock when filter loading is
initiated by relocation of dlopening dso.

Reported and tested by:	Taku YAMAMOTO <taku tackymt homeip net>
MFC after:	1 week
This commit is contained in:
kib 2012-04-12 10:32:22 +00:00
parent 8827ec0618
commit 238a397e46

View File

@ -85,7 +85,7 @@ static void digest_dynamic(Obj_Entry *, int);
static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *); static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
static Obj_Entry *dlcheck(void *); static Obj_Entry *dlcheck(void *);
static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj, static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj,
int lo_flags, int mode); int lo_flags, int mode, RtldLockState *lockstate);
static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int); static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int);
static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *); static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
static bool donelist_check(DoneList *, const Obj_Entry *); static bool donelist_check(DoneList *, const Obj_Entry *);
@ -1672,13 +1672,14 @@ unload_filtees(Obj_Entry *obj)
} }
static void static void
load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags) load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags,
RtldLockState *lockstate)
{ {
for (; needed != NULL; needed = needed->next) { for (; needed != NULL; needed = needed->next) {
needed->obj = dlopen_object(obj->strtab + needed->name, -1, obj, needed->obj = dlopen_object(obj->strtab + needed->name, -1, obj,
flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) | flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) |
RTLD_LOCAL); RTLD_LOCAL, lockstate);
} }
} }
@ -1688,8 +1689,8 @@ load_filtees(Obj_Entry *obj, int flags, RtldLockState *lockstate)
lock_restart_for_upgrade(lockstate); lock_restart_for_upgrade(lockstate);
if (!obj->filtees_loaded) { if (!obj->filtees_loaded) {
load_filtee1(obj, obj->needed_filtees, flags); load_filtee1(obj, obj->needed_filtees, flags, lockstate);
load_filtee1(obj, obj->needed_aux_filtees, flags); load_filtee1(obj, obj->needed_aux_filtees, flags, lockstate);
obj->filtees_loaded = true; obj->filtees_loaded = true;
} }
} }
@ -2489,7 +2490,7 @@ rtld_dlopen(const char *name, int fd, int mode)
lo_flags |= RTLD_LO_TRACE; lo_flags |= RTLD_LO_TRACE;
return (dlopen_object(name, fd, obj_main, lo_flags, return (dlopen_object(name, fd, obj_main, lo_flags,
mode & (RTLD_MODEMASK | RTLD_GLOBAL))); mode & (RTLD_MODEMASK | RTLD_GLOBAL), NULL));
} }
static void static void
@ -2504,17 +2505,20 @@ dlopen_cleanup(Obj_Entry *obj)
static Obj_Entry * static Obj_Entry *
dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags, dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags,
int mode) int mode, RtldLockState *lockstate)
{ {
Obj_Entry **old_obj_tail; Obj_Entry **old_obj_tail;
Obj_Entry *obj; Obj_Entry *obj;
Objlist initlist; Objlist initlist;
RtldLockState lockstate; RtldLockState mlockstate;
int result; int result;
objlist_init(&initlist); objlist_init(&initlist);
wlock_acquire(rtld_bind_lock, &lockstate); if (lockstate == NULL && !(lo_flags & RTLD_LO_EARLY)) {
wlock_acquire(rtld_bind_lock, &mlockstate);
lockstate = &mlockstate;
}
GDB_STATE(RT_ADD,NULL); GDB_STATE(RT_ADD,NULL);
old_obj_tail = obj_tail; old_obj_tail = obj_tail;
@ -2543,7 +2547,7 @@ dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags,
if (result == -1 || (relocate_objects(obj, if (result == -1 || (relocate_objects(obj,
(mode & RTLD_MODEMASK) == RTLD_NOW, &obj_rtld, (mode & RTLD_MODEMASK) == RTLD_NOW, &obj_rtld,
(lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0, (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0,
&lockstate)) == -1) { lockstate)) == -1) {
dlopen_cleanup(obj); dlopen_cleanup(obj);
obj = NULL; obj = NULL;
} else if (lo_flags & RTLD_LO_EARLY) { } else if (lo_flags & RTLD_LO_EARLY) {
@ -2587,28 +2591,31 @@ dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags,
GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL); GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL);
if (!(lo_flags & RTLD_LO_EARLY)) { if (!(lo_flags & RTLD_LO_EARLY)) {
map_stacks_exec(&lockstate); map_stacks_exec(lockstate);
} }
if (initlist_objects_ifunc(&initlist, (mode & RTLD_MODEMASK) == RTLD_NOW, if (initlist_objects_ifunc(&initlist, (mode & RTLD_MODEMASK) == RTLD_NOW,
(lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0, (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0,
&lockstate) == -1) { lockstate) == -1) {
objlist_clear(&initlist); objlist_clear(&initlist);
dlopen_cleanup(obj); dlopen_cleanup(obj);
lock_release(rtld_bind_lock, &lockstate); if (lockstate == &mlockstate)
lock_release(rtld_bind_lock, lockstate);
return (NULL); return (NULL);
} }
if (!(lo_flags & RTLD_LO_EARLY)) { if (!(lo_flags & RTLD_LO_EARLY)) {
/* Call the init functions. */ /* Call the init functions. */
objlist_call_init(&initlist, &lockstate); objlist_call_init(&initlist, lockstate);
} }
objlist_clear(&initlist); objlist_clear(&initlist);
lock_release(rtld_bind_lock, &lockstate); if (lockstate == &mlockstate)
lock_release(rtld_bind_lock, lockstate);
return obj; return obj;
trace: trace:
trace_loaded_objects(obj); trace_loaded_objects(obj);
lock_release(rtld_bind_lock, &lockstate); if (lockstate == &mlockstate)
lock_release(rtld_bind_lock, lockstate);
exit(0); exit(0);
} }