summaryrefslogtreecommitdiffstats
path: root/libexec/rtld-elf/rtld.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2010-11-03 09:23:08 +0000
committerkib <kib@FreeBSD.org>2010-11-03 09:23:08 +0000
commit6ddde2168bc79a10ab0937ba69afe0a74559eea0 (patch)
tree32512f3bc1869db4dad1920d6d6d20fa63b1d63e /libexec/rtld-elf/rtld.c
parentd83519c991663fe77cf7bd81dbd86ef38d115065 (diff)
downloadFreeBSD-src-6ddde2168bc79a10ab0937ba69afe0a74559eea0.zip
FreeBSD-src-6ddde2168bc79a10ab0937ba69afe0a74559eea0.tar.gz
If dlopen() is called for the dso that has been already loaded as a
dependency, then the dso never has its DAG initialized. Empty DAG makes ref_dag() call in dlopen() a nop, and the dso refcount is off by one. Initialize the DAG on the first dlopen() call, using a boolean flag to prevent double initialization. From the PR (edited): Assume we have a library liba.so, containing a function a(), and a library libb.so, containing function b(). liba.so needs functionality from libb.so, so liba.so links in libb.so. An application doesn't know about the relation between these libraries, but needs to call a() and b(). It dlopen()s liba.so and obtains a pointer to a(), then it dlopen()s libb.so and obtains a pointer to b(). As soon as the application doesn't need a() anymore, it dlclose()s liba.so. Expected result: the pointer to b() is still valid and can be called Actual result: the pointer to b() has become invalid, even though the application did not dlclose() the handle to libb.so. On calling b(), the application crashes with a segmentation fault. PR: misc/151861 Based on patch by: jh Reviewed by: kan Tested by: Arjan van Leeuwen <freebsd-maintainer opera com> MFC after: 1 week
Diffstat (limited to 'libexec/rtld-elf/rtld.c')
-rw-r--r--libexec/rtld-elf/rtld.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 8bc8692..f1ffc3e 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -1275,8 +1275,11 @@ init_dag(Obj_Entry *root)
{
DoneList donelist;
+ if (root->dag_inited)
+ return;
donelist_init(&donelist);
init_dag1(root, root, &donelist);
+ root->dag_inited = true;
}
static void
@@ -2045,8 +2048,16 @@ dlopen(const char *name, int mode)
}
} else {
- /* Bump the reference counts for objects on this DAG. */
- ref_dag(obj);
+ /*
+ * Bump the reference counts for objects on this DAG. If
+ * this is the first dlopen() call for the object that was
+ * already loaded as a dependency, initialize the dag
+ * starting at it.
+ */
+ if (obj->dl_refcount == 1)
+ init_dag(obj);
+ else
+ ref_dag(obj);
if (ld_tracing)
goto trace;
OpenPOWER on IntegriCloud