[libc-commits] [libc] [libc] Support for argument-taking ptrace requests (PR #201830)
via libc-commits
libc-commits at lists.llvm.org
Fri Jun 5 05:48:44 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Pavel Labath (labath)
<details>
<summary>Changes</summary>
Some of these requests are architecture specific, and linux/ptrace.h is pretty clean, so I'm including it for all the constants definitions (this matches bionic, but not musl or glibc).
There's one unfortunate rename that we need to fix up (kernel uses PTRACE_PEEKUSR, without the *E*), and (matching all libc implementations) I also add the BSD-compatibility names for the basic requests (although the usefulness if this is questionable as nearly everything about ptrace is OS-specific).
Some of the options structs also have a different name in the kernel headers (ptrace_peeksiginfo_args) and glibc (__ptrace_peeksiginfo_args), but:
- these are very scarcely used, and
- the manpage actually documents the kernel names which is why I'm hoping we can get away with this.
The ptrace entry point implements the standard PTRACE_PEEK translation from by-ptr arguments to return values.
---
Full diff: https://github.com/llvm/llvm-project/pull/201830.diff
9 Files Affected:
- (modified) libc/hdr/CMakeLists.txt (+9)
- (added) libc/hdr/sys_ptrace_macros.h (+27)
- (modified) libc/include/llvm-libc-macros/sys-ptrace-macros.h (+17-1)
- (modified) libc/include/sys/ptrace.yaml (+24-2)
- (modified) libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt (+1)
- (modified) libc/src/__support/OSUtil/linux/syscall_wrappers/ptrace.h (+6-2)
- (modified) libc/src/sys/ptrace/linux/CMakeLists.txt (+5-1)
- (modified) libc/src/sys/ptrace/linux/ptrace.cpp (+19-3)
- (modified) libc/test/integration/src/sys/ptrace/linux/ptrace_test.cpp (+31-7)
``````````diff
diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index 8da1e2a2a1872..0bc0202022680 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -203,6 +203,15 @@ add_proxy_header_library(
libc.include.llvm-libc-macros.sys_mman_macros
)
+add_proxy_header_library(
+ sys_ptrace_macros
+ HDRS
+ sys_ptrace_macros.h
+ FULL_BUILD_DEPENDS
+ libc.include.sys_ptrace
+ libc.include.llvm-libc-macros.sys_ptrace_macros
+)
+
add_header_library(unistd_overlay HDRS unistd_overlay.h)
add_proxy_header_library(
unistd_macros
diff --git a/libc/hdr/sys_ptrace_macros.h b/libc/hdr/sys_ptrace_macros.h
new file mode 100644
index 0000000000000..61b3f033e0fa4
--- /dev/null
+++ b/libc/hdr/sys_ptrace_macros.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Definition of proxy macros from sys/ptrace.h.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_SYS_PTRACE_MACROS_H
+#define LLVM_LIBC_HDR_SYS_PTRACE_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/sys-ptrace-macros.h"
+
+#else // Overlay mode
+
+#include <sys/ptrace.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_SYS_PTRACE_MACROS_H
diff --git a/libc/include/llvm-libc-macros/sys-ptrace-macros.h b/libc/include/llvm-libc-macros/sys-ptrace-macros.h
index dd5c024ef7c8c..f1d782fbfbdc7 100644
--- a/libc/include/llvm-libc-macros/sys-ptrace-macros.h
+++ b/libc/include/llvm-libc-macros/sys-ptrace-macros.h
@@ -14,7 +14,23 @@
#ifndef LLVM_LIBC_MACROS_SYS_PTRACE_MACROS_H
#define LLVM_LIBC_MACROS_SYS_PTRACE_MACROS_H
-#define PTRACE_TRACEME 0
+#include <linux/ptrace.h>
+
+// Userspace expects a different name for these two
+#define PTRACE_POKEUSER PTRACE_POKEUSR
+#define PTRACE_PEEKUSER PTRACE_PEEKUSR
+
+// For BSD compatibility
#define PT_TRACE_ME PTRACE_TRACEME
+#define PT_READ_I PTRACE_PEEKTEXT
+#define PT_READ_D PTRACE_PEEKDATA
+#define PT_WRITE_I PTRACE_POKETEXT
+#define PT_WRITE_D PTRACE_POKEDATA
+#define PT_CONTINUE PTRACE_CONT
+#define PT_STEP PTRACE_SINGLESTEP
+#define PT_KILL PTRACE_KILL
+#define PT_ATTACH PTRACE_ATTACH
+#define PT_DETACH PTRACE_DETACH
+#define PT_SYSCALL PTRACE_SYSCALL
#endif // LLVM_LIBC_MACROS_SYS_PTRACE_MACROS_H
diff --git a/libc/include/sys/ptrace.yaml b/libc/include/sys/ptrace.yaml
index b45ea0ec7f6c9..8c546fb17e481 100644
--- a/libc/include/sys/ptrace.yaml
+++ b/libc/include/sys/ptrace.yaml
@@ -2,10 +2,32 @@ header: sys/ptrace.h
standards:
- linux
macros:
- - macro_name: PTRACE_TRACEME
- macro_header: sys-ptrace-macros.h
- macro_name: PT_TRACE_ME
macro_header: sys-ptrace-macros.h
+ - macro_name: PTRACE_POKEUSER
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PTRACE_PEEKUSER
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_READ_I
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_READ_D
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_WRITE_I
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_WRITE_D
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_CONTINUE
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_STEP
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_KILL
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_ATTACH
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_DETACH
+ macro_header: sys-ptrace-macros.h
+ - macro_name: PT_SYSCALL
+ macro_header: sys-ptrace-macros.h
types: []
enums: []
objects: []
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index c82c0a0d0393d..4fc8c4eb3b67a 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -352,6 +352,7 @@ add_header_library(
HDRS
ptrace.h
DEPENDS
+ libc.hdr.types.pid_t
libc.src.__support.OSUtil.osutil
libc.src.__support.common
libc.src.__support.error_or
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/ptrace.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/ptrace.h
index db3328119e058..f7616f3537150 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/ptrace.h
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/ptrace.h
@@ -14,6 +14,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_PTRACE_H
#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_PTRACE_H
+#include "hdr/types/pid_t.h"
#include "src/__support/OSUtil/linux/syscall.h" // For syscall_checked
#include "src/__support/error_or.h"
#include "src/__support/macros/config.h"
@@ -22,8 +23,11 @@
namespace LIBC_NAMESPACE_DECL {
namespace linux_syscalls {
-LIBC_INLINE ErrorOr<long> ptrace(int request) {
- return syscall_checked<long>(SYS_ptrace, request);
+// NB: This function follows the kernel ABI, which means the result of
+// PTRACE_PEEK requests is returned through the fourth argument.
+LIBC_INLINE ErrorOr<long> ptrace(int request, pid_t pid, void *addr,
+ void *data) {
+ return syscall_checked<long>(SYS_ptrace, request, pid, addr, data);
}
} // namespace linux_syscalls
diff --git a/libc/src/sys/ptrace/linux/CMakeLists.txt b/libc/src/sys/ptrace/linux/CMakeLists.txt
index 927c4a93102e5..c8918f9a3145e 100644
--- a/libc/src/sys/ptrace/linux/CMakeLists.txt
+++ b/libc/src/sys/ptrace/linux/CMakeLists.txt
@@ -5,7 +5,11 @@ add_entrypoint_object(
HDRS
../ptrace.h
DEPENDS
+ libc.hdr.types.pid_t
+ libc.hdr.sys_ptrace_macros
libc.include.sys_ptrace
libc.src.__support.OSUtil.linux.syscall_wrappers.ptrace
- libc.src.errno.errno
+ libc.src.__support.error_or
+ libc.src.__support.libc_errno
+ libc.src.__support.macros.config
)
diff --git a/libc/src/sys/ptrace/linux/ptrace.cpp b/libc/src/sys/ptrace/linux/ptrace.cpp
index 5675ebacddee2..9cc82e0d9ec4f 100644
--- a/libc/src/sys/ptrace/linux/ptrace.cpp
+++ b/libc/src/sys/ptrace/linux/ptrace.cpp
@@ -13,22 +13,38 @@
#include "src/sys/ptrace/ptrace.h"
+#include "hdr/sys_ptrace_macros.h"
+#include "hdr/types/pid_t.h"
#include "src/__support/OSUtil/linux/syscall_wrappers/ptrace.h"
+#include "src/__support/error_or.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
+#include <stdarg.h>
namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(long, ptrace, (int request, ...)) {
- // TODO: Handle request arguments.
- auto result = linux_syscalls::ptrace(request);
+ // Unlike the C interface, the kernel returns the peek result through the
+ // fourth argument. This means the user needs to clear errno to distinguish
+ // failure from a legitimate "-1" return.
+ bool is_peek = request == PTRACE_PEEKTEXT || request == PTRACE_PEEKDATA ||
+ request == PTRACE_PEEKUSER;
+ long peek_result;
+ va_list args;
+ va_start(args, request);
+ pid_t pid = va_arg(args, pid_t);
+ void *addr = va_arg(args, void *);
+ void *data = is_peek ? &peek_result : va_arg(args, void *);
+ va_end(args);
+
+ ErrorOr<long> result = linux_syscalls::ptrace(request, pid, addr, data);
if (!result.has_value()) {
libc_errno = result.error();
return -1;
}
- return result.value();
+ return is_peek ? peek_result : result.value();
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/integration/src/sys/ptrace/linux/ptrace_test.cpp b/libc/test/integration/src/sys/ptrace/linux/ptrace_test.cpp
index 542c0a6936e82..a41b6efca25fe 100644
--- a/libc/test/integration/src/sys/ptrace/linux/ptrace_test.cpp
+++ b/libc/test/integration/src/sys/ptrace/linux/ptrace_test.cpp
@@ -22,17 +22,20 @@
#include <errno.h>
#include <sys/ptrace.h>
-void trace_me_test() {
+void basic_ptrace_test() {
+ // This variable will be read and written to via ptrace.
+ volatile long test_variable = 0;
pid_t pid = LIBC_NAMESPACE::fork();
ASSERT_TRUE(pid >= 0);
if (pid == 0) {
// Child process
+ test_variable = 0xdeadbeef;
long ret = LIBC_NAMESPACE::ptrace(PTRACE_TRACEME, 0, nullptr, nullptr);
if (ret != 0)
LIBC_NAMESPACE::internal::exit(errno);
LIBC_NAMESPACE::raise(SIGSTOP);
- LIBC_NAMESPACE::internal::exit(0);
+ LIBC_NAMESPACE::internal::exit(static_cast<int>(test_variable));
}
// Parent process
@@ -42,12 +45,33 @@ void trace_me_test() {
ASSERT_TRUE(WIFSTOPPED(status));
ASSERT_EQ(WSTOPSIG(status), SIGSTOP);
- // Kill the child
- ASSERT_EQ(LIBC_NAMESPACE::kill(pid, SIGKILL), 0);
+ // Try reading from an invalid address, and check for failure.
+ ASSERT_EQ(-1,
+ LIBC_NAMESPACE::ptrace(PTRACE_PEEKDATA, pid,
+ reinterpret_cast<void *>(0x470), nullptr));
+ ASSERT_ERRNO_EQ(EIO);
+
+ errno = 0;
+ // We're reading the value of test_variable from the child process. As the
+ // child is a fork our ourselves, the variable will have the same address in
+ // both processes.
+ long value =
+ LIBC_NAMESPACE::ptrace(PTRACE_PEEKDATA, pid, &test_variable, nullptr);
+ ASSERT_EQ(value, 0xdeadbeef);
+ ASSERT_ERRNO_SUCCESS();
+
+ // Now modify the variable
+ ASSERT_EQ(0,
+ LIBC_NAMESPACE::ptrace(PTRACE_POKEDATA, pid, &test_variable, 47));
+ ASSERT_ERRNO_SUCCESS();
+
+ // Resume the child, it should read back the modified value.
+ ASSERT_EQ(0, LIBC_NAMESPACE::ptrace(PTRACE_CONT, pid, nullptr, nullptr));
+ ASSERT_ERRNO_SUCCESS();
wait_ret = LIBC_NAMESPACE::waitpid(pid, &status, 0);
ASSERT_EQ(wait_ret, pid);
- ASSERT_TRUE(WIFSIGNALED(status));
- ASSERT_EQ(WTERMSIG(status), SIGKILL);
+ ASSERT_TRUE(WIFEXITED(status));
+ ASSERT_EQ(WEXITSTATUS(status), 47);
}
void errno_test() {
@@ -57,7 +81,7 @@ void errno_test() {
}
TEST_MAIN() {
- trace_me_test();
+ basic_ptrace_test();
errno_test();
return 0;
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/201830
More information about the libc-commits
mailing list