[clang] [compiler-rt] Add SignalSanitizer (PR #172086)
Ben Kallus via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 12 13:35:40 PST 2025
https://github.com/kenballus updated https://github.com/llvm/llvm-project/pull/172086
>From 92afc2af6c11b89425c038a2e722cbd496fb80d6 Mon Sep 17 00:00:00 2001
From: Ben Kallus <benjamin.p.kallus.gr at dartmouth.edu>
Date: Thu, 11 Dec 2025 23:17:53 -0500
Subject: [PATCH 1/5] Add SigSan
---
clang/include/clang/Basic/Sanitizers.def | 2 +
clang/include/clang/Driver/SanitizerArgs.h | 1 +
clang/lib/Driver/ToolChains/CommonArgs.cpp | 7 +
clang/lib/Driver/ToolChains/Linux.cpp | 1 +
compiler-rt/lib/CMakeLists.txt | 1 +
compiler-rt/lib/sigsan/CMakeLists.txt | 18 ++
compiler-rt/lib/sigsan/sigsan.cpp | 200 +++++++++++++++++++++
7 files changed, 230 insertions(+)
create mode 100644 compiler-rt/lib/sigsan/CMakeLists.txt
create mode 100644 compiler-rt/lib/sigsan/sigsan.cpp
diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def
index da85431625026..9594e0166c780 100644
--- a/clang/include/clang/Basic/Sanitizers.def
+++ b/clang/include/clang/Basic/Sanitizers.def
@@ -88,6 +88,8 @@ SANITIZER("realtime", Realtime)
// LeakSanitizer
SANITIZER("leak", Leak)
+SANITIZER("signal", Signal)
+
// UndefinedBehaviorSanitizer
SANITIZER("alignment", Alignment)
SANITIZER("array-bounds", ArrayBounds)
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index eea7897e96afd..7ae1f202eb41d 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -83,6 +83,7 @@ class SanitizerArgs {
SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
bool DiagnoseErrors = true);
+ bool needsSigsanRt() const { return Sanitizers.has(SanitizerKind::Signal); }
bool needsSharedRt() const { return SharedRuntime; }
bool needsStableAbi() const { return StableABI; }
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index ec8dcdc81db56..605a4a32aa276 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1574,6 +1574,9 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
const SanitizerArgs &SanArgs = TC.getSanitizerArgs(Args);
// Collect shared runtimes.
if (SanArgs.needsSharedRt()) {
+ if (SanArgs.needsSigsanRt()) {
+ SharedRuntimes.push_back("sigsan");
+ }
if (SanArgs.needsAsanRt()) {
SharedRuntimes.push_back("asan");
if (!Args.hasArg(options::OPT_shared) && !TC.getTriple().isAndroid())
@@ -1628,6 +1631,10 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
// Each static runtime that has a DSO counterpart above is excluded below,
// but runtimes that exist only as static are not affected by needsSharedRt.
+ if (!SanArgs.needsSharedRt() && SanArgs.needsSigsanRt()) {
+ StaticRuntimes.push_back("sigsan");
+ }
+
if (!SanArgs.needsSharedRt() && SanArgs.needsAsanRt()) {
StaticRuntimes.push_back("asan");
if (SanArgs.linkCXXRuntimes())
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
index 94a9fe8b1a63f..745a005aed286 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -914,6 +914,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
Res |= SanitizerKind::KernelAddress;
Res |= SanitizerKind::Vptr;
Res |= SanitizerKind::SafeStack;
+ Res |= SanitizerKind::Signal;
if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64)
Res |= SanitizerKind::DataFlow;
if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64 ||
diff --git a/compiler-rt/lib/CMakeLists.txt b/compiler-rt/lib/CMakeLists.txt
index e6158ec408895..e9960bb237fe1 100644
--- a/compiler-rt/lib/CMakeLists.txt
+++ b/compiler-rt/lib/CMakeLists.txt
@@ -42,6 +42,7 @@ if(COMPILER_RT_BUILD_SANITIZERS)
add_subdirectory(lsan)
# Contains RTUbsan used even without COMPILER_RT_HAS_UBSAN.
add_subdirectory(ubsan)
+ add_subdirectory(sigsan)
endif()
foreach(sanitizer ${COMPILER_RT_SANITIZERS_TO_BUILD})
diff --git a/compiler-rt/lib/sigsan/CMakeLists.txt b/compiler-rt/lib/sigsan/CMakeLists.txt
new file mode 100644
index 0000000000000..1168e0fcc9973
--- /dev/null
+++ b/compiler-rt/lib/sigsan/CMakeLists.txt
@@ -0,0 +1,18 @@
+include_directories(..)
+
+# Runtime library sources and build flags.
+set(SIGSAN_SOURCES
+ sigsan.cpp
+)
+
+add_compiler_rt_component(sigsan)
+add_compiler_rt_runtime(clang_rt.sigsan
+ STATIC
+ ARCHS x86_64
+ SOURCES ${SIGSAN_SOURCES}
+ OBJECT_LIBS RTInterception
+ RTSanitizerCommon
+ RTSanitizerCommonLibc
+ RTSanitizerCommonSymbolizer
+ PARENT_TARGET sigsan
+)
diff --git a/compiler-rt/lib/sigsan/sigsan.cpp b/compiler-rt/lib/sigsan/sigsan.cpp
new file mode 100644
index 0000000000000..34099dcdbd405
--- /dev/null
+++ b/compiler-rt/lib/sigsan/sigsan.cpp
@@ -0,0 +1,200 @@
+#include "interception/interception.h" // for INTERCEPTOR, INTERCEPT_FUNCTION, REAL
+#include "sanitizer_common/sanitizer_stacktrace.h" // for GET_CURRENT_PC_BP_SP, BufferedStackTrace
+#include "sanitizer_common/sanitizer_report_decorator.h" // for SanitizerCommonDecorator
+#include "sanitizer_common/sanitizer_common.h" // for Printf, Die
+// TODO: figure out the include for Report
+
+#include <signal.h> // for siginfo_t, NSIG, struct sigaction, SIG_IGN, SIG_DFL
+#include <cstdint> // for uintptr_t
+#include <cstdarg> // for va_list, va_start, va_end
+#include <stdio.h> // for FILE
+
+using namespace __sanitizer;
+
+using signal_handler = void (*)(int);
+using extended_signal_handler = void (*)(int, siginfo_t *, void *);
+
+thread_local unsigned int __sigsan_signal_depth = 0;
+
+// TODO: handle sigvec and the other deprecated sighandler installation functions
+
+// TODO: Make these atomic so concurrent calls to signal for the same signum don't cause problems
+static uintptr_t __sigsan_handlers[NSIG];
+
+void __sigsan_handler(int signum) {
+ __sigsan_signal_depth++;
+ ((signal_handler)(__sigsan_handlers[signum]))(signum);
+ __sigsan_signal_depth--;
+}
+
+void __sigsan_extended_handler(int signum, siginfo_t *si, void *arg) {
+ __sigsan_signal_depth++;
+ ((extended_signal_handler)(__sigsan_handlers[signum]))(signum, si, arg);
+ __sigsan_signal_depth--;
+}
+
+INTERCEPTOR(signal_handler, signal, int signum, signal_handler handler) {
+ // Adapted from llvm-project/libc/src/signal/linux/signal.cpp
+ struct sigaction action, old;
+ action.sa_handler = handler;
+ action.sa_flags = SA_RESTART;
+ return sigaction(signum, &action, &old) < 0 ? SIG_ERR : old.sa_handler;
+}
+
+INTERCEPTOR(int, sigaction, int sig, struct sigaction const *__restrict act, struct sigaction *oldact) {
+ auto old_handler = __sigsan_handlers[sig];
+
+ int result;
+ if (!act || act->sa_handler == SIG_IGN || act->sa_handler == SIG_DFL) {
+ result = REAL(sigaction)(sig, act, oldact);
+ } else {
+ // Pass in act, but replace the sa_handler with our middleman
+ struct sigaction act_copy = *act;
+ act_copy.sa_handler = act_copy.sa_flags & SA_SIGINFO ? (signal_handler)(uintptr_t)__sigsan_extended_handler : __sigsan_handler;
+ result = REAL(sigaction)(sig, &act_copy, oldact);
+ }
+
+ if (result == 0) {
+ if (act) {
+ // TODO: Fix race condition.
+ // (sig gets delievered right here, causing old sighandler to be called)
+ __sigsan_handlers[sig] = (uintptr_t)act->sa_handler;
+ }
+
+ if (oldact) {
+ // TODO: figure out if oldact gets written to even when result != 0
+
+ // Stick in the handler from __sigsan_handlers, so the caller isn't aware of our trickery :)
+ oldact->sa_handler = (signal_handler)old_handler;
+ }
+ }
+
+ return result;
+}
+
+void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
+ uptr top = 0;
+ uptr bottom = 0;
+ GetThreadStackTopAndBottom(false, &top, &bottom);
+ Unwind(max_depth, pc, bp, context, top, bottom, request_fast);
+}
+
+#define SIGSAN_INTERCEPTOR(ret_type, func, args, ...) \
+ INTERCEPTOR(ret_type, func, ##__VA_ARGS__) { \
+ if (__sigsan_signal_depth > 0) { \
+ __sigsan_signal_depth = 0; /* To avoid having to make this function async-signal-safe :) */ \
+ SanitizerCommonDecorator d; \
+ Printf("%s", d.Warning()); \
+ Report("ERROR: SignalSanitizer: Async-signal unsafe function " #func " called from a signal handler.\n"); \
+ Printf("%s", d.Default()); \
+ BufferedStackTrace stack; \
+ GET_CURRENT_PC_BP_SP; \
+ (void)sp; \
+ stack.Unwind(pc, bp, nullptr, false); \
+ stack.Print(); \
+ Die(); \
+ } \
+ return REAL(func)args; \
+ }
+
+SIGSAN_INTERCEPTOR(void *, malloc, (size), size_t size)
+SIGSAN_INTERCEPTOR(void *, calloc, (n, size), size_t n, size_t size)
+SIGSAN_INTERCEPTOR(void *, realloc, (p, size), void *p, size_t size)
+SIGSAN_INTERCEPTOR(void *, reallocarray, (p, n, size), void *p, size_t n, size_t size)
+SIGSAN_INTERCEPTOR(void, free, (p), void *p)
+
+SIGSAN_INTERCEPTOR(int, fputc, (c, stream), int c, FILE *stream)
+SIGSAN_INTERCEPTOR(int, putc, (c, stream), int c, FILE *stream)
+SIGSAN_INTERCEPTOR(int, putchar, (c), int c);
+SIGSAN_INTERCEPTOR(int, fputs, (s, stream), const char *s, FILE *stream)
+SIGSAN_INTERCEPTOR(int, puts, (s), const char *s)
+
+SIGSAN_INTERCEPTOR(int, vprintf, (format, ap), const char *format, va_list ap)
+SIGSAN_INTERCEPTOR(int, vfprintf, (stream, format, ap), FILE *stream, const char *format, va_list ap)
+SIGSAN_INTERCEPTOR(int, vdprintf, (fd, format, ap), int fd, const char *format, va_list ap)
+SIGSAN_INTERCEPTOR(int, vsprintf, (str, format, ap), char *str, const char *format, va_list ap)
+SIGSAN_INTERCEPTOR(int, vsnprintf, (str, size, format, ap), char *str, size_t size, const char *format, va_list ap)
+
+INTERCEPTOR(int, printf, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ auto result = REAL(vprintf)(format, ap);
+ va_end(ap);
+ return result;
+}
+INTERCEPTOR(int, fprintf, FILE *stream, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ auto result = REAL(vfprintf)(stream, format, ap);
+ va_end(ap);
+ return result;
+}
+INTERCEPTOR(int, dprintf, int fd, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ auto result = REAL(vdprintf)(fd, format, ap);
+ va_end(ap);
+ return result;
+}
+INTERCEPTOR(int, sprintf, char *str, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ auto result = REAL(vsprintf)(str, format, ap);
+ va_end(ap);
+ return result;
+}
+INTERCEPTOR(int, snprintf, char *str, size_t size, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ auto result = REAL(vsnprintf)(str, size, format, ap);
+ va_end(ap);
+ return result;
+}
+
+__attribute__((constructor)) void __sigsan_init() {
+ SetCommonFlagsDefaults();
+ InitializeCommonFlags();
+
+ INTERCEPT_FUNCTION(signal);
+ INTERCEPT_FUNCTION(sigaction);
+
+ INTERCEPT_FUNCTION(malloc);
+ INTERCEPT_FUNCTION(calloc);
+ INTERCEPT_FUNCTION(realloc);
+ INTERCEPT_FUNCTION(reallocarray);
+ INTERCEPT_FUNCTION(free);
+ INTERCEPT_FUNCTION(fputc);
+ INTERCEPT_FUNCTION(putc);
+ INTERCEPT_FUNCTION(putchar);
+ INTERCEPT_FUNCTION(fputs);
+ INTERCEPT_FUNCTION(puts);
+
+ INTERCEPT_FUNCTION(vprintf);
+ INTERCEPT_FUNCTION(vfprintf);
+ INTERCEPT_FUNCTION(vdprintf);
+ INTERCEPT_FUNCTION(vsprintf);
+ INTERCEPT_FUNCTION(vsnprintf);
+
+ INTERCEPT_FUNCTION(printf);
+ INTERCEPT_FUNCTION(fprintf);
+ INTERCEPT_FUNCTION(dprintf);
+ INTERCEPT_FUNCTION(sprintf);
+ INTERCEPT_FUNCTION(snprintf);
+
+ // TODO: Fix race conditions.
+ // (signal/sigaction called while this loop is running)
+ for (int i = 0; i < NSIG; i++) {
+ struct sigaction existing_sa;
+ if (REAL(sigaction)(i, NULL, &existing_sa) == 0) {
+ auto const existing_handler = existing_sa.sa_handler;
+ if (existing_handler != SIG_IGN && existing_handler != SIG_DFL) {
+ __sigsan_handlers[i] = (uintptr_t)existing_handler;
+ if (existing_sa.sa_flags & SA_SIGINFO) {
+ existing_sa.sa_handler = __sigsan_handler;
+ } else {
+ existing_sa.sa_handler = (signal_handler)(uintptr_t)__sigsan_extended_handler;
+ }
+ }
+ }
+ }
+}
>From 8d4d9f9bd7cebb3e24da28b5aed52a4f8e3bf6c8 Mon Sep 17 00:00:00 2001
From: Ben Kallus <benjamin.p.kallus.gr at dartmouth.edu>
Date: Fri, 12 Dec 2025 09:25:33 -0500
Subject: [PATCH 2/5] format and add a few more functions
---
compiler-rt/lib/sigsan/sigsan.cpp | 339 ++++++++++++++++++------------
1 file changed, 208 insertions(+), 131 deletions(-)
diff --git a/compiler-rt/lib/sigsan/sigsan.cpp b/compiler-rt/lib/sigsan/sigsan.cpp
index 34099dcdbd405..b48583740e2b2 100644
--- a/compiler-rt/lib/sigsan/sigsan.cpp
+++ b/compiler-rt/lib/sigsan/sigsan.cpp
@@ -1,13 +1,14 @@
#include "interception/interception.h" // for INTERCEPTOR, INTERCEPT_FUNCTION, REAL
-#include "sanitizer_common/sanitizer_stacktrace.h" // for GET_CURRENT_PC_BP_SP, BufferedStackTrace
+#include "sanitizer_common/sanitizer_common.h" // for Printf, Die
#include "sanitizer_common/sanitizer_report_decorator.h" // for SanitizerCommonDecorator
-#include "sanitizer_common/sanitizer_common.h" // for Printf, Die
+#include "sanitizer_common/sanitizer_stacktrace.h" // for GET_CURRENT_PC_BP_SP, BufferedStackTrace
// TODO: figure out the include for Report
+#include <cstdarg> // for va_list, va_start, va_end
+#include <cstdint> // for uintptr_t
+#include <errno.h> // for errno
#include <signal.h> // for siginfo_t, NSIG, struct sigaction, SIG_IGN, SIG_DFL
-#include <cstdint> // for uintptr_t
-#include <cstdarg> // for va_list, va_start, va_end
-#include <stdio.h> // for FILE
+#include <stdio.h> // for FILE
using namespace __sanitizer;
@@ -16,21 +17,63 @@ using extended_signal_handler = void (*)(int, siginfo_t *, void *);
thread_local unsigned int __sigsan_signal_depth = 0;
-// TODO: handle sigvec and the other deprecated sighandler installation functions
+// TODO: handle sigvec and the other deprecated sighandler installation
+// functions
-// TODO: Make these atomic so concurrent calls to signal for the same signum don't cause problems
+// TODO: Make these atomic so concurrent calls to signal for the same signum
+// don't cause problems
static uintptr_t __sigsan_handlers[NSIG];
+[[noreturn]] void __sigsan_print_backtrace_and_die() {
+ BufferedStackTrace stack;
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ stack.Unwind(pc, bp, nullptr, false);
+ stack.Print();
+ Die();
+}
+
+[[noreturn]] void
+__sigsan_die_from_unsafe_function_call(char const *func_name) {
+ __sigsan_signal_depth =
+ 0; /* To avoid having to make this function async-signal-safe :) */
+ SanitizerCommonDecorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: SignalSanitizer: async-signal-unsafe function %s called from "
+ "a signal handler.\n",
+ func_name);
+ Printf("%s", d.Default());
+ __sigsan_print_backtrace_and_die();
+}
+
+[[noreturn]] void __sigsan_die_from_modified_errno() {
+ __sigsan_signal_depth =
+ 0; /* To avoid having to make this function async-signal-safe :) */
+ SanitizerCommonDecorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: SignalSanitizer: errno modified from a signal handler.\n");
+ Printf("%s", d.Default());
+ __sigsan_print_backtrace_and_die();
+}
+
void __sigsan_handler(int signum) {
__sigsan_signal_depth++;
+ int saved_errno = errno;
((signal_handler)(__sigsan_handlers[signum]))(signum);
+ if (errno != saved_errno) {
+ __sigsan_die_from_modified_errno();
+ }
__sigsan_signal_depth--;
}
void __sigsan_extended_handler(int signum, siginfo_t *si, void *arg) {
- __sigsan_signal_depth++;
- ((extended_signal_handler)(__sigsan_handlers[signum]))(signum, si, arg);
- __sigsan_signal_depth--;
+ __sigsan_signal_depth++;
+ int saved_errno = errno;
+ ((extended_signal_handler)(__sigsan_handlers[signum]))(signum, si, arg);
+ if (errno != saved_errno) {
+ __sigsan_die_from_modified_errno();
+ }
+ __sigsan_signal_depth--;
}
INTERCEPTOR(signal_handler, signal, int signum, signal_handler handler) {
@@ -41,160 +84,194 @@ INTERCEPTOR(signal_handler, signal, int signum, signal_handler handler) {
return sigaction(signum, &action, &old) < 0 ? SIG_ERR : old.sa_handler;
}
-INTERCEPTOR(int, sigaction, int sig, struct sigaction const *__restrict act, struct sigaction *oldact) {
- auto old_handler = __sigsan_handlers[sig];
-
- int result;
- if (!act || act->sa_handler == SIG_IGN || act->sa_handler == SIG_DFL) {
- result = REAL(sigaction)(sig, act, oldact);
- } else {
- // Pass in act, but replace the sa_handler with our middleman
- struct sigaction act_copy = *act;
- act_copy.sa_handler = act_copy.sa_flags & SA_SIGINFO ? (signal_handler)(uintptr_t)__sigsan_extended_handler : __sigsan_handler;
- result = REAL(sigaction)(sig, &act_copy, oldact);
- }
+INTERCEPTOR(int, sigaction, int sig, struct sigaction const *__restrict act,
+ struct sigaction *oldact) {
+ auto old_handler = __sigsan_handlers[sig];
- if (result == 0) {
- if (act) {
- // TODO: Fix race condition.
- // (sig gets delievered right here, causing old sighandler to be called)
- __sigsan_handlers[sig] = (uintptr_t)act->sa_handler;
- }
+ int result;
+ if (!act || act->sa_handler == SIG_IGN || act->sa_handler == SIG_DFL) {
+ result = REAL(sigaction)(sig, act, oldact);
+ } else {
+ // Pass in act, but replace the sa_handler with our middleman
+ struct sigaction act_copy = *act;
+ act_copy.sa_handler =
+ act_copy.sa_flags & SA_SIGINFO
+ ? (signal_handler)(uintptr_t)__sigsan_extended_handler
+ : __sigsan_handler;
+ result = REAL(sigaction)(sig, &act_copy, oldact);
+ }
- if (oldact) {
- // TODO: figure out if oldact gets written to even when result != 0
+ if (result == 0) {
+ if (act) {
+ // TODO: Fix race condition.
+ // (sig gets delievered right here, causing old sighandler to be called)
+ __sigsan_handlers[sig] = (uintptr_t)act->sa_handler;
+ }
- // Stick in the handler from __sigsan_handlers, so the caller isn't aware of our trickery :)
- oldact->sa_handler = (signal_handler)old_handler;
- }
+ if (oldact) {
+ // TODO: figure out if oldact gets written to even when result != 0
+
+ // Stick in the handler from __sigsan_handlers, so the caller isn't aware
+ // of our trickery :)
+ oldact->sa_handler = (signal_handler)old_handler;
}
+ }
- return result;
+ return result;
}
-void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
- uptr top = 0;
- uptr bottom = 0;
- GetThreadStackTopAndBottom(false, &top, &bottom);
- Unwind(max_depth, pc, bp, context, top, bottom, request_fast);
+void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp,
+ void *context,
+ bool request_fast,
+ u32 max_depth) {
+ uptr top = 0;
+ uptr bottom = 0;
+ GetThreadStackTopAndBottom(false, &top, &bottom);
+ Unwind(max_depth, pc, bp, context, top, bottom, request_fast);
}
-#define SIGSAN_INTERCEPTOR(ret_type, func, args, ...) \
- INTERCEPTOR(ret_type, func, ##__VA_ARGS__) { \
- if (__sigsan_signal_depth > 0) { \
- __sigsan_signal_depth = 0; /* To avoid having to make this function async-signal-safe :) */ \
- SanitizerCommonDecorator d; \
- Printf("%s", d.Warning()); \
- Report("ERROR: SignalSanitizer: Async-signal unsafe function " #func " called from a signal handler.\n"); \
- Printf("%s", d.Default()); \
- BufferedStackTrace stack; \
- GET_CURRENT_PC_BP_SP; \
- (void)sp; \
- stack.Unwind(pc, bp, nullptr, false); \
- stack.Print(); \
- Die(); \
- } \
- return REAL(func)args; \
- }
+#define SIGSAN_INTERCEPTOR(ret_type, func, args, ...) \
+ INTERCEPTOR(ret_type, func, ##__VA_ARGS__) { \
+ if (__sigsan_signal_depth > 0) { \
+ __sigsan_die_from_unsafe_function_call(#func); \
+ } \
+ return REAL(func) args; \
+ }
+// malloc
SIGSAN_INTERCEPTOR(void *, malloc, (size), size_t size)
SIGSAN_INTERCEPTOR(void *, calloc, (n, size), size_t n, size_t size)
SIGSAN_INTERCEPTOR(void *, realloc, (p, size), void *p, size_t size)
-SIGSAN_INTERCEPTOR(void *, reallocarray, (p, n, size), void *p, size_t n, size_t size)
+SIGSAN_INTERCEPTOR(void *, reallocarray, (p, n, size), void *p, size_t n,
+ size_t size)
SIGSAN_INTERCEPTOR(void, free, (p), void *p)
+// stdio
SIGSAN_INTERCEPTOR(int, fputc, (c, stream), int c, FILE *stream)
SIGSAN_INTERCEPTOR(int, putc, (c, stream), int c, FILE *stream)
SIGSAN_INTERCEPTOR(int, putchar, (c), int c);
SIGSAN_INTERCEPTOR(int, fputs, (s, stream), const char *s, FILE *stream)
SIGSAN_INTERCEPTOR(int, puts, (s), const char *s)
-
+SIGSAN_INTERCEPTOR(int, fflush, (stream), FILE *stream)
SIGSAN_INTERCEPTOR(int, vprintf, (format, ap), const char *format, va_list ap)
-SIGSAN_INTERCEPTOR(int, vfprintf, (stream, format, ap), FILE *stream, const char *format, va_list ap)
-SIGSAN_INTERCEPTOR(int, vdprintf, (fd, format, ap), int fd, const char *format, va_list ap)
-SIGSAN_INTERCEPTOR(int, vsprintf, (str, format, ap), char *str, const char *format, va_list ap)
-SIGSAN_INTERCEPTOR(int, vsnprintf, (str, size, format, ap), char *str, size_t size, const char *format, va_list ap)
-
+SIGSAN_INTERCEPTOR(int, vfprintf, (stream, format, ap), FILE *stream,
+ const char *format, va_list ap)
+SIGSAN_INTERCEPTOR(int, vdprintf, (fd, format, ap), int fd, const char *format,
+ va_list ap)
+SIGSAN_INTERCEPTOR(int, vsprintf, (str, format, ap), char *str,
+ const char *format, va_list ap)
+SIGSAN_INTERCEPTOR(int, vsnprintf, (str, size, format, ap), char *str,
+ size_t size, const char *format, va_list ap)
INTERCEPTOR(int, printf, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- auto result = REAL(vprintf)(format, ap);
- va_end(ap);
- return result;
+ va_list ap;
+ va_start(ap, format);
+ auto const result = REAL(vprintf)(format, ap);
+ va_end(ap);
+ return result;
}
INTERCEPTOR(int, fprintf, FILE *stream, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- auto result = REAL(vfprintf)(stream, format, ap);
- va_end(ap);
- return result;
+ va_list ap;
+ va_start(ap, format);
+ auto const result = REAL(vfprintf)(stream, format, ap);
+ va_end(ap);
+ return result;
}
INTERCEPTOR(int, dprintf, int fd, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- auto result = REAL(vdprintf)(fd, format, ap);
- va_end(ap);
- return result;
+ va_list ap;
+ va_start(ap, format);
+ auto const result = REAL(vdprintf)(fd, format, ap);
+ va_end(ap);
+ return result;
}
INTERCEPTOR(int, sprintf, char *str, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- auto result = REAL(vsprintf)(str, format, ap);
- va_end(ap);
- return result;
+ va_list ap;
+ va_start(ap, format);
+ auto const result = REAL(vsprintf)(str, format, ap);
+ va_end(ap);
+ return result;
}
INTERCEPTOR(int, snprintf, char *str, size_t size, const char *format, ...) {
- va_list ap;
- va_start(ap, format);
- auto result = REAL(vsnprintf)(str, size, format, ap);
- va_end(ap);
- return result;
+ va_list ap;
+ va_start(ap, format);
+ auto const result = REAL(vsnprintf)(str, size, format, ap);
+ va_end(ap);
+ return result;
+}
+SIGSAN_INTERCEPTOR(FILE *, fopen, (path, mode), const char *path,
+ const char *mode)
+SIGSAN_INTERCEPTOR(FILE *, fdopen, (fd, mode), int fd, const char *mode)
+SIGSAN_INTERCEPTOR(FILE *, freopen, (path, mode, stream), const char *path,
+ const char *mode, FILE *stream)
+
+// syslog
+SIGSAN_INTERCEPTOR(void, openlog, (ident, option, facility), const char *ident,
+ int option, int facility)
+SIGSAN_INTERCEPTOR(void, closelog, ())
+SIGSAN_INTERCEPTOR(void, vsyslog, (priority, format, ap), int priority,
+ const char *format, va_list ap)
+INTERCEPTOR(void, syslog, int priority, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ REAL(vsyslog)(priority, format, ap);
+ va_end(ap);
}
__attribute__((constructor)) void __sigsan_init() {
- SetCommonFlagsDefaults();
- InitializeCommonFlags();
-
- INTERCEPT_FUNCTION(signal);
- INTERCEPT_FUNCTION(sigaction);
-
- INTERCEPT_FUNCTION(malloc);
- INTERCEPT_FUNCTION(calloc);
- INTERCEPT_FUNCTION(realloc);
- INTERCEPT_FUNCTION(reallocarray);
- INTERCEPT_FUNCTION(free);
- INTERCEPT_FUNCTION(fputc);
- INTERCEPT_FUNCTION(putc);
- INTERCEPT_FUNCTION(putchar);
- INTERCEPT_FUNCTION(fputs);
- INTERCEPT_FUNCTION(puts);
-
- INTERCEPT_FUNCTION(vprintf);
- INTERCEPT_FUNCTION(vfprintf);
- INTERCEPT_FUNCTION(vdprintf);
- INTERCEPT_FUNCTION(vsprintf);
- INTERCEPT_FUNCTION(vsnprintf);
-
- INTERCEPT_FUNCTION(printf);
- INTERCEPT_FUNCTION(fprintf);
- INTERCEPT_FUNCTION(dprintf);
- INTERCEPT_FUNCTION(sprintf);
- INTERCEPT_FUNCTION(snprintf);
-
- // TODO: Fix race conditions.
- // (signal/sigaction called while this loop is running)
- for (int i = 0; i < NSIG; i++) {
- struct sigaction existing_sa;
- if (REAL(sigaction)(i, NULL, &existing_sa) == 0) {
- auto const existing_handler = existing_sa.sa_handler;
- if (existing_handler != SIG_IGN && existing_handler != SIG_DFL) {
- __sigsan_handlers[i] = (uintptr_t)existing_handler;
- if (existing_sa.sa_flags & SA_SIGINFO) {
- existing_sa.sa_handler = __sigsan_handler;
- } else {
- existing_sa.sa_handler = (signal_handler)(uintptr_t)__sigsan_extended_handler;
- }
- }
+ SetCommonFlagsDefaults();
+ InitializeCommonFlags();
+
+ INTERCEPT_FUNCTION(signal);
+ INTERCEPT_FUNCTION(sigaction);
+
+ // malloc
+ INTERCEPT_FUNCTION(malloc);
+ INTERCEPT_FUNCTION(calloc);
+ INTERCEPT_FUNCTION(realloc);
+ INTERCEPT_FUNCTION(reallocarray);
+ INTERCEPT_FUNCTION(free);
+
+ // stdio
+ INTERCEPT_FUNCTION(fputc);
+ INTERCEPT_FUNCTION(putc);
+ INTERCEPT_FUNCTION(putchar);
+ INTERCEPT_FUNCTION(fputs);
+ INTERCEPT_FUNCTION(puts);
+ INTERCEPT_FUNCTION(fflush);
+ INTERCEPT_FUNCTION(vprintf);
+ INTERCEPT_FUNCTION(vfprintf);
+ INTERCEPT_FUNCTION(vdprintf);
+ INTERCEPT_FUNCTION(vsprintf);
+ INTERCEPT_FUNCTION(vsnprintf);
+ INTERCEPT_FUNCTION(printf);
+ INTERCEPT_FUNCTION(fprintf);
+ INTERCEPT_FUNCTION(dprintf);
+ INTERCEPT_FUNCTION(sprintf);
+ INTERCEPT_FUNCTION(snprintf);
+ INTERCEPT_FUNCTION(fopen);
+ INTERCEPT_FUNCTION(fdopen);
+ INTERCEPT_FUNCTION(freopen);
+
+ // syslog
+ INTERCEPT_FUNCTION(openlog);
+ INTERCEPT_FUNCTION(closelog);
+ INTERCEPT_FUNCTION(vsyslog);
+ INTERCEPT_FUNCTION(syslog);
+
+ // TODO: Fix race conditions.
+ // (signal/sigaction called while this loop is running)
+ for (int i = 0; i < NSIG; i++) {
+ struct sigaction existing_sa;
+ if (REAL(sigaction)(i, NULL, &existing_sa) == 0) {
+ auto const existing_handler = existing_sa.sa_handler;
+ if (existing_handler != SIG_IGN && existing_handler != SIG_DFL) {
+ __sigsan_handlers[i] = (uintptr_t)existing_handler;
+ if (existing_sa.sa_flags & SA_SIGINFO) {
+ existing_sa.sa_handler = __sigsan_handler;
+ } else {
+ existing_sa.sa_handler =
+ (signal_handler)(uintptr_t)__sigsan_extended_handler;
}
+ }
}
+ }
}
>From c5db568b858ea325042238942a322c7e7b7c4a03 Mon Sep 17 00:00:00 2001
From: Ben Kallus <benjamin.p.kallus.gr at dartmouth.edu>
Date: Fri, 12 Dec 2025 09:32:35 -0500
Subject: [PATCH 3/5] add pthread_mutex_whatever
---
compiler-rt/lib/sigsan/sigsan.cpp | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/compiler-rt/lib/sigsan/sigsan.cpp b/compiler-rt/lib/sigsan/sigsan.cpp
index b48583740e2b2..39d4bd44e8ea7 100644
--- a/compiler-rt/lib/sigsan/sigsan.cpp
+++ b/compiler-rt/lib/sigsan/sigsan.cpp
@@ -216,6 +216,12 @@ INTERCEPTOR(void, syslog, int priority, const char *format, ...) {
va_end(ap);
}
+SIGSAN_INTERCEPTOR(int, pthread_mutex_init, (mutex, mutexattr), pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
+SIGSAN_INTERCEPTOR(int, pthread_mutex_lock, (mutex), pthread_mutex_t *mutex)
+SIGSAN_INTERCEPTOR(int, pthread_mutex_trylock, (mutex), pthread_mutex_t *mutex)
+SIGSAN_INTERCEPTOR(int, pthread_mutex_unlock, (mutex), pthread_mutex_t *mutex)
+SIGSAN_INTERCEPTOR(int, pthread_mutex_destroy, (mutex), pthread_mutex_t *mutex)
+
__attribute__((constructor)) void __sigsan_init() {
SetCommonFlagsDefaults();
InitializeCommonFlags();
@@ -257,6 +263,13 @@ __attribute__((constructor)) void __sigsan_init() {
INTERCEPT_FUNCTION(vsyslog);
INTERCEPT_FUNCTION(syslog);
+ // pthreads
+ INTERCEPT_FUNCTION(pthread_mutex_init);
+ INTERCEPT_FUNCTION(pthread_mutex_lock);
+ INTERCEPT_FUNCTION(pthread_mutex_trylock);
+ INTERCEPT_FUNCTION(pthread_mutex_unlock);
+ INTERCEPT_FUNCTION(pthread_mutex_destroy);
+
// TODO: Fix race conditions.
// (signal/sigaction called while this loop is running)
for (int i = 0; i < NSIG; i++) {
>From 174017af9ab3ce563f402b71d244f6c09d30194a Mon Sep 17 00:00:00 2001
From: Ben Kallus <benjamin.p.kallus.gr at dartmouth.edu>
Date: Fri, 12 Dec 2025 16:29:00 -0500
Subject: [PATCH 4/5] Add vasprintf and saprintf checks
---
compiler-rt/lib/sigsan/CMakeLists.txt | 2 +-
compiler-rt/lib/sigsan/sigsan.cpp | 14 ++++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/compiler-rt/lib/sigsan/CMakeLists.txt b/compiler-rt/lib/sigsan/CMakeLists.txt
index 1168e0fcc9973..9c446ae5227db 100644
--- a/compiler-rt/lib/sigsan/CMakeLists.txt
+++ b/compiler-rt/lib/sigsan/CMakeLists.txt
@@ -1,10 +1,10 @@
include_directories(..)
-# Runtime library sources and build flags.
set(SIGSAN_SOURCES
sigsan.cpp
)
+# TODO: support other architectures
add_compiler_rt_component(sigsan)
add_compiler_rt_runtime(clang_rt.sigsan
STATIC
diff --git a/compiler-rt/lib/sigsan/sigsan.cpp b/compiler-rt/lib/sigsan/sigsan.cpp
index 39d4bd44e8ea7..f14ed9357636c 100644
--- a/compiler-rt/lib/sigsan/sigsan.cpp
+++ b/compiler-rt/lib/sigsan/sigsan.cpp
@@ -203,6 +203,16 @@ SIGSAN_INTERCEPTOR(FILE *, fdopen, (fd, mode), int fd, const char *mode)
SIGSAN_INTERCEPTOR(FILE *, freopen, (path, mode, stream), const char *path,
const char *mode, FILE *stream)
+// GNU stdio
+SIGSAN_INTERCEPTOR(int, vasprintf, (strp, fmt, ap), char **strp, const char *fmt, va_list ap)
+INTERCEPTOR(int, asprintf, char **strp, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ auto const result = REAL(vasprintf)(strp, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
// syslog
SIGSAN_INTERCEPTOR(void, openlog, (ident, option, facility), const char *ident,
int option, int facility)
@@ -257,6 +267,10 @@ __attribute__((constructor)) void __sigsan_init() {
INTERCEPT_FUNCTION(fdopen);
INTERCEPT_FUNCTION(freopen);
+ // GNU stdio
+ INTERCEPT_FUNCTION(vasprintf);
+ INTERCEPT_FUNCTION(asprintf);
+
// syslog
INTERCEPT_FUNCTION(openlog);
INTERCEPT_FUNCTION(closelog);
>From 062745b7a861ef7e2dd8509733ef4b8b0e24bf76 Mon Sep 17 00:00:00 2001
From: Ben Kallus <benjamin.p.kallus.gr at dartmouth.edu>
Date: Fri, 12 Dec 2025 16:35:26 -0500
Subject: [PATCH 5/5] format
---
compiler-rt/lib/sigsan/sigsan.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/sigsan/sigsan.cpp b/compiler-rt/lib/sigsan/sigsan.cpp
index f14ed9357636c..939562f52faaa 100644
--- a/compiler-rt/lib/sigsan/sigsan.cpp
+++ b/compiler-rt/lib/sigsan/sigsan.cpp
@@ -204,7 +204,8 @@ SIGSAN_INTERCEPTOR(FILE *, freopen, (path, mode, stream), const char *path,
const char *mode, FILE *stream)
// GNU stdio
-SIGSAN_INTERCEPTOR(int, vasprintf, (strp, fmt, ap), char **strp, const char *fmt, va_list ap)
+SIGSAN_INTERCEPTOR(int, vasprintf, (strp, fmt, ap), char **strp,
+ const char *fmt, va_list ap)
INTERCEPTOR(int, asprintf, char **strp, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
@@ -226,7 +227,8 @@ INTERCEPTOR(void, syslog, int priority, const char *format, ...) {
va_end(ap);
}
-SIGSAN_INTERCEPTOR(int, pthread_mutex_init, (mutex, mutexattr), pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
+SIGSAN_INTERCEPTOR(int, pthread_mutex_init, (mutex, mutexattr),
+ pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
SIGSAN_INTERCEPTOR(int, pthread_mutex_lock, (mutex), pthread_mutex_t *mutex)
SIGSAN_INTERCEPTOR(int, pthread_mutex_trylock, (mutex), pthread_mutex_t *mutex)
SIGSAN_INTERCEPTOR(int, pthread_mutex_unlock, (mutex), pthread_mutex_t *mutex)
More information about the llvm-commits
mailing list