@@ -403,7 +403,54 @@ struct ScopedSetTracerPID {
403403 }
404404};
405405
406+ // This detects whether ptrace is blocked (e.g., by seccomp), by forking and
407+ // then attempting ptrace.
408+ // This separate check is necessary because StopTheWorld() creates a child
409+ // process with a shared virtual address space and shared TLS, and therefore
410+ // cannot use waitpid() due to the shared errno.
411+ static void TestPTrace () {
412+ // Heuristic: only check the first time this is called. This is not always
413+ // correct (e.g., user manually triggers leak detection, then updates
414+ // seccomp, then leak detection is triggered again).
415+ static bool checked = false ;
416+ if (checked)
417+ return ;
418+ checked = true ;
419+
420+ // We hope that fork() is not too expensive, because of copy-on-write.
421+ // Besides, this is only called the first time.
422+ int pid = internal_fork ();
423+
424+ if (pid < 0 ) {
425+ int rverrno;
426+ if (internal_iserror (pid, &rverrno)) {
427+ Report (" WARNING: TestPTrace() failed to fork (errno %d)\n " , rverrno);
428+ }
429+ _exit (-1 );
430+ }
431+
432+ if (pid == 0 ) {
433+ // Child subprocess
434+ internal_ptrace (PTRACE_ATTACH, 0 , nullptr , nullptr );
435+ _exit (0 );
436+ } else {
437+ int wstatus;
438+ internal_waitpid (pid, &wstatus, 0 );
439+
440+ if (WIFSIGNALED (wstatus)) {
441+ VReport (0 ,
442+ " Warning: ptrace appears to be blocked (is seccomp enabled?). "
443+ " LeakSanitizer may hang.\n " );
444+ VReport (0 , " Child exited with signal %d.\n " , WTERMSIG (wstatus));
445+ // We don't abort the sanitizer - it's still worth letting the sanitizer
446+ // try.
447+ }
448+ }
449+ }
450+
406451void StopTheWorld (StopTheWorldCallback callback, void *argument) {
452+ TestPTrace ();
453+
407454 StopTheWorldScope in_stoptheworld;
408455 // Prepare the arguments for TracerThread.
409456 struct TracerThreadArgument tracer_thread_argument;
@@ -457,7 +504,8 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) {
457504 internal_prctl (PR_SET_PTRACER, tracer_pid, 0 , 0 , 0 );
458505 // Allow the tracer thread to start.
459506 tracer_thread_argument.mutex .Unlock ();
460- // NOTE: errno is shared between this thread and the tracer thread.
507+ // NOTE: errno is shared between this thread and the tracer thread
508+ // (clone was called without CLONE_SETTLS / newtls).
461509 // internal_waitpid() may call syscall() which can access/spoil errno,
462510 // so we can't call it now. Instead we for the tracer thread to finish using
463511 // the spin loop below. Man page for sched_yield() says "In the Linux
0 commit comments