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):
&gt; 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.
&gt; ...
&gt; To avoid problems with unsafe functions, there are two possible choices:
&gt; (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.
&gt; (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.
&gt; Generally, the second choice is difficult in programs of any complexity, 
so the first choice is taken.

Consider the following C program:
```C
#include &lt;signal.h&gt; // for signal, SIGINT
#include &lt;stdlib.h&gt; // 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 &lt; ARRAY_LEN(p); i++) {
      p[i] = malloc(128);
    }
    for (size_t i = 0; i &lt; 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) &amp;&amp; old_size == 0) || ((unsigned long) (old_size) &gt;= 
MINSIZE &amp;&amp; prev_inuse (old_top) &amp;&amp; ((unsigned long) old_end 
&amp; (pagesize - 1)) == 0)
```
or this:
```
malloc(): mismatching next-&gt;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

Reply via email to