From bde1d2d0b370b323e62bec165d17e40a8108719c Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Mon, 25 Nov 2024 06:36:14 -0800 Subject: [PATCH] [rtsan] Add syscall interceptor --- .../lib/rtsan/rtsan_interceptors_posix.cpp | 39 ++++++++++ .../tests/rtsan_test_interceptors_posix.cpp | 17 ++++ compiler-rt/test/rtsan/syscall.cpp | 78 +++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 compiler-rt/test/rtsan/syscall.cpp diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp index 7dddaf553dad6..4262039e8e1fa 100644 --- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp +++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp @@ -815,6 +815,43 @@ INTERCEPTOR(int, mkfifo, const char *pathname, mode_t mode) { return REAL(mkfifo)(pathname, mode); } +#if SANITIZER_APPLE +#define INT_TYPE_SYSCALL int +#else +#define INT_TYPE_SYSCALL long +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +INTERCEPTOR(INT_TYPE_SYSCALL, syscall, INT_TYPE_SYSCALL number, ...) { + __rtsan_notify_intercepted_call("syscall"); + + va_list args; + va_start(args, number); + + // the goal is to pick something large enough to hold all syscall args + // see fcntl for more discussion and why we always pull all 6 args + using arg_type = unsigned long; + arg_type arg1 = va_arg(args, arg_type); + arg_type arg2 = va_arg(args, arg_type); + arg_type arg3 = va_arg(args, arg_type); + arg_type arg4 = va_arg(args, arg_type); + arg_type arg5 = va_arg(args, arg_type); + arg_type arg6 = va_arg(args, arg_type); + + // these are various examples of things that COULD be passed + static_assert(sizeof(arg_type) >= sizeof(off_t)); + static_assert(sizeof(arg_type) >= sizeof(struct flock *)); + static_assert(sizeof(arg_type) >= sizeof(const char *)); + static_assert(sizeof(arg_type) >= sizeof(int)); + static_assert(sizeof(arg_type) >= sizeof(unsigned long)); + + va_end(args); + + return REAL(syscall)(number, arg1, arg2, arg3, arg4, arg5, arg6); +} +#pragma clang diagnostic pop + // Preinit void __rtsan::InitializeInterceptors() { INTERCEPT_FUNCTION(calloc); @@ -918,6 +955,8 @@ void __rtsan::InitializeInterceptors() { INTERCEPT_FUNCTION(pipe); INTERCEPT_FUNCTION(mkfifo); + + INTERCEPT_FUNCTION(syscall); } #endif // SANITIZER_POSIX diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp index f715b0b11b288..88df8ec46d0a3 100644 --- a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp +++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -1090,4 +1091,20 @@ TEST(TestRtsanInterceptors, PipeDiesWhenRealtime) { ExpectNonRealtimeSurvival(Func); } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +TEST(TestRtsanInterceptors, SyscallDiesWhenRealtime) { + auto Func = []() { syscall(SYS_getpid); }; + ExpectRealtimeDeath(Func, "syscall"); + ExpectNonRealtimeSurvival(Func); +} + +TEST(TestRtsanInterceptors, GetPidReturnsSame) { + int pid = syscall(SYS_getpid); + EXPECT_THAT(pid, Ne(-1)); + + EXPECT_THAT(getpid(), Eq(pid)); +} +#pragma clang diagnostic pop + #endif // SANITIZER_POSIX diff --git a/compiler-rt/test/rtsan/syscall.cpp b/compiler-rt/test/rtsan/syscall.cpp new file mode 100644 index 0000000000000..a23a934184a69 --- /dev/null +++ b/compiler-rt/test/rtsan/syscall.cpp @@ -0,0 +1,78 @@ +// RUN: %clangxx -fsanitize=realtime %s -o %t +// RUN: %env_rtsan_opts="halt_on_error=false" %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK-RTSAN,CHECK + +// RUN: %clangxx %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// UNSUPPORTED: ios + +// Intent: Ensure the `syscall` call behaves in the same way with/without the +// sanitizer disabled + +#include +#include +#include +#include +#include +#include +#include + +const char *GetTemporaryFilePath() { return "/tmp/rtsan_syscall_test.txt"; } + +void custom_assert(bool condition, const char *message) { + if (!condition) { + fprintf(stderr, "ASSERTION FAILED: %s\n", message); + exit(1); + } +} + +class ScopedFileCleanup { +public: + [[nodiscard]] ScopedFileCleanup() = default; + ~ScopedFileCleanup() { + if (access(GetTemporaryFilePath(), F_OK) != -1) + unlink(GetTemporaryFilePath()); + } +}; + +// Apple has deprecated `syscall`, ignore that error +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +int main() [[clang::nonblocking]] { + ScopedFileCleanup cleanup; + + { + int fd = syscall(SYS_openat, AT_FDCWD, GetTemporaryFilePath(), + O_CREAT | O_WRONLY, 0644); + + custom_assert(fd != -1, "Failed to open file - write"); + + int written = syscall(SYS_write, fd, "Hello, world!", 13); + custom_assert(written == 13, "Failed to write to file"); + + custom_assert(syscall(SYS_close, fd) == 0, "Failed to close file - write"); + } + + { + int fd = syscall(SYS_openat, AT_FDCWD, GetTemporaryFilePath(), O_RDONLY); + custom_assert(fd != -1, "Failed to open file - read"); + + char buffer[13]; + int read = syscall(SYS_read, fd, buffer, 13); + custom_assert(read == 13, "Failed to read from file"); + + custom_assert(memcmp(buffer, "Hello, world!", 13) == 0, + "Read data does not match written data"); + + custom_assert(syscall(SYS_close, fd) == 0, "Failed to close file - read"); + } + + unlink(GetTemporaryFilePath()); + printf("DONE\n"); +} +#pragma clang diagnostic pop + +// CHECK-NOT: ASSERTION FAILED +// CHECK-RTSAN-COUNT-6: Intercepted call to real-time unsafe function `syscall` + +// CHECK: DONE