[compiler-rt] [rtsan] Add fork/execve interceptors (PR #117198)

Chris Apple via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 2 08:14:29 PST 2024


https://github.com/cjappl updated https://github.com/llvm/llvm-project/pull/117198

>From 475b0e2570cabab45d89013bdfaea99546089355 Mon Sep 17 00:00:00 2001
From: Chris Apple <cja-private at pm.me>
Date: Thu, 21 Nov 2024 09:46:23 -0800
Subject: [PATCH] [rtsan] Add fork/execve interceptors

---
 .../lib/rtsan/rtsan_interceptors_posix.cpp    | 19 ++++++
 compiler-rt/test/rtsan/fork_exec.cpp          | 59 +++++++++++++++++++
 2 files changed, 78 insertions(+)
 create mode 100644 compiler-rt/test/rtsan/fork_exec.cpp

diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
index 4262039e8e1fa6..4f7b534ee17a86 100644
--- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
@@ -815,6 +815,22 @@ INTERCEPTOR(int, mkfifo, const char *pathname, mode_t mode) {
   return REAL(mkfifo)(pathname, mode);
 }
 
+INTERCEPTOR(pid_t, fork, void) {
+  __rtsan_notify_intercepted_call("fork");
+  return REAL(fork)();
+}
+
+INTERCEPTOR(int, execve, const char *filename, char *const argv[],
+            char *const envp[]) {
+  __rtsan_notify_intercepted_call("execve");
+  return REAL(execve)(filename, argv, envp);
+}
+
+// TODO: the `wait` family of functions is an oddity. In testing, if you
+// intercept them, Darwin seemingly ignores them, and linux never returns from
+// the test. Revisit this in the future, but hopefully intercepting fork/exec is
+// enough to dissuade usage of wait by proxy.
+
 #if SANITIZER_APPLE
 #define INT_TYPE_SYSCALL int
 #else
@@ -956,6 +972,9 @@ void __rtsan::InitializeInterceptors() {
   INTERCEPT_FUNCTION(pipe);
   INTERCEPT_FUNCTION(mkfifo);
 
+  INTERCEPT_FUNCTION(fork);
+  INTERCEPT_FUNCTION(execve);
+
   INTERCEPT_FUNCTION(syscall);
 }
 
diff --git a/compiler-rt/test/rtsan/fork_exec.cpp b/compiler-rt/test/rtsan/fork_exec.cpp
new file mode 100644
index 00000000000000..3b2d2e5ca2f5d2
--- /dev/null
+++ b/compiler-rt/test/rtsan/fork_exec.cpp
@@ -0,0 +1,59 @@
+// RUN: %clangxx -fsanitize=realtime -DIS_NONBLOCKING=1 %s -o %t
+// RUN: %env_rtsan_opts="halt_on_error=true" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-HALT
+// RUN: %env_rtsan_opts="halt_on_error=false" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOHALT
+
+// RUN: %clangxx -fsanitize=realtime -DIS_NONBLOCKING=0 %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-OK
+// RUN: %env_rtsan_opts="halt_on_error=false" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-OK
+
+// UNSUPPORTED: ios
+
+// Intent: Ensure fork/exec dies when realtime and survives otherwise
+//         This behavior is difficult to test in a gtest, because the process is
+//         wiped away with exec.
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if IS_NONBLOCKING
+#  define MAYBE_NONBLOCKING [[clang::nonblocking]]
+#else
+#  define MAYBE_NONBLOCKING
+#endif
+
+int main() MAYBE_NONBLOCKING {
+  const pid_t pid = fork();
+
+  if (pid == 0) {
+    char *args[] = {"/bin/ls", nullptr};
+    execve(args[0], args, nullptr);
+    perror("execve failed");
+    return 1;
+  } else if (pid > 0) {
+    int status;
+    waitpid(pid, &status, 0);
+    usleep(1);
+  } else {
+    perror("fork failed");
+    return 1;
+  }
+
+  printf("fork/exec succeeded\n");
+  return 0;
+}
+
+// CHECK-NOHALT: Intercepted call to {{.*}} `fork` {{.*}}
+// CHECK-NOHALT: Intercepted call to {{.*}} `execve` {{.*}}
+
+// usleep checks that rtsan is still enabled in the parent process
+// See note in our interceptors file for why we don't look for `wait`
+// CHECK-NOHALT: Intercepted call to {{.*}} `usleep` {{.*}}
+
+// CHECK-NOHALT: fork/exec succeeded
+
+// CHECK-HALT: ==ERROR: RealtimeSanitizer: unsafe-library-call
+// CHECK-HALT-NEXT: Intercepted call to {{.*}} `fork` {{.*}}
+
+// CHECK-OK: fork/exec succeeded



More information about the llvm-commits mailing list