Skip to content

Conversation

DavidSpickett
Copy link
Collaborator

Fixes #161510

Addresses the Linux parts of #138085

The situation we have to handle here is systems where Yama ptrace_scope set to 1.

1 - restricted ptrace: a process must have a predefined relationship
with the inferior it wants to call PTRACE_ATTACH on. By default,
this relationship is that of only its descendants when the above
classic criteria is also met. To change the relationship, an
inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
an allowed debugger PID to call PTRACE_ATTACH on the inferior.
Using PTRACE_TRACEME is unchanged.

(https://www.kernel.org/doc/Documentation/security/Yama.txt)

The inferior was addressing this by calling this at the start of main():
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);

Which is ok if lldb-server tries to attach after that call has happened, but there was nothing to synchronise this. So if the system was heavily loaded, the inferior may be stalled, delaying the call, causing lldb-server to fail to attach with EPERM (permission denied).

We were not using any mechanism to retry the attach or wait for some signal from the inferior.

Except we do do this in other tests, even other lldb-server tests.

So I have adopted that mechanism to these tests:

  • The inferior is launched with syncfile:<path> as its first argument.
  • It creates this file at <path>, at a point where we know attaching has been allowed.
  • The test framework launches the inferior then waits for the file to appear.
  • This check is retried a few times, increasing the delay each time and eventually giving up.
  • Only once it has seen the file does it start lldb-server and tell it to attach to the inferior.

I have tested this by insterting a sleep() call before the attach enable call and running the test on a machine with ptrace_scope set to 1. I was able to increase the sleep to 6 seconds before tests failed (when running just these tests, single threaded).

With OS scheduling, you could be stalled indefinitely, so we may have to increase this timeout but this is easy to do with wait_for_file_on_target.

The alternative is to have the test runner check ptrace_scope and only enable these on systems where it's 0. Would be good to keep them running if we can though.

…er test inferior

Fixes llvm#161510

Addresses the Linux parts of llvm#138085

The situation we have to handle here is systems where
Yama ptrace_scope set to 1.

> 1 - restricted ptrace: a process must have a predefined relationship
>     with the inferior it wants to call PTRACE_ATTACH on. By default,
>     this relationship is that of only its descendants when the above
>     classic criteria is also met. To change the relationship, an
>     inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
>     an allowed debugger PID to call PTRACE_ATTACH on the inferior.
>     Using PTRACE_TRACEME is unchanged.

(https://www.kernel.org/doc/Documentation/security/Yama.txt)

The inferior was addressing this by calling this at the start
of main():
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);

Which is ok if lldb-server tries to attach after that call has happened,
but there was nothing to synchronise this. So if the system was heavily
loaded, the inferior may be stalled, delaying the call, causing lldb-server
to fail to attach with EPERM (permission denied).

We were not using any mechanism to retry the attach or wait for some
signal from the inferior.

Except we do do this in other tests, even other lldb-server tests.

So I have adopted that mechanism to these tests:
* The inferior is launched with `syncfile:<path>` as its first
  argument.
* It creates this file at `<path>`, at a point where we know attaching has
  been allowed.
* The test framework launches the inferior then waits for the file
  to appear.
* This check is retried a few times, increasing the delay each time
  and eventually giving up.
* Only once it has seen the file does it start lldb-server and
  tell it to attach to the inferior.

I have tested this by insterting a `sleep()` call before the
attach enable call and running the test on a machine with
ptrace_scope set to 1. I was able to increase the sleep to 6 seconds
before tests failed (when running just these tests, single threaded).

With OS scheduling, you could be stalled indefinitely, so we may
have to increase this timeout but this is easy to do with
wait_for_file_on_target.

The alternative is to have the test runner check ptrace_scope
and only enable these on systems where it's 0. Would be good
to keep them running if we can though.
@llvmbot
Copy link
Member

llvmbot commented Oct 6, 2025

@llvm/pr-subscribers-lldb

Author: David Spickett (DavidSpickett)

Changes

Fixes #161510

Addresses the Linux parts of #138085

The situation we have to handle here is systems where Yama ptrace_scope set to 1.

> 1 - restricted ptrace: a process must have a predefined relationship
> with the inferior it wants to call PTRACE_ATTACH on. By default,
> this relationship is that of only its descendants when the above
> classic criteria is also met. To change the relationship, an
> inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
> an allowed debugger PID to call PTRACE_ATTACH on the inferior.
> Using PTRACE_TRACEME is unchanged.

(https://www.kernel.org/doc/Documentation/security/Yama.txt)

The inferior was addressing this by calling this at the start of main():
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);

Which is ok if lldb-server tries to attach after that call has happened, but there was nothing to synchronise this. So if the system was heavily loaded, the inferior may be stalled, delaying the call, causing lldb-server to fail to attach with EPERM (permission denied).

We were not using any mechanism to retry the attach or wait for some signal from the inferior.

Except we do do this in other tests, even other lldb-server tests.

So I have adopted that mechanism to these tests:

  • The inferior is launched with syncfile:&lt;path&gt; as its first argument.
  • It creates this file at &lt;path&gt;, at a point where we know attaching has been allowed.
  • The test framework launches the inferior then waits for the file to appear.
  • This check is retried a few times, increasing the delay each time and eventually giving up.
  • Only once it has seen the file does it start lldb-server and tell it to attach to the inferior.

I have tested this by insterting a sleep() call before the attach enable call and running the test on a machine with ptrace_scope set to 1. I was able to increase the sleep to 6 seconds before tests failed (when running just these tests, single threaded).

With OS scheduling, you could be stalled indefinitely, so we may have to increase this timeout but this is easy to do with wait_for_file_on_target.

The alternative is to have the test runner check ptrace_scope and only enable these on systems where it's 0. Would be good to keep them running if we can though.


Full diff: https://github.com/llvm/llvm-project/pull/162064.diff

2 Files Affected:

  • (modified) lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py (+9-2)
  • (modified) lldb/test/API/tools/lldb-server/main.cpp (+5-1)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
index 19c766996292e..aea6b9fe36b2c 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -444,13 +444,20 @@ def launch_process_for_attach(
         if not exe_path:
             exe_path = self.getBuildArtifact("a.out")
 
-        args = []
+        # This file will be created once the inferior has enabled attaching.
+        sync_file_path = lldbutil.append_to_process_working_directory(
+            self, "process_ready"
+        )
+        args = [f"syncfile:{sync_file_path}"]
         if inferior_args:
             args.extend(inferior_args)
         if sleep_seconds:
             args.append("sleep:%d" % sleep_seconds)
 
-        return self.spawnSubprocess(exe_path, args)
+        inferior = self.spawnSubprocess(exe_path, args)
+        lldbutil.wait_for_file_on_target(self, sync_file_path)
+
+        return inferior
 
     def prep_debug_monitor_and_inferior(
         self,
diff --git a/lldb/test/API/tools/lldb-server/main.cpp b/lldb/test/API/tools/lldb-server/main.cpp
index 0e9323cce88cf..688c17a360b13 100644
--- a/lldb/test/API/tools/lldb-server/main.cpp
+++ b/lldb/test/API/tools/lldb-server/main.cpp
@@ -6,6 +6,7 @@
 #include <cstring>
 #include <errno.h>
 #include <future>
+#include <fstream>
 #include <inttypes.h>
 #include <memory>
 #include <mutex>
@@ -265,7 +266,10 @@ int main(int argc, char **argv) {
   // Process command line args.
   for (int i = 1; i < argc; ++i) {
     std::string arg = argv[i];
-    if (consume_front(arg, "stderr:")) {
+    if (consume_front(arg, "syncfile:")) {
+      // Write to this file to tell test framework that attaching is now possible.
+      std::ofstream(arg).close();
+    } else if (consume_front(arg, "stderr:")) {
       // Treat remainder as text to go to stderr.
       fprintf(stderr, "%s\n", arg.c_str());
     } else if (consume_front(arg, "retval:")) {

Copy link

github-actions bot commented Oct 6, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@DavidSpickett DavidSpickett requested a review from labath October 6, 2025 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Intermittent lldb-api.tools/lldb-server/TestLldbGdbServer.py failure
2 participants