# HG changeset patch # User Taylor R Campbell # Date 1763829582 0 # Sat Nov 22 16:39:42 2025 +0000 # Branch trunk # Node ID a048dd8d8fbd4ad97bbf3697ba5b834d4cfbc192 # Parent 03d10f218fce5630dbd63fdd135fec096db9f6ca # EXP-Topic riastradh-pr59784-dlcloselibpthread tests/lib/libpthread: Test unloading libpthread after lazy binding. If you dlopen libpthread and dlclose it again, the thread stubs like pthread_mutex_lock need to continue working -- a library might have calls to it in order to support thread-safety for threaded applications, but that library needs to continue working even in non-threaded applications after lazy binding of the libpthread symbol instead of the libc stub. PR lib/59784: dlopening and dlclosing libpthread is broken diff -r 03d10f218fce -r a048dd8d8fbd tests/lib/libpthread/dlopen/t_dlopen.c --- a/tests/lib/libpthread/dlopen/t_dlopen.c Sun Oct 19 18:56:19 2025 +0000 +++ b/tests/lib/libpthread/dlopen/t_dlopen.c Sat Nov 22 16:39:42 2025 +0000 @@ -37,23 +37,17 @@ __RCSID("$NetBSD: t_dlopen.c,v 1.1 2013/ #include #include #include +#include #include -ATF_TC(dlopen); - -ATF_TC_HEAD(dlopen, tc) -{ - atf_tc_set_md_var(tc, "descr", - "Test if dlopen can load -lpthread DSO"); -} - #define DSO TESTDIR "/h_pthread_dlopen.so" -ATF_TC_BODY(dlopen, tc) +static void +test_dlopen(int flags) { void *handle; int (*testf_dso_null)(void); - handle = dlopen(DSO, RTLD_NOW | RTLD_LOCAL); + handle = dlopen(DSO, flags); ATF_REQUIRE_MSG(handle != NULL, "dlopen fails: %s", dlerror()); testf_dso_null = dlsym(handle, "testf_dso_null"); @@ -64,15 +58,30 @@ ATF_TC_BODY(dlopen, tc) ATF_REQUIRE(dlclose(handle) == 0); } -ATF_TC(dlopen_mutex); - -ATF_TC_HEAD(dlopen_mutex, tc) +ATF_TC(dlopen); +ATF_TC_HEAD(dlopen, tc) { atf_tc_set_md_var(tc, "descr", - "Test if dlopen can load -lpthread DSO without breaking mutex"); + "Test if dlopen can load -lpthread DSO"); +} +ATF_TC_BODY(dlopen, tc) +{ + test_dlopen(RTLD_NOW | RTLD_LOCAL); } -ATF_TC_BODY(dlopen_mutex, tc) +ATF_TC(dlopen_lazyglobal); +ATF_TC_HEAD(dlopen_lazyglobal, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test if dlopen can load -lpthread DSO"); +} +ATF_TC_BODY(dlopen_lazyglobal, tc) +{ + test_dlopen(RTLD_LAZY | RTLD_GLOBAL); +} + +static void +test_dlopen_mutex(int flags) { pthread_mutex_t mtx; void *handle; @@ -81,7 +90,7 @@ ATF_TC_BODY(dlopen_mutex, tc) ATF_REQUIRE(pthread_mutex_init(&mtx, NULL) == 0); ATF_REQUIRE(pthread_mutex_lock(&mtx) == 0); - handle = dlopen(DSO, RTLD_NOW | RTLD_LOCAL); + handle = dlopen(DSO, flags); ATF_REQUIRE_MSG(handle != NULL, "dlopen fails: %s", dlerror()); testf_dso_null = dlsym(handle, "testf_dso_null"); @@ -93,18 +102,38 @@ ATF_TC_BODY(dlopen_mutex, tc) ATF_REQUIRE(dlclose(handle) == 0); + ATF_REQUIRE(pthread_mutex_lock(&mtx) == 0); + ATF_REQUIRE(pthread_mutex_unlock(&mtx) == 0); + pthread_mutex_destroy(&mtx); } -ATF_TC(dlopen_mutex_libc); - -ATF_TC_HEAD(dlopen_mutex_libc, tc) +ATF_TC(dlopen_mutex); +ATF_TC_HEAD(dlopen_mutex, tc) { atf_tc_set_md_var(tc, "descr", - "Test if dlopen can load -lpthread DSO and use libc locked mutex"); + "Test if dlopen can load -lpthread DSO without breaking mutex"); +} +ATF_TC_BODY(dlopen_mutex, tc) +{ + test_dlopen_mutex(RTLD_NOW | RTLD_LOCAL); } -ATF_TC_BODY(dlopen_mutex_libc, tc) +ATF_TC(dlopen_mutex_lazyglobal); +ATF_TC_HEAD(dlopen_mutex_lazyglobal, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test if dlopen can load -lpthread DSO without breaking mutex"); +} +ATF_TC_BODY(dlopen_mutex_lazyglobal, tc) +{ + atf_tc_expect_signal(SIGSEGV, "PR lib/59784: " + "dlopening and dlclosing libpthread is broken"); + test_dlopen_mutex(RTLD_LAZY | RTLD_GLOBAL); +} + +static void +test_dlopen_mutex_libc(int flags) { pthread_mutex_t mtx; void *handle; @@ -113,7 +142,7 @@ ATF_TC_BODY(dlopen_mutex_libc, tc) ATF_REQUIRE(pthread_mutex_init(&mtx, NULL) == 0); ATF_REQUIRE(pthread_mutex_lock(&mtx) == 0); - handle = dlopen(DSO, RTLD_NOW | RTLD_LOCAL); + handle = dlopen(DSO, flags); ATF_REQUIRE_MSG(handle != NULL, "dlopen fails: %s", dlerror()); testf_dso_mutex_unlock = dlsym(handle, "testf_dso_mutex_unlock"); @@ -124,19 +153,36 @@ ATF_TC_BODY(dlopen_mutex_libc, tc) dlclose(handle); + ATF_REQUIRE(pthread_mutex_lock(&mtx) == 0); + ATF_REQUIRE(pthread_mutex_unlock(&mtx) == 0); + pthread_mutex_destroy(&mtx); } -ATF_TC(dlopen_mutex_libpthread); - -ATF_TC_HEAD(dlopen_mutex_libpthread, tc) +ATF_TC(dlopen_mutex_libc); +ATF_TC_HEAD(dlopen_mutex_libc, tc) { atf_tc_set_md_var(tc, "descr", - "Test if dlopen can load -lpthread DSO and use " - "libpthread locked mutex"); + "Test if dlopen can load -lpthread DSO and use libc locked mutex"); +} +ATF_TC_BODY(dlopen_mutex_libc, tc) +{ + test_dlopen_mutex_libc(RTLD_NOW | RTLD_LOCAL); } -ATF_TC_BODY(dlopen_mutex_libpthread, tc) +ATF_TC(dlopen_mutex_libc_lazyglobal); +ATF_TC_HEAD(dlopen_mutex_libc_lazyglobal, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test if dlopen can load -lpthread DSO and use libc locked mutex"); +} +ATF_TC_BODY(dlopen_mutex_libc_lazyglobal, tc) +{ + test_dlopen_mutex_libc(RTLD_LAZY | RTLD_GLOBAL); +} + +static void +test_dlopen_mutex_libpthread(int flags) { pthread_mutex_t mtx; void *handle; @@ -144,7 +190,7 @@ ATF_TC_BODY(dlopen_mutex_libpthread, tc) ATF_REQUIRE(pthread_mutex_init(&mtx, NULL) == 0); - handle = dlopen(DSO, RTLD_NOW | RTLD_LOCAL); + handle = dlopen(DSO, flags); ATF_REQUIRE_MSG(handle != NULL, "dlopen fails: %s", dlerror()); testf_dso_mutex_lock = dlsym(handle, "testf_dso_mutex_lock"); @@ -157,15 +203,50 @@ ATF_TC_BODY(dlopen_mutex_libpthread, tc) dlclose(handle); + ATF_REQUIRE(pthread_mutex_lock(&mtx) == 0); + ATF_REQUIRE(pthread_mutex_unlock(&mtx) == 0); + pthread_mutex_destroy(&mtx); } +ATF_TC(dlopen_mutex_libpthread); +ATF_TC_HEAD(dlopen_mutex_libpthread, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test if dlopen can load -lpthread DSO and use " + "libpthread locked mutex"); +} +ATF_TC_BODY(dlopen_mutex_libpthread, tc) +{ + test_dlopen_mutex_libpthread(RTLD_NOW | RTLD_LOCAL); +} + +ATF_TC(dlopen_mutex_libpthread_lazyglobal); +ATF_TC_HEAD(dlopen_mutex_libpthread_lazyglobal, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test if dlopen can load -lpthread DSO and use " + "libpthread locked mutex"); +} +ATF_TC_BODY(dlopen_mutex_libpthread_lazyglobal, tc) +{ + atf_tc_expect_signal(SIGSEGV, "PR lib/59784: " + "dlopening and dlclosing libpthread is broken"); + test_dlopen_mutex_libpthread(RTLD_LAZY | RTLD_GLOBAL); +} + ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, dlopen); ATF_TP_ADD_TC(tp, dlopen_mutex); ATF_TP_ADD_TC(tp, dlopen_mutex_libc); ATF_TP_ADD_TC(tp, dlopen_mutex_libpthread); + ATF_TP_ADD_TC(tp, dlopen_lazyglobal); + ATF_TP_ADD_TC(tp, dlopen_mutex_lazyglobal); + ATF_TP_ADD_TC(tp, dlopen_mutex_libc_lazyglobal); + ATF_TP_ADD_TC(tp, dlopen_mutex_libpthread_lazyglobal); + return atf_no_error(); } # HG changeset patch # User Taylor R Campbell # Date 1763829961 0 # Sat Nov 22 16:46:01 2025 +0000 # Branch trunk # Node ID a7a1cbce5305c206e4af89b2991abeda10c6af31 # Parent a048dd8d8fbd4ad97bbf3697ba5b834d4cfbc192 # EXP-Topic riastradh-pr59784-dlcloselibpthread libpthread: Link with -Wl,-z,nodelete. Can't safely unload libpthread because of the interaction with libc thread stubs. PR lib/59784: dlopening and dlclosing libpthread is broken diff -r a048dd8d8fbd -r a7a1cbce5305 UPDATING --- a/UPDATING Sat Nov 22 16:39:42 2025 +0000 +++ b/UPDATING Sat Nov 22 16:46:01 2025 +0000 @@ -19,6 +19,10 @@ See also: BUILDING, build.sh, Makefile. Recent changes: ^^^^^^^^^^^^^^^ +20251122: + Changes to libpthread for PR 59784 require you to make clean in + lib/libpthread so it is rebuilt with updated ld flags. + 20250721: GCC 12.5 was imported and there may be weird build issues. Clean any GCC build dirs if you encounter weird things. diff -r a048dd8d8fbd -r a7a1cbce5305 lib/libpthread/Makefile --- a/lib/libpthread/Makefile Sat Nov 22 16:39:42 2025 +0000 +++ b/lib/libpthread/Makefile Sat Nov 22 16:46:01 2025 +0000 @@ -65,6 +65,22 @@ CPPFLAGS+=-I${NETBSDSRCDIR}/sys -I${.CUR # set by the rumprun software stacks (see https://github.com/rumpkernel/rumprun ) PTHREAD_MAKELWP?= pthread_makelwp_netbsd.c +# Prevent unloading libpthread. +# +# libpthread provides strong definitions of some symbols defined weakly +# by libc, such as pthread_mutex_lock (actually __libc_mutex_lock under +# the hood), which are used in libraries that take locks for +# thread-safety -- this way, in non-threaded applications, +# pthread_mutex_lock is a cheap noop libc stub, while in threaded +# applications, it's a real libpthread operation. If libpthread is +# dlopened, the symbol might be lazily resolved _after_ it is dlopened +# to point at libpthread; then it cannot be safely unloaded, because +# there's no way to roll back symbol resolution. +# +# PR lib/59784: dlopening and dlclosing libpthread is broken +# +LDADD+= -Wl,-z,nodelete + # # NOTE: When you create a new file for libpthread, make sure that pthread.c # gets a reference to a symbol in that file. Otherwise, Unix's stupid static diff -r a048dd8d8fbd -r a7a1cbce5305 tests/lib/libpthread/dlopen/t_dlopen.c --- a/tests/lib/libpthread/dlopen/t_dlopen.c Sat Nov 22 16:39:42 2025 +0000 +++ b/tests/lib/libpthread/dlopen/t_dlopen.c Sat Nov 22 16:46:01 2025 +0000 @@ -127,8 +127,6 @@ ATF_TC_HEAD(dlopen_mutex_lazyglobal, tc) } ATF_TC_BODY(dlopen_mutex_lazyglobal, tc) { - atf_tc_expect_signal(SIGSEGV, "PR lib/59784: " - "dlopening and dlclosing libpthread is broken"); test_dlopen_mutex(RTLD_LAZY | RTLD_GLOBAL); } @@ -230,8 +228,6 @@ ATF_TC_HEAD(dlopen_mutex_libpthread_lazy } ATF_TC_BODY(dlopen_mutex_libpthread_lazyglobal, tc) { - atf_tc_expect_signal(SIGSEGV, "PR lib/59784: " - "dlopening and dlclosing libpthread is broken"); test_dlopen_mutex_libpthread(RTLD_LAZY | RTLD_GLOBAL); } # HG changeset patch # User Taylor R Campbell # Date 1763830401 0 # Sat Nov 22 16:53:21 2025 +0000 # Branch trunk # Node ID d8b712bc72323e84144c97e0d517b6a577629357 # Parent a7a1cbce5305c206e4af89b2991abeda10c6af31 # EXP-Topic riastradh-pr59784-dlcloselibpthread tests/lib/libpthread: Don't abuse xfail. Use a signal handler to check for SIGABRT, rather than atf_tc_expect_signal. xfail is for when there is a bug that we haven't fixed yet and the test manifests a symptom of that bug -- a list of xfails is a list of open bugs to be fixed. In this case, we are verifying that pthread_create _correctly_ raises SIGABRT (or fails with nonzero return code -- both are acceptable outcomes, really), and there is no bug here at the moment. Prompted by (but unrelated to): PR lib/59784: dlopening and dlclosing libpthread is broken diff -r a7a1cbce5305 -r d8b712bc7232 tests/lib/libpthread/dlopen/t_dso_pthread_create.c --- a/tests/lib/libpthread/dlopen/t_dso_pthread_create.c Sat Nov 22 16:46:01 2025 +0000 +++ b/tests/lib/libpthread/dlopen/t_dso_pthread_create.c Sat Nov 22 16:53:21 2025 +0000 @@ -34,12 +34,15 @@ #include __RCSID("$NetBSD: t_dso_pthread_create.c,v 1.1 2013/03/21 16:50:21 christos Exp $"); -#include #include #include #include +#include +#include #include +#include "../../../h_macros.h" + #define DSO TESTDIR "/h_pthread_dlopen.so" void * @@ -49,6 +52,17 @@ routine(void *arg) return NULL; } +static jmp_buf abort_aborting; + +static void +handle_sigabrt(int signo) +{ + + ATF_REQUIRE_EQ_MSG(signo, SIGABRT, "signo=%d (%s)", signo, + strsignal(signo)); + longjmp(abort_aborting, 1); +} + ATF_TC(dso_pthread_create_dso); ATF_TC_HEAD(dso_pthread_create_dso, tc) @@ -64,25 +78,24 @@ ATF_TC_BODY(dso_pthread_create_dso, tc) pthread_t thread; void *arg = (void *)0xcafe; void *handle; - int (*testf_dso_pthread_create)(pthread_t *, pthread_attr_t *, + int (*testf_dso_pthread_create)(pthread_t *, pthread_attr_t *, void *(*)(void *), void *); - struct rlimit rl; - - atf_tc_expect_signal(6, - "calling pthread_create() requires -lpthread main"); - - rl.rlim_max = rl.rlim_cur = 0; - ATF_REQUIRE_EQ(setrlimit(RLIMIT_CORE, &rl), 0); + void (*sighandler)(int); handle = dlopen(DSO, RTLD_NOW | RTLD_LOCAL); ATF_REQUIRE_MSG(handle != NULL, "dlopen fails: %s", dlerror()); testf_dso_pthread_create = dlsym(handle, "testf_dso_pthread_create"); - ATF_REQUIRE_MSG(testf_dso_pthread_create != NULL, + ATF_REQUIRE_MSG(testf_dso_pthread_create != NULL, "dlsym fails: %s", dlerror()); - ret = testf_dso_pthread_create(&thread, NULL, routine, arg); - ATF_REQUIRE(ret == 0); + REQUIRE_LIBC((sighandler = signal(SIGABRT, &handle_sigabrt)), SIG_ERR); + if (setjmp(abort_aborting) == 0) { + ret = testf_dso_pthread_create(&thread, NULL, routine, arg); + ATF_REQUIRE_MSG(ret != 0, + "pthread_create unexpectedly succeeded"); + } + REQUIRE_LIBC(signal(SIGABRT, sighandler), SIG_ERR); ATF_REQUIRE(dlclose(handle) == 0);