From 890adf486cd16e531eeb6ac18c2522f2969364d7 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Tue, 5 Aug 2025 03:46:30 +0000 Subject: [PATCH 1/4] [sanitizer] Don't TestPTrace() if SPARC Fixes corner case of https://github.com/llvm/llvm-project/pull/151406 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(). Also adds some comments/TODOs. --- .../sanitizer_stoptheworld_linux_libcdep.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 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 d5cf0f14dfeb8..00426de50a388 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp @@ -409,6 +409,12 @@ struct ScopedSetTracerPID { // process 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"); +# 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,26 +423,36 @@ 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?). " @@ -446,6 +462,7 @@ static void TestPTrace() { // try. } } +# endif } void StopTheWorld(StopTheWorldCallback callback, void *argument) { From 2ec6558aa73c287fe9c21580ac5896646e2766bb Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Tue, 5 Aug 2025 04:23:18 +0000 Subject: [PATCH 2/4] Fix up "process" -> "thread" --- .../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 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 00426de50a388..f38e44cdc5960 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp @@ -405,8 +405,8 @@ 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 From 5a630c5684a14dd768c0456238086914fe33d458 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Tue, 5 Aug 2025 17:54:52 +0000 Subject: [PATCH 3/4] Elaborate on SPARC warning --- .../sanitizer_stoptheworld_linux_libcdep.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 f38e44cdc5960..0d1f21bad0944 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp @@ -413,7 +413,12 @@ static void TestPTrace() { // 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("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 From 18d07e49aee94888f7578026d1e7993eb9946bf7 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Tue, 5 Aug 2025 18:00:22 +0000 Subject: [PATCH 4/4] CAPITALIZE --- .../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 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 0d1f21bad0944..5fde65ea48c42 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp @@ -413,7 +413,7 @@ static void TestPTrace() { // 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("WARNING: skipping TestPTrace() because this is SPARC\n"); Report( "If seccomp blocks ptrace, LeakSanitizer may hang without further " "notice\n"); @@ -460,7 +460,7 @@ static void TestPTrace() { // 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