https://github.com/edcsnt updated https://github.com/llvm/llvm-project/pull/190126
>From 6394b7ffcec9f30ec991dfbcd4b1a39a2cd25c5e Mon Sep 17 00:00:00 2001 From: Eduardo Santos <[email protected]> Date: Thu, 2 Apr 2026 04:32:17 -0300 Subject: [PATCH] [safestack] Support statically linked executables cat >a.c <<EOF && clang -fsanitize=safe-stack -static a.c && ./a.out void *f(void *p) { return NULL; } int main(void) { pthread_t t; (void)pthread_create(&t, NULL, f, NULL); return 0; } EOF causes an invalid memory reference at 0x0 that generates SIGSEGV, as lldb -b -o r -Q ./a.out will attest. On the first call to pthread_create, the SafeStack interceptor calls dlsym(RTLD_NEXT) to obtain the address of pthread_create. In a statically linked executable file, there is no next executable object file to search in. A null pointer is dereferenced when the interceptor attempts to call REAL(pthread_create). Declare a weak symbol for the implementation of pthread_create and fall back to it if dlsym returns a null pointer. The Clang driver instructs the link editor to extract the defining archive member if linking statically. illumos and Solaris do not provide libc.a as [1] and [2] document. The SafeStack runtime obtains the addresses of libc symbols for NetBSD almost exclusively via dlsym [3]. Even if refactored to avoid dlsym, [4] itself discourages static linking. [1] https://www.illumos.org/issues/5247#note-7 [2] http://www.linker-aliens.org/blogs/rie/entry/static_linking_where_did_it/ [3] https://github.com/llvm/llvm-project/blob/e3cbd9984a78422c3799629eb6b5f7f7818c1a11/compiler-rt/lib/safestack/safestack_platform.h#L66-L86 [4] https://man.netbsd.org/pthread.3#DESCRIPTION --- clang/lib/Driver/ToolChains/CommonArgs.cpp | 9 +++++ compiler-rt/lib/safestack/safestack.cpp | 45 +++++++++++++++++++++ compiler-rt/test/safestack/pthread-static.c | 20 +++++++++ 3 files changed, 74 insertions(+) create mode 100644 compiler-rt/test/safestack/pthread-static.c diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index c04e2c2b5b8c5..094675fee1632 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1707,6 +1707,15 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args, if (SanArgs.needsSafeStackRt()) { NonWholeStaticRuntimes.push_back("safestack"); RequiredSymbols.push_back("__safestack_init"); + if (Args.hasArg(options::OPT_static) || + Args.hasArg(options::OPT_static_pie)) { + if (TC.getTriple().isGNUEnvironment()) + RequiredSymbols.push_back("__pthread_create_2_1"); + else if (TC.getTriple().isOSLinux()) + RequiredSymbols.push_back("__pthread_create"); + else if (TC.getTriple().isOSFreeBSD()) + RequiredSymbols.push_back("_pthread_create"); + } } if (!(SanArgs.needsSharedRt() && SanArgs.needsUbsanRt())) { if (SanArgs.needsCfiCrossDsoRt()) diff --git a/compiler-rt/lib/safestack/safestack.cpp b/compiler-rt/lib/safestack/safestack.cpp index f01a642987646..05cee3289bff1 100644 --- a/compiler-rt/lib/safestack/safestack.cpp +++ b/compiler-rt/lib/safestack/safestack.cpp @@ -62,6 +62,26 @@ __attribute__((visibility( "default"))) __thread void *__safestack_unsafe_stack_ptr = nullptr; } +// These symbols may have no definition within the component being linked, +// either because the archive library is not passed to the link editor or +// because the shared object does not include them in its dynamic linking +// symbol table. Give them an STB_WEAK binding so that, if left unresolved, +// they have a zero value. +#if SANITIZER_GLIBC +extern "C" + __attribute__((weak)) int __pthread_create_2_1(pthread_t*, + const pthread_attr_t*, + void* (*)(void*), void*); +#elif SANITIZER_LINUX +extern "C" __attribute__((weak)) int __pthread_create(pthread_t*, + const pthread_attr_t*, + void* (*)(void*), void*); +#elif SANITIZER_FREEBSD +extern "C" + __attribute__((weak)) int _pthread_create(pthread_t*, const pthread_attr_t*, + void* (*)(void*), void*); +#endif + namespace { // TODO: The runtime library does not currently protect the safe stack beyond @@ -288,6 +308,24 @@ void EnsureInterceptorsInitialized() { // Initialize pthread interceptors for thread allocation INTERCEPT_FUNCTION(pthread_create); +#if SANITIZER_GLIBC + // REAL(pthread_create) is null after INTERCEPT_FUNCTION(pthread_create) in + // statically linked executable files. Fall back to the weak symbol we + // declare. + // + // diet libc and uClibc-ng are not supported because interception requires a + // distinct symbol to call after replacing pthread_create; neither provides + // such a symbol. + if (!REAL(pthread_create)) + REAL(pthread_create) = __pthread_create_2_1; +#elif SANITIZER_LINUX + if (!REAL(pthread_create)) + REAL(pthread_create) = __pthread_create; +#elif SANITIZER_FREEBSD + if (!REAL(pthread_create)) + REAL(pthread_create) = _pthread_create; +#endif + interceptors_inited = true; } @@ -320,8 +358,15 @@ void __safestack_init() { // On other platforms we use the constructor attribute to arrange to run our // initialization early. extern "C" { +# if !SANITIZER_MUSL __attribute__((section(".preinit_array"), used)) void (*__safestack_preinit)(void) = __safestack_init; +# else +// musl does not execute .preinit_array functions. +__attribute__((constructor(0))) void __safestack_init_fallback() { + __safestack_init(); +} +# endif } #endif diff --git a/compiler-rt/test/safestack/pthread-static.c b/compiler-rt/test/safestack/pthread-static.c new file mode 100644 index 0000000000000..fde4b174aaff2 --- /dev/null +++ b/compiler-rt/test/safestack/pthread-static.c @@ -0,0 +1,20 @@ +// RUN: %clang_safestack %s -pthread -static -o %t +// RUN: %run %t + +// REQUIRES: linux || freebsd + +// Smoke test for pthread_create in a statically linked executable. + +#include <pthread.h> + +void *f(void *p) +{ + return NULL; +} + +int main(void) +{ + pthread_t t; + (void)pthread_create(&t, NULL, f, NULL); + return 0; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
