Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[compiler-rt][rtsan] ptrace interception on Linux. #123941

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

devnexen
Copy link
Member

No description provided.

Copy link

github-actions bot commented Jan 22, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@devnexen devnexen marked this pull request as ready for review January 22, 2025 16:51
@devnexen devnexen requested a review from cjappl January 22, 2025 16:51
@llvmbot
Copy link
Member

llvmbot commented Jan 22, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: David CARLIER (devnexen)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/123941.diff

2 Files Affected:

  • (modified) compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp (+38)
  • (modified) compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp (+11)
diff --git a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
index 71938d3edba38d..64e3fab8200efc 100644
--- a/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/rtsan/rtsan_interceptors_posix.cpp
@@ -41,6 +41,10 @@ void OSSpinLockLock(volatile OSSpinLock *__lock);
 #include <malloc.h>
 #endif
 
+#if SANITIZER_INTERCEPT_PTRACE
+#include <sys/ptrace.h>
+#endif
+
 #include <fcntl.h>
 #include <poll.h>
 #include <pthread.h>
@@ -1161,6 +1165,39 @@ INTERCEPTOR(ssize_t, process_vm_writev, pid_t pid,
 #define RTSAN_MAYBE_INTERCEPT_PROCESS_VM_WRITEV
 #endif
 
+#if SANITIZER_INTERCEPT_PTRACE
+#if SANITIZER_MUSL
+INTERCEPTOR(long, ptrace, int request, ...) {
+#else
+INTERCEPTOR(long, ptrace, enum __ptrace_request request, ...) {
+#endif // SANITIZER_MUSL
+  __rtsan_notify_intercepted_call("ptrace");
+  va_list args;
+
+  // A handful of ptrace requests need an additional argument on Linux/sparc
+  // (e.g. PTRACE_READDATA) but we only intercept the standard calls at the
+  // moment. We might need to rework all if rtsan is supported on BSD,
+  // interfaces differ vastly, data is read in word size on Linux vs large
+  // chunks on freebsd and so on ...
+  va_start(args, request);
+  pid_t pid = va_arg(args, pid_t);
+
+  // addr and data types depend on the request, either of these are ignored in
+  // some cases too. using intptr_t to be able to hold accepted ptrace types.
+  using arg_type = intptr_t;
+  static_assert(sizeof(arg_type) >= sizeof(struct ptrace_seekinfo_args *));
+  static_assert(sizeof(arg_type) >= sizeof(int));
+  arg_type addr = va_arg(args, arg_type);
+  arg_type priv = va_arg(args, arg_type);
+  va_end(args);
+
+  return REAL(ptrace)(request, pid, addr, priv);
+}
+#define RTSAN_MAYBE_INTERCEPT_PTRACE INTERCEPT_FUNCTION(ptrace)
+#else
+#define RTSAN_MAYBE_INTERCEPT_PTRACE
+#endif
+
 // 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
@@ -1347,6 +1384,7 @@ void __rtsan::InitializeInterceptors() {
 
   RTSAN_MAYBE_INTERCEPT_PROCESS_VM_READV;
   RTSAN_MAYBE_INTERCEPT_PROCESS_VM_WRITEV;
+  RTSAN_MAYBE_INTERCEPT_PTRACE;
 
   INTERCEPT_FUNCTION(syscall);
 }
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 0a59ae0ea92548..8de6c3f9599368 100644
--- a/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
+++ b/compiler-rt/lib/rtsan/tests/rtsan_test_interceptors_posix.cpp
@@ -43,6 +43,9 @@
 #include <poll.h>
 #include <pthread.h>
 #include <stdio.h>
+#if SANITIZER_INTERCEPT_PTRACE
+#include <sys/ptrace.h>
+#endif
 #include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <sys/socket.h>
@@ -748,6 +751,14 @@ TEST(TestRtsanInterceptors, ProcessVmWritevDiesWhenRealtime) {
 }
 #endif
 
+#if SANITIZER_INTERCEPT_PTRACE
+TEST(TestRtsanInterceptors, PtraceWhenRealtime) {
+  auto Func = []() { ptrace(PTRACE_PEEKUSER, -1, 0, nullptr); };
+  ExpectRealtimeDeath(Func, "ptrace");
+  ExpectNonRealtimeSurvival(Func);
+}
+#endif
+
 class RtsanDirectoryTest : public ::testing::Test {
 protected:
   void SetUp() override {

@cjappl cjappl requested a review from davidtrevelyan January 24, 2025 01:18
@cjappl
Copy link
Contributor

cjappl commented Jan 24, 2025

I think this is another one like the scheduling ones - this call is used to debug, do we intercept it or assume that someone using it knows what they are doing? An alternative read could be "People should be notified if they accidentally leave ptrace in, and they will not be using it alongside rtsan".

@davidtrevelyan do you think we should intercept this call? I'm on the fence

@davidtrevelyan
Copy link
Contributor

@cjappl i lean towards intercepting - remembering that our users care about real-time-unsafe calls from within dependencies as well as from within their own code. I'd say that we have easy-enough escape hatches for people who really want to escape to make the cost of missing a ptrace enough to outweigh the cost of disabling when you really want it :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants