git: 1426fd6cff06 - main - rtld: implement _dl_iterate_phdr_locked

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Thu, 14 Nov 2024 01:34:28 UTC
The branch main has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=1426fd6cff0603f0ee275b99f2ba35dc36f3d0c2

commit 1426fd6cff0603f0ee275b99f2ba35dc36f3d0c2
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2024-11-14 01:33:59 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2024-11-14 01:33:59 +0000

    rtld: implement _dl_iterate_phdr_locked
    
    Some sanitizers need to be able to use dl_iterate_phdr() after stopping
    the rest of the process, but it's very hard to do so reliably as a
    non-participant in the main logic of the program.
    
    Introduce _dl_iterate_phdr_locked to bypass the locking that's normally
    required for dl_iterate_phdr() and slap some scary warning on it.  It
    will remain undocumented and probably shouldn't be used for anything
    else.
    
    Reviewed by:    kib
    Differential Revision:  https://reviews.freebsd.org/D47558
---
 lib/libc/gen/Symbol.map     |  1 +
 lib/libc/gen/dlfcn.c        | 24 ++++++++++++++++++++----
 libexec/rtld-elf/Symbol.map |  1 +
 libexec/rtld-elf/rtld.c     | 24 ++++++++++++++++++++++++
 4 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map
index df950697cf23..4f77023dad72 100644
--- a/lib/libc/gen/Symbol.map
+++ b/lib/libc/gen/Symbol.map
@@ -464,6 +464,7 @@ FBSDprivate_1.0 {
 	/* needed by thread libraries */
 	__thr_jtable;
 
+	_dl_iterate_phdr_locked;
 	_pthread_atfork;
 	_pthread_attr_destroy;
 	_pthread_attr_getdetachstate;
diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c
index b30cb82e5e43..b814fdc845c4 100644
--- a/lib/libc/gen/dlfcn.c
+++ b/lib/libc/gen/dlfcn.c
@@ -203,9 +203,10 @@ dl_init_phdr_info(void)
 }
 #endif
 
-#pragma weak dl_iterate_phdr
+#pragma weak _dl_iterate_phdr_locked
 int
-dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *) __unused,
+_dl_iterate_phdr_locked(
+    int (*callback)(struct dl_phdr_info *, size_t, void *) __unused,
     void *data __unused)
 {
 #if defined IN_LIBDL
@@ -227,14 +228,29 @@ dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *) __unused,
 	_once(&dl_phdr_info_once, dl_init_phdr_info);
 	ti.ti_module = 1;
 	ti.ti_offset = 0;
-	mutex_lock(&dl_phdr_info_lock);
 	phdr_info.dlpi_tls_data = __tls_get_addr(&ti);
 	ret = callback(&phdr_info, sizeof(phdr_info), data);
-	mutex_unlock(&dl_phdr_info_lock);
 	return (ret);
 #endif
 }
 
+#pragma weak dl_iterate_phdr
+int
+dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *) __unused,
+    void *data __unused)
+{
+	int error;
+
+#if !defined(IN_LIBDL) && !defined(PIC)
+	mutex_lock(&dl_phdr_info_lock);
+#endif
+	error = _dl_iterate_phdr_locked(callback, data);
+#if !defined(IN_LIBDL) && !defined(PIC)
+	mutex_unlock(&dl_phdr_info_lock);
+#endif
+	return (error);
+}
+
 #pragma weak fdlopen
 void *
 fdlopen(int fd __unused, int mode __unused)
diff --git a/libexec/rtld-elf/Symbol.map b/libexec/rtld-elf/Symbol.map
index 3cdbb30d04a0..9e9e702a1261 100644
--- a/libexec/rtld-elf/Symbol.map
+++ b/libexec/rtld-elf/Symbol.map
@@ -27,6 +27,7 @@ FBSD_1.8 {
 };
 
 FBSDprivate_1.0 {
+    _dl_iterate_phdr_locked;
     _rtld_thread_init;
     _rtld_allocate_tls;
     _rtld_free_tls;
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index f1075bc75d5f..9e5876e26453 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -252,6 +252,7 @@ int dladdr(const void *, Dl_info *) __exported;
 void dllockinit(void *, void *(*)(void *), void (*)(void *), void (*)(void *),
     void (*)(void *), void (*)(void *), void (*)(void *)) __exported;
 int dlinfo(void *, int , void *) __exported;
+int _dl_iterate_phdr_locked(__dl_iterate_hdr_callback, void *) __exported;
 int dl_iterate_phdr(__dl_iterate_hdr_callback, void *) __exported;
 int _rtld_addr_phdr(const void *, struct dl_phdr_info *) __exported;
 int _rtld_get_stack_prot(void) __exported;
@@ -4201,6 +4202,29 @@ rtld_fill_dl_phdr_info(const Obj_Entry *obj, struct dl_phdr_info *phdr_info)
 	phdr_info->dlpi_subs = obj_loads - obj_count;
 }
 
+/*
+ * It's completely UB to actually use this, so extreme caution is advised.  It's
+ * probably not what you want.
+ */
+int
+_dl_iterate_phdr_locked(__dl_iterate_hdr_callback callback, void *param)
+{
+	struct dl_phdr_info phdr_info;
+	Obj_Entry *obj;
+	int error;
+
+	for (obj = globallist_curr(TAILQ_FIRST(&obj_list)); obj != NULL;
+	    obj = globallist_next(obj)) {
+		rtld_fill_dl_phdr_info(obj, &phdr_info);
+		error = callback(&phdr_info, sizeof(phdr_info), param);
+		if (error != 0)
+			return (error);
+	}
+
+	rtld_fill_dl_phdr_info(&obj_rtld, &phdr_info);
+	return (callback(&phdr_info, sizeof(phdr_info), param));
+}
+
 int
 dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param)
 {