[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