Skip to content

Commit d21b6a6

Browse files
committed
[rtsan] Add fork/execve interceptors
1 parent a12e79a commit d21b6a6

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ void OSSpinLockLock(volatile OSSpinLock *__lock);
4747
#include <stdarg.h>
4848
#include <stdio.h>
4949
#include <sys/socket.h>
50+
#include <sys/wait.h>
5051
#include <time.h>
5152
#include <unistd.h>
5253

@@ -751,6 +752,22 @@ INTERCEPTOR(int, mkfifo, const char *pathname, mode_t mode) {
751752
return REAL(mkfifo)(pathname, mode);
752753
}
753754

755+
INTERCEPTOR(pid_t, fork, void) {
756+
__rtsan_notify_intercepted_call("fork");
757+
return REAL(fork)();
758+
}
759+
760+
INTERCEPTOR(int, execve, const char *filename, char *const argv[],
761+
char *const envp[]) {
762+
__rtsan_notify_intercepted_call("execve");
763+
return REAL(execve)(filename, argv, envp);
764+
}
765+
766+
// TODO: the `wait` family of functions is an oddity. In testing, if you
767+
// intercept them, Darwin seemingly ignores them, and linux never returns from
768+
// the test. Revisit this in the future, but hopefully intercepting fork/exec is
769+
// enough to dissuade usage of wait by proxy.
770+
754771
// Preinit
755772
void __rtsan::InitializeInterceptors() {
756773
INTERCEPT_FUNCTION(calloc);
@@ -855,6 +872,9 @@ void __rtsan::InitializeInterceptors() {
855872

856873
INTERCEPT_FUNCTION(pipe);
857874
INTERCEPT_FUNCTION(mkfifo);
875+
876+
INTERCEPT_FUNCTION(fork);
877+
INTERCEPT_FUNCTION(execve);
858878
}
859879

860880
#endif // SANITIZER_POSIX
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %clangxx -fsanitize=realtime -DIS_NONBLOCKING=1 %s -o %t
2+
// RUN: %env_rtsan_opts="halt_on_error=true" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-HALT
3+
// RUN: %env_rtsan_opts="halt_on_error=false" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOHALT
4+
5+
// RUN: %clangxx -fsanitize=realtime -DIS_NONBLOCKING=0 %s -o %t
6+
// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-OK
7+
// RUN: %env_rtsan_opts="halt_on_error=false" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-OK
8+
9+
// UNSUPPORTED: ios
10+
11+
// Intent: Ensure fork/exec dies when realtime and survives otherwise
12+
// This behavior is difficult to test in a gtest, because the process is
13+
// wiped away with exec.
14+
15+
#include <stdio.h>
16+
#include <sys/types.h>
17+
#include <sys/wait.h>
18+
#include <unistd.h>
19+
20+
#if IS_NONBLOCKING
21+
# define MAYBE_NONBLOCKING [[clang::nonblocking]]
22+
#else
23+
# define MAYBE_NONBLOCKING
24+
#endif
25+
26+
int main() MAYBE_NONBLOCKING {
27+
const pid_t pid = fork();
28+
29+
if (pid == 0) {
30+
char *args[] = {"/bin/ls", nullptr};
31+
execve(args[0], args, nullptr);
32+
perror("execve failed");
33+
return 1;
34+
} else if (pid > 0) {
35+
int status;
36+
waitpid(pid, &status, 0);
37+
usleep(1);
38+
} else {
39+
perror("fork failed");
40+
return 1;
41+
}
42+
43+
printf("fork/exec succeeded\n");
44+
return 0;
45+
}
46+
47+
// CHECK-NOHALT: Intercepted call to {{.*}} `fork` {{.*}}
48+
// CHECK-NOHALT: Intercepted call to {{.*}} `execve` {{.*}}
49+
50+
// usleep checks that rtsan is still enabled in the parent process
51+
// See note in our interceptors file for why we don't look for `wait`
52+
// CHECK-NOHALT: Intercepted call to {{.*}} `usleep` {{.*}}
53+
54+
// CHECK-NOHALT: fork/exec succeeded
55+
56+
// CHECK-HALT: ==ERROR: RealtimeSanitizer: unsafe-library-call
57+
// CHECK-HALT-NEXT: Intercepted call to {{.*}} `fork` {{.*}}
58+
59+
// CHECK-OK: fork/exec succeeded

0 commit comments

Comments
 (0)