aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThurston Dang <thurston@google.com>2025-08-05 16:31:35 -0700
committerGitHub <noreply@github.com>2025-08-05 16:31:35 -0700
commit435b8b51dc7e236d3940efe4b94104338dbac0e8 (patch)
tree13561b377ceead1400d36b606e3712b574fc9d96
parentb8eb61adc92bb384bc63f01b7ccddd931409b223 (diff)
downloadllvm-435b8b51dc7e236d3940efe4b94104338dbac0e8.zip
llvm-435b8b51dc7e236d3940efe4b94104338dbac0e8.tar.gz
llvm-435b8b51dc7e236d3940efe4b94104338dbac0e8.tar.bz2
[sanitizer] Don't TestPTrace() if SPARC; don't give up if internal_fork() fails (#152072)
Fixes corner cases of https://github.com/llvm/llvm-project/pull/151406: - Don't run TestPTrace() on SPARC, because internal_fork() on SPARC actually calls __fork(). We can't safely __fork(), because it's possible seccomp has been configured to disallow fork() but allow clone(). - if internal_fork() fails for whatever reason, we shouldn't give up. It is strictly worse to give up early than to attempt StopTheWorld. Also updates some comments/TODOs.
-rw-r--r--compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp36
1 files changed, 29 insertions, 7 deletions
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp
index d5cf0f1..5fde65e 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp
@@ -405,10 +405,21 @@ struct ScopedSetTracerPID {
// This detects whether ptrace is blocked (e.g., by seccomp), by forking and
// then attempting ptrace.
-// This separate check is necessary because StopTheWorld() creates a child
-// process with a shared virtual address space and shared TLS, and therefore
+// This separate check is necessary because StopTheWorld() creates a thread
+// with a shared virtual address space and shared TLS, and therefore
// cannot use waitpid() due to the shared errno.
static void TestPTrace() {
+# if SANITIZER_SPARC
+ // internal_fork() on SPARC actually calls __fork(). We can't safely fork,
+ // because it's possible seccomp has been configured to disallow fork() but
+ // allow clone().
+ Report("WARNING: skipping TestPTrace() because this is SPARC\n");
+ Report(
+ "If seccomp blocks ptrace, LeakSanitizer may hang without further "
+ "notice\n");
+ Report(
+ "If seccomp does not block ptrace, you can safely ignore this warning\n");
+# else
// Heuristic: only check the first time this is called. This is not always
// correct (e.g., user manually triggers leak detection, then updates
// seccomp, then leak detection is triggered again).
@@ -417,35 +428,46 @@ static void TestPTrace() {
return;
checked = true;
- // We hope that fork() is not too expensive, because of copy-on-write.
+ // Hopefully internal_fork() is not too expensive, thanks to copy-on-write.
// Besides, this is only called the first time.
+ // Note that internal_fork() on non-SPARC Linux actually calls
+ // SYSCALL(clone); thus, it is reasonable to use it because if seccomp kills
+ // TestPTrace(), it would have killed StopTheWorld() anyway.
int pid = internal_fork();
if (pid < 0) {
int rverrno;
- if (internal_iserror(pid, &rverrno)) {
+ if (internal_iserror(pid, &rverrno))
Report("WARNING: TestPTrace() failed to fork (errno %d)\n", rverrno);
- }
- internal__exit(-1);
+
+ // We don't abort the sanitizer - it's still worth letting the sanitizer
+ // try.
+ return;
}
if (pid == 0) {
// Child subprocess
+
+ // TODO: consider checking return value of internal_ptrace, to handle
+ // SCMP_ACT_ERRNO. However, be careful not to consume too many
+ // resources performing a proper ptrace.
internal_ptrace(PTRACE_ATTACH, 0, nullptr, nullptr);
internal__exit(0);
} else {
int wstatus;
internal_waitpid(pid, &wstatus, 0);
+ // Handle SCMP_ACT_KILL
if (WIFSIGNALED(wstatus)) {
VReport(0,
- "Warning: ptrace appears to be blocked (is seccomp enabled?). "
+ "WARNING: ptrace appears to be blocked (is seccomp enabled?). "
"LeakSanitizer may hang.\n");
VReport(0, "Child exited with signal %d.\n", WTERMSIG(wstatus));
// We don't abort the sanitizer - it's still worth letting the sanitizer
// try.
}
}
+# endif
}
void StopTheWorld(StopTheWorldCallback callback, void *argument) {