[llvm-branch-commits] [lldb] 2ffba26 - Revert "[lldb] Survive ptrace(PT_DENY_ATTACH) when attaching (#204688)"

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Jun 22 02:59:24 PDT 2026


Author: Raphael Isemann
Date: 2026-06-22T10:59:20+01:00
New Revision: 2ffba269566eabccfbd7d2ee4586c4586d9a70a1

URL: https://github.com/llvm/llvm-project/commit/2ffba269566eabccfbd7d2ee4586c4586d9a70a1
DIFF: https://github.com/llvm/llvm-project/commit/2ffba269566eabccfbd7d2ee4586c4586d9a70a1.diff

LOG: Revert "[lldb] Survive ptrace(PT_DENY_ATTACH) when attaching (#204688)"

This reverts commit f4043dbcafc93f9107b7c93ee8740cdb0fe22d7c.

Added: 
    

Modified: 
    lldb/tools/debugserver/source/MacOSX/MachProcess.mm

Removed: 
    lldb/test/API/macosx/deny-attach/Makefile
    lldb/test/API/macosx/deny-attach/TestDenyAttach.py
    lldb/test/API/macosx/deny-attach/main.c


################################################################################
diff  --git a/lldb/test/API/macosx/deny-attach/Makefile b/lldb/test/API/macosx/deny-attach/Makefile
deleted file mode 100644
index 10495940055b6..0000000000000
--- a/lldb/test/API/macosx/deny-attach/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-C_SOURCES := main.c
-
-include Makefile.rules

diff  --git a/lldb/test/API/macosx/deny-attach/TestDenyAttach.py b/lldb/test/API/macosx/deny-attach/TestDenyAttach.py
deleted file mode 100644
index f061bee51c31f..0000000000000
--- a/lldb/test/API/macosx/deny-attach/TestDenyAttach.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import time
-import lldb
-from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-
-
-class DenyAttachTestCase(TestBase):
-    NO_DEBUG_INFO_TESTCASE = True
-
-    @skipUnlessDarwin
-    @skipIfDarwinEmbedded  # PT_DENY_ATTACH attach behavior 
diff ers on ios/tvos/etc
-    @skipIfAsan  # Attach tests time out inconsistently under asan.
-    def test_attach_to_deny_attach_process(self):
-        """Attaching to a PT_DENY_ATTACH process reports an error, not a crash."""
-        self.build()
-        exe = self.getBuildArtifact("a.out")
-
-        # Use a file as a synchronization point between test and inferior: the
-        # inferior writes its pid only after it has called PT_DENY_ATTACH.
-        pid_file_path = lldbutil.append_to_process_working_directory(
-            self, "pid_file_%d" % (int(time.time()))
-        )
-        self.addTearDownHook(
-            lambda: self.run_platform_command("rm %s" % (pid_file_path))
-        )
-
-        popen = self.spawnSubprocess(exe, [pid_file_path])
-        pid = lldbutil.wait_for_file_on_target(self, pid_file_path)
-
-        self.expect(
-            "process attach -p " + pid,
-            startstr="error: attach failed:",
-            substrs=["PT_DENY_ATTACH"],
-            error=True,
-        )

diff  --git a/lldb/test/API/macosx/deny-attach/main.c b/lldb/test/API/macosx/deny-attach/main.c
deleted file mode 100644
index 1f19f6928604a..0000000000000
--- a/lldb/test/API/macosx/deny-attach/main.c
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/ptrace.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-// Write our pid into file_name atomically (write to a temp file, then rename)
-// so the test never observes a partially written pid.
-static int write_pid(const char *file_name) {
-  char tmp_name[1024];
-  snprintf(tmp_name, sizeof(tmp_name), "%s_tmp", file_name);
-
-  int fd = open(tmp_name, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR);
-  if (fd == -1) {
-    fprintf(stderr, "open(%s) failed: %s\n", tmp_name, strerror(errno));
-    return 1;
-  }
-
-  char buffer[64];
-  int len = snprintf(buffer, sizeof(buffer), "%ld", (long)getpid());
-  int result = 0;
-  if (write(fd, buffer, len) == -1) {
-    fprintf(stderr, "write failed: %s\n", strerror(errno));
-    result = 1;
-  }
-  close(fd);
-
-  if (rename(tmp_name, file_name) == -1) {
-    fprintf(stderr, "rename failed: %s\n", strerror(errno));
-    result = 1;
-  }
-  return result;
-}
-
-int main(int argc, char const *argv[]) {
-  if (argc < 2) {
-    fprintf(stderr, "invalid number of command line arguments\n");
-    return 1;
-  }
-
-  // Tell the kernel to refuse all debugger attachments to this process. Any
-  // subsequent ptrace(PT_ATTACHEXC) against us makes the kernel deliver SIGSEGV
-  // to the attaching process (debugserver).
-  if (ptrace(PT_DENY_ATTACH, 0, 0, 0) == -1) {
-    fprintf(stderr, "ptrace(PT_DENY_ATTACH) failed: %s\n", strerror(errno));
-    return 1;
-  }
-
-  if (write_pid(argv[1]) != 0)
-    return 1;
-
-  // Wait for the debugger to try (and fail) to attach.
-  while (1)
-    sleep(60);
-
-  return 0;
-}

diff  --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
index 3374267f7bde8..d79313846438b 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
+++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.mm
@@ -19,7 +19,6 @@
 #include <mach/mach.h>
 #include <mach/task.h>
 #include <pthread.h>
-#include <setjmp.h>
 #include <signal.h>
 #include <spawn.h>
 #include <sys/fcntl.h>
@@ -2937,79 +2936,11 @@ static uint64_t bits(uint64_t value, uint32_t msbit, uint32_t lsbit) {
   return NULL;
 }
 
-namespace {
-// The XNU kernel enforces ptrace(PT_DENY_ATTACH) by delivering a SIGSEGV to the
-// process that tries to attach, while it is still inside the ptrace() syscall.
-// That kills debugserver outright instead of failing the call with an error.
-// This leaves lldb unable to tell the user why the attach failed. The condition
-// can't be detected up front because the target's P_LNOATTACH flag isn't
-// exposed to userspace, so instead we install a temporary SIGSEGV handler
-// around the ptrace() call and jump back out of it if the signal fires, turning
-// the fatal signal into a clean error.
-
-sigjmp_buf g_deny_attach_jmpbuf;
-// Only act on the SIGSEGV if it arrives on the thread that armed the guard
-// while a PT_ATTACHEXC call is in flight; anything else is a genuine crash.
-volatile sig_atomic_t g_deny_attach_armed = 0;
-pthread_t g_deny_attach_thread;
-
-void DenyAttachSIGSEGVHandler(int signo) {
-  if (g_deny_attach_armed &&
-      pthread_equal(pthread_self(), g_deny_attach_thread)) {
-    g_deny_attach_armed = 0;
-    siglongjmp(g_deny_attach_jmpbuf, 1);
-  }
-  // Not the deny-attach case: restore the default disposition and re-raise so a
-  // real crash is still reported the usual way.
-  signal(signo, SIG_DFL);
-  raise(signo);
-}
-
-// Wrapper around ptrace(PT_ATTACHEXC, pid) that survives the SIGSEGV the kernel
-// sends when `pid` has called ptrace(PT_DENY_ATTACH). On a normal attach it
-// behaves exactly like ptrace() (returning its result with errno set). If the
-// attach is rejected via the deny-attach signal it sets `denied_attach` and
-// returns -1 with errno set to EPERM.
-int PTraceAttachExcDenyAttachSafe(pid_t pid, bool &denied_attach) {
-  denied_attach = false;
-
-  struct sigaction new_action = {};
-  struct sigaction old_action = {};
-  new_action.sa_handler = DenyAttachSIGSEGVHandler;
-  sigemptyset(&new_action.sa_mask);
-  // SA_NODEFER so a genuine fault inside the handler crashes normally instead
-  // of deadlocking with SIGSEGV blocked.
-  new_action.sa_flags = SA_NODEFER;
-
-  if (::sigaction(SIGSEGV, &new_action, &old_action) != 0) {
-    // Couldn't install the handler; fall back to the unguarded call.
-    return ::ptrace(PT_ATTACHEXC, pid, 0, 0);
-  }
-
-  g_deny_attach_thread = pthread_self();
-  int result;
-  int saved_errno;
-  if (sigsetjmp(g_deny_attach_jmpbuf, 1) == 0) {
-    g_deny_attach_armed = 1;
-    result = ::ptrace(PT_ATTACHEXC, pid, 0, 0);
-    saved_errno = errno;
-    g_deny_attach_armed = 0;
-  } else {
-    // The kernel delivered SIGSEGV: the target denied the attach.
-    denied_attach = true;
-    result = -1;
-    saved_errno = EPERM;
-  }
-
-  ::sigaction(SIGSEGV, &old_action, nullptr);
-  errno = saved_errno;
-  return result;
-}
-} // namespace
-
 pid_t MachProcess::AttachForDebug(
-    pid_t pid, const RNBContext::IgnoredExceptions &ignored_exceptions,
-    char *err_str, size_t err_len) {
+    pid_t pid, 
+    const RNBContext::IgnoredExceptions &ignored_exceptions, 
+    char *err_str,
+    size_t err_len) {
   // Clear out and clean up from any current state
   Clear();
   if (pid != 0) {
@@ -3042,8 +2973,7 @@ int PTraceAttachExcDenyAttachSafe(pid_t pid, bool &denied_attach) {
     DNBLog("[LaunchAttach] (%d) About to ptrace(PT_ATTACHEXC, %d)...", getpid(),
            pid);
     errno = 0;
-    bool denied_attach = false;
-    int ptrace_result = PTraceAttachExcDenyAttachSafe(pid, denied_attach);
+    int ptrace_result = ::ptrace(PT_ATTACHEXC, pid, 0, 0);
     int ptrace_errno = errno;
     DNBLog("[LaunchAttach] (%d) Completed ptrace(PT_ATTACHEXC, %d) == %d",
            getpid(), pid, ptrace_result);
@@ -3060,18 +2990,6 @@ int PTraceAttachExcDenyAttachSafe(pid_t pid, bool &denied_attach) {
       m_flags |= eMachProcessFlagsAttached;
       DNBLogThreadedIf(LOG_PROCESS, "successfully attached to pid %d", pid);
       return m_pid;
-    } else if (denied_attach) {
-      // The target denied being debugged via ptrace(PT_DENY_ATTACH). The kernel
-      // would normally kill debugserver for attempting this; we caught the
-      // signal instead, so report a useful error rather than crashing.
-      snprintf(err_str, err_len,
-               "cannot attach to process %d because it has disabled debugging "
-               "via ptrace(PT_DENY_ATTACH). Attach earlier, put a breakpoint "
-               "on ptrace and return 0.",
-               pid);
-      DNBLogError("[LaunchAttach] (%d) MachProcess::AttachForDebug pid %d "
-                  "denied attach via ptrace(PT_DENY_ATTACH)",
-                  getpid(), pid);
     } else {
       ::snprintf(err_str, err_len, "%s", err.AsString());
       DNBLogError(


        


More information about the llvm-branch-commits mailing list