[clang] [compiler-rt] [safestack] Support statically linked executables (PR #190126)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 2 00:52:55 PDT 2026
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 <33993373+edcsnt at users.noreply.github.com>
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;
+}
More information about the llvm-commits
mailing list