llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-compiler-rt-sanitizer Author: Ben Kallus (kenballus) <details> <summary>Changes</summary> # SignalSanitizer (`sigsan`) ## Summary This PR adds a new sanitizer to detect async-signal-unsafe function calls made from signal handlers, to catch bugs like [RegreSSHion](https://www.qualys.com/2024/07/01/cve-2024-6387/regresshion.txt). ## Background >From signal-safety(7): > An async-signal-safe function is one that can be safely called from within a signal handler. Many functions are not async-signal-safe. In particular, nonreentrant functions are generally unsafe to call from a signal handler. > ... > To avoid problems with unsafe functions, there are two possible choices: > (a) Ensure that (1) the signal handler calls only async-signal-safe functions, and (2) the signal handler itself is reentrant with respect to global variables in the main program. > (b) Block signal delivery in the main program when calling functions that are unsafe or operating on global data that is also accessed by the signal handler. > Generally, the second choice is difficult in programs of any complexity, so the first choice is taken. Consider the following C program: ```C #include <signal.h> // for signal, SIGINT #include <stdlib.h> // for malloc, free void handler(int) { free(malloc(128)); } #define ARRAY_LEN(a) (sizeof(a) / sizeof(*a)) int main(void) { signal(SIGINT, handler); while (1) { void *p[128]; for (size_t i = 0; i < ARRAY_LEN(p); i++) { p[i] = malloc(128); } for (size_t i = 0; i < ARRAY_LEN(p); i++) { free(p[i]); } } } ``` This program just `malloc`s and `free`s in a loop. When it receives `SIGINT`, it `malloc`s once and `free`s it immediately. Because `malloc` and `free` are not async-signal-safe, `handler` can corrupt the heap. For a demo, try building the above program at `-O0` and send `SIGINT` in a loop; the program should crash with errors like this: ``` Fatal glibc error: malloc.c:2610 (sysmalloc): assertion failed: (old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0) ``` or this: ``` malloc(): mismatching next->prev_size (unsorted) ``` or this: ``` double free or corruption (!prev) ``` or this: ``` free(): double free detected in tcache 2 ``` On my amd64 GNU/Linux system, it takes ~30 `SIGINT`s before one of them causes detectable heap corruption. ## Mechanism To catch these bugs at runtime, a sanitizer might employ the following high-level strategy: - Whenever someone tries to install a signal handler, instead install a wrapper that increments a thread-local counter, calls the provided handler, then decrements the counter. - For each async-signal-unsafe function that tends to cause problems, intercept it with a wrapper that fails with a backtrace if the counter is nonzero, and calls the function otherwise. This draft PR implements these ideas, and intercepts a few async-signal-unsafe libc functions as a PoC. It also ensures that signal handlers do not clobber `errno`, because doing so opens the door to `errno`-checking race conditions. ## Demo To see this catch a real bug (RegreSSHion, which enabled RCE in i386 OpenSSH), do the following (on a computer you don't really care about): ```bash # Download the OpenSSH 9.7p1 source code wget https://github.com/openssh/openssh-portable/archive/refs/tags/V_9_7_P1.tar.gz tar xf V_9_7_P1.tar.gz cd openssh-portable-V_9_7_P1 # Build it export CC=/the/path/to/this/build/of/clang CFLAGS='-O0 -g -fsanitize=signal' LDFLAGS='-fsanitize=signal' autoreconf -fi ./configure --without-openssl make -j$(nproc) # Install it sudo make install # Run it on port 9999 with a 1 second LoginGracePeriod sudo /usr/local/sbin/sshd -g 1 -eD -p 9999 # From another shell, talk to it nc localhost 9999 ``` A second after getting the OpenSSH banner, you should be kicked off and see a backtrace on the `sshd`'s `stderr`: ``` ==2338676==ERROR: SignalSanitizer: async-signal-unsafe function vsnprintf called from a signal handler. #<!-- -->0 0x5d96f00828d5 (/usr/local/sbin/sshd+0x3e8d5) #<!-- -->1 0x5d96f00829a5 (/usr/local/sbin/sshd+0x3e9a5) #<!-- -->2 0x5d96f0083238 (/usr/local/sbin/sshd+0x3f238) #<!-- -->3 0x5d96f00f5eef (/usr/local/sbin/sshd+0xb1eef) #<!-- -->4 0x5d96f00f5a33 (/usr/local/sbin/sshd+0xb1a33) #<!-- -->5 0x5d96f00f5c23 (/usr/local/sbin/sshd+0xb1c23) #<!-- -->6 0x5d96f00896b8 (/usr/local/sbin/sshd+0x456b8) #<!-- -->7 0x5d96f0082a5f (/usr/local/sbin/sshd+0x3ea5f) #<!-- -->8 0x7b8f8fc3e4cf (/usr/lib/libc.so.6+0x3e4cf) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d) #<!-- -->9 0x7b8f8fc9318d (/usr/lib/libc.so.6+0x9318d) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d) #<!-- -->10 0x7b8f8fc931b3 (/usr/lib/libc.so.6+0x931b3) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d) #<!-- -->11 0x7b8f8fd0da2d (/usr/lib/libc.so.6+0x10da2d) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d) #<!-- -->12 0x5d96f01070fc (/usr/local/sbin/sshd+0xc30fc) #<!-- -->13 0x5d96f0107272 (/usr/local/sbin/sshd+0xc3272) #<!-- -->14 0x5d96f01248dd (/usr/local/sbin/sshd+0xe08dd) #<!-- -->15 0x5d96f00871dc (/usr/local/sbin/sshd+0x431dc) #<!-- -->16 0x7b8f8fc27634 (/usr/lib/libc.so.6+0x27634) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d) #<!-- -->17 0x7b8f8fc276e8 (/usr/lib/libc.so.6+0x276e8) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d) #<!-- -->18 0x5d96f005d114 (/usr/local/sbin/sshd+0x19114) ``` ## Future Directions I'd be happy to make large changes to this PR to help it better fit the LLVM style and structure. This whole thing might belong in UBSan instead of being its own separate part of compiler-rt. It would be interesting to try to statically classify functions in IR as async-signal-safe or unsafe and insert the counter checks in a pass, but for now I went with a simple `INTERCEPTOR`-based scheme because it's easy and doesn't require recompiling the world :) --- Full diff: https://github.com/llvm/llvm-project/pull/172086.diff 7 Files Affected: - (modified) clang/include/clang/Basic/Sanitizers.def (+2) - (modified) clang/include/clang/Driver/SanitizerArgs.h (+1) - (modified) clang/lib/Driver/ToolChains/CommonArgs.cpp (+7) - (modified) clang/lib/Driver/ToolChains/Linux.cpp (+1) - (modified) compiler-rt/lib/CMakeLists.txt (+1) - (added) compiler-rt/lib/sigsan/CMakeLists.txt (+18) - (added) compiler-rt/lib/sigsan/sigsan.cpp (+306) ``````````diff 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 84fb66e16bee3..1b4c7de532504 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -85,6 +85,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 882283a99d4f1..bac8e49005fee 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1579,6 +1579,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()) @@ -1633,6 +1636,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 cdbf21fb90263..6cb63d0ba473e 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..9c446ae5227db --- /dev/null +++ b/compiler-rt/lib/sigsan/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories(..) + +set(SIGSAN_SOURCES + sigsan.cpp +) + +# TODO: support other architectures +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..bc111c1785770 --- /dev/null +++ b/compiler-rt/lib/sigsan/sigsan.cpp @@ -0,0 +1,306 @@ +#include "interception/interception.h" // for INTERCEPTOR, INTERCEPT_FUNCTION, REAL +#include "sanitizer_common/sanitizer_common.h" // for Printf, Die +#include "sanitizer_common/sanitizer_report_decorator.h" // for SanitizerCommonDecorator +#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 <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]; + +[[noreturn]] static 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]] static 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]] static 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(); +} + +static 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--; +} + +static void __sigsan_extended_handler(int signum, siginfo_t *si, void *arg) { + __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) { + // 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_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, 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) +INTERCEPTOR(int, printf, const char *format, ...) { + 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 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 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 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 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) + +// 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) +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); +} + +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) + +static __attribute__((constructor)) void __sigsan_init() { + 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); + + // GNU stdio + INTERCEPT_FUNCTION(vasprintf); + INTERCEPT_FUNCTION(asprintf); + + // syslog + INTERCEPT_FUNCTION(openlog); + INTERCEPT_FUNCTION(closelog); + 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++) { + 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; + } + } + } + } +} `````````` </details> https://github.com/llvm/llvm-project/pull/172086 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
