Skip to content

Commit eae30a2

Browse files
authored
[rtsan] Add syscall interceptor (#118250)
This is a complex one - `syscall` is used when people want to bypass libc and make the call directly However, this call: * Has a variable amount of arguments (up to 6, typically) * Has arguments that can be any type, (think of whatever arguments go in to the libc call, or see more details here https://syscalls.mebeim.net/?table=x86/64/x64/latest) I've tried to put in a couple tests to ensure we aren't mucking with the underlying functionality and they seem to be working.
1 parent 1288f6d commit eae30a2

File tree

3 files changed

+134
-0
lines changed

3 files changed

+134
-0
lines changed

compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp

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

818+
#if SANITIZER_APPLE
819+
#define INT_TYPE_SYSCALL int
820+
#else
821+
#define INT_TYPE_SYSCALL long
822+
#endif
823+
824+
#pragma clang diagnostic push
825+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
826+
INTERCEPTOR(INT_TYPE_SYSCALL, syscall, INT_TYPE_SYSCALL number, ...) {
827+
__rtsan_notify_intercepted_call("syscall");
828+
829+
va_list args;
830+
va_start(args, number);
831+
832+
// the goal is to pick something large enough to hold all syscall args
833+
// see fcntl for more discussion and why we always pull all 6 args
834+
using arg_type = unsigned long;
835+
arg_type arg1 = va_arg(args, arg_type);
836+
arg_type arg2 = va_arg(args, arg_type);
837+
arg_type arg3 = va_arg(args, arg_type);
838+
arg_type arg4 = va_arg(args, arg_type);
839+
arg_type arg5 = va_arg(args, arg_type);
840+
arg_type arg6 = va_arg(args, arg_type);
841+
842+
// these are various examples of things that COULD be passed
843+
static_assert(sizeof(arg_type) >= sizeof(off_t));
844+
static_assert(sizeof(arg_type) >= sizeof(struct flock *));
845+
static_assert(sizeof(arg_type) >= sizeof(const char *));
846+
static_assert(sizeof(arg_type) >= sizeof(int));
847+
static_assert(sizeof(arg_type) >= sizeof(unsigned long));
848+
849+
va_end(args);
850+
851+
return REAL(syscall)(number, arg1, arg2, arg3, arg4, arg5, arg6);
852+
}
853+
#pragma clang diagnostic pop
854+
818855
// Preinit
819856
void __rtsan::InitializeInterceptors() {
820857
INTERCEPT_FUNCTION(calloc);
@@ -918,6 +955,8 @@ void __rtsan::InitializeInterceptors() {
918955

919956
INTERCEPT_FUNCTION(pipe);
920957
INTERCEPT_FUNCTION(mkfifo);
958+
959+
INTERCEPT_FUNCTION(syscall);
921960
}
922961

923962
#endif // SANITIZER_POSIX

compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <sys/mman.h>
4848
#include <sys/socket.h>
4949
#include <sys/stat.h>
50+
#include <sys/syscall.h>
5051
#include <sys/types.h>
5152
#include <sys/uio.h>
5253

@@ -1090,4 +1091,20 @@ TEST(TestRtsanInterceptors, PipeDiesWhenRealtime) {
10901091
ExpectNonRealtimeSurvival(Func);
10911092
}
10921093

1094+
#pragma clang diagnostic push
1095+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1096+
TEST(TestRtsanInterceptors, SyscallDiesWhenRealtime) {
1097+
auto Func = []() { syscall(SYS_getpid); };
1098+
ExpectRealtimeDeath(Func, "syscall");
1099+
ExpectNonRealtimeSurvival(Func);
1100+
}
1101+
1102+
TEST(TestRtsanInterceptors, GetPidReturnsSame) {
1103+
int pid = syscall(SYS_getpid);
1104+
EXPECT_THAT(pid, Ne(-1));
1105+
1106+
EXPECT_THAT(getpid(), Eq(pid));
1107+
}
1108+
#pragma clang diagnostic pop
1109+
10931110
#endif // SANITIZER_POSIX

compiler-rt/test/rtsan/syscall.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// RUN: %clangxx -fsanitize=realtime %s -o %t
2+
// RUN: %env_rtsan_opts="halt_on_error=false" %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK-RTSAN,CHECK
3+
4+
// RUN: %clangxx %s -o %t
5+
// RUN: %run %t 2>&1 | FileCheck %s
6+
7+
// UNSUPPORTED: ios
8+
9+
// Intent: Ensure the `syscall` call behaves in the same way with/without the
10+
// sanitizer disabled
11+
12+
#include <fcntl.h>
13+
#include <stdio.h>
14+
#include <stdlib.h>
15+
#include <string.h>
16+
#include <sys/syscall.h>
17+
#include <sys/types.h>
18+
#include <unistd.h>
19+
20+
const char *GetTemporaryFilePath() { return "/tmp/rtsan_syscall_test.txt"; }
21+
22+
void custom_assert(bool condition, const char *message) {
23+
if (!condition) {
24+
fprintf(stderr, "ASSERTION FAILED: %s\n", message);
25+
exit(1);
26+
}
27+
}
28+
29+
class ScopedFileCleanup {
30+
public:
31+
[[nodiscard]] ScopedFileCleanup() = default;
32+
~ScopedFileCleanup() {
33+
if (access(GetTemporaryFilePath(), F_OK) != -1)
34+
unlink(GetTemporaryFilePath());
35+
}
36+
};
37+
38+
// Apple has deprecated `syscall`, ignore that error
39+
#pragma clang diagnostic push
40+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
41+
int main() [[clang::nonblocking]] {
42+
ScopedFileCleanup cleanup;
43+
44+
{
45+
int fd = syscall(SYS_openat, AT_FDCWD, GetTemporaryFilePath(),
46+
O_CREAT | O_WRONLY, 0644);
47+
48+
custom_assert(fd != -1, "Failed to open file - write");
49+
50+
int written = syscall(SYS_write, fd, "Hello, world!", 13);
51+
custom_assert(written == 13, "Failed to write to file");
52+
53+
custom_assert(syscall(SYS_close, fd) == 0, "Failed to close file - write");
54+
}
55+
56+
{
57+
int fd = syscall(SYS_openat, AT_FDCWD, GetTemporaryFilePath(), O_RDONLY);
58+
custom_assert(fd != -1, "Failed to open file - read");
59+
60+
char buffer[13];
61+
int read = syscall(SYS_read, fd, buffer, 13);
62+
custom_assert(read == 13, "Failed to read from file");
63+
64+
custom_assert(memcmp(buffer, "Hello, world!", 13) == 0,
65+
"Read data does not match written data");
66+
67+
custom_assert(syscall(SYS_close, fd) == 0, "Failed to close file - read");
68+
}
69+
70+
unlink(GetTemporaryFilePath());
71+
printf("DONE\n");
72+
}
73+
#pragma clang diagnostic pop
74+
75+
// CHECK-NOT: ASSERTION FAILED
76+
// CHECK-RTSAN-COUNT-6: Intercepted call to real-time unsafe function `syscall`
77+
78+
// CHECK: DONE

0 commit comments

Comments
 (0)