Skip to content

Commit af4ae12

Browse files
authored
[rtsan] Add fork/execve interceptors (#117198)
1 parent df43af4 commit af4ae12

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,22 @@ INTERCEPTOR(int, mkfifo, const char *pathname, mode_t mode) {
815815
return REAL(mkfifo)(pathname, mode);
816816
}
817817

818+
INTERCEPTOR(pid_t, fork, void) {
819+
__rtsan_notify_intercepted_call("fork");
820+
return REAL(fork)();
821+
}
822+
823+
INTERCEPTOR(int, execve, const char *filename, char *const argv[],
824+
char *const envp[]) {
825+
__rtsan_notify_intercepted_call("execve");
826+
return REAL(execve)(filename, argv, envp);
827+
}
828+
829+
// TODO: the `wait` family of functions is an oddity. In testing, if you
830+
// intercept them, Darwin seemingly ignores them, and linux never returns from
831+
// the test. Revisit this in the future, but hopefully intercepting fork/exec is
832+
// enough to dissuade usage of wait by proxy.
833+
818834
#if SANITIZER_APPLE
819835
#define INT_TYPE_SYSCALL int
820836
#else
@@ -956,6 +972,9 @@ void __rtsan::InitializeInterceptors() {
956972
INTERCEPT_FUNCTION(pipe);
957973
INTERCEPT_FUNCTION(mkfifo);
958974

975+
INTERCEPT_FUNCTION(fork);
976+
INTERCEPT_FUNCTION(execve);
977+
959978
INTERCEPT_FUNCTION(syscall);
960979
}
961980

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)