[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