Skip to content

Commit 7497971

Browse files
authored
PEP 768: Add some minor changes to the APIs and some clarifications (#4162)
1 parent e4f3216 commit 7497971

File tree

1 file changed

+62
-20
lines changed

1 file changed

+62
-20
lines changed

peps/pep-0768.rst

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ are **never accessed during normal execution**. The ``debugger_pending_call`` fi
143143
indicates when a debugger has requested execution, while ``debugger_script``
144144
provides Python code to be executed when the interpreter reaches a safe point.
145145

146+
The value for ``MAX_SCRIPT_SIZE`` will be a trade-off between binary size and
147+
how big debugging scripts can be. As most of the logic should be in libraries
148+
and arbitrary code can be executed with very short ammount of Python we are
149+
proposing to start with 4kb initially. This value can be extended in the future
150+
if we ever need to.
151+
146152

147153
Debug Offsets Table
148154
-------------------
@@ -191,7 +197,8 @@ When a debugger wants to attach to a Python process, it follows these steps:
191197

192198
5. Write control information:
193199

194-
- Write python code to be executed into the ``debugger_script`` field in ``_PyRemoteDebuggerSupport``
200+
- Write a string of Python code to be executed into the ``debugger_script``
201+
field in ``_PyRemoteDebuggerSupport``.
195202
- Set ``debugger_pending_call`` flag in ``_PyRemoteDebuggerSupport``
196203
- Set ``_PY_EVAL_PLEASE_STOP_BIT`` in the ``eval_breaker`` field
197204

@@ -232,6 +239,11 @@ is checked.
232239
}
233240
234241
242+
If the code being executed raises any Python exception it will be processed as
243+
an `unraisable exception
244+
<https://docs.python.org/3/c-api/exceptions.html#c.PyErr_WriteUnraisable>`__ in
245+
the thread where the code was executed.
246+
235247
Python API
236248
----------
237249

@@ -242,13 +254,16 @@ arbitrary Python code within the context of a specified Python process:
242254

243255
.. code-block:: python
244256
245-
def remote_exec(pid: int, code: str) -> None:
257+
def remote_exec(pid: int, code: str, timeout: int = 0) -> None:
246258
"""
247259
Executes a block of Python code in a given remote Python process.
248260
249261
Args:
250262
pid (int): The process ID of the target Python process.
251263
code (str): A string containing the Python code to be executed.
264+
timeout (int): An optional timeout for waiting for the remote
265+
process to execute the code. If the timeout is exceeded a
266+
``TimeoutError`` will be raised.
252267
"""
253268
254269
An example usage of the API would look like:
@@ -258,7 +273,9 @@ An example usage of the API would look like:
258273
import sys
259274
# Execute a print statement in a remote Python process with PID 12345
260275
try:
261-
sys.remote_exec(12345, "print('Hello from remote execution!')")
276+
sys.remote_exec(12345, "print('Hello from remote execution!')", timeout=3)
277+
except TimeoutError:
278+
print(f"The remote process took too long to execute the code")
262279
except Exception as e:
263280
print(f"Failed to execute code: {e}")
264281
@@ -270,7 +287,6 @@ This change has no impact on existing Python code or interpreter performance.
270287
The added fields are only accessed during debugger attachment, and the checking
271288
mechanism piggybacks on existing interpreter safe points.
272289

273-
274290
Security Implications
275291
=====================
276292

@@ -280,23 +296,26 @@ the PEP doesn't specify how memory should be written to the target process, in p
280296
this will be done using standard system calls that are already being used by other
281297
debuggers and tools. Some examples are:
282298

283-
* On Linux, the ``process_vm_readv()`` and ``process_vm_writev()`` system calls
299+
* On Linux, the `process_vm_readv() <https://man7.org/linux/man-pages/man2/process_vm_readv.2.html>`__
300+
and `process_vm_writev() <https://man7.org/linux/man-pages/man2/process_vm_writev.2.html>`__ system calls
284301
are used to read and write memory from another process. These operations are
285-
controlled by ptrace access mode checks - the same ones that govern debugger
286-
attachment. A process can only read from or write to another process's memory
287-
if it has the appropriate permissions (typically requiring either root or the
288-
``CAP_SYS_PTRACE`` capability, though less security minded distributions may
289-
allow any process running as the same uid to attach).
290-
291-
* On macOS, the interface would leverage ``mach_vm_read_overwrite()`` and
292-
``mach_vm_write()`` through the Mach task system. These operations require
302+
controlled by `ptrace <https://man7.org/linux/man-pages/man2/ptrace.2.html>`__ access mode
303+
checks - the same ones that govern debugger attachment. A process can only read from
304+
or write to another process's memory if it has the appropriate permissions (typically
305+
requiring either root or the `CAP_SYS_PTRACE <https://man7.org/linux/man-pages/man7/capabilities.7.html>`__
306+
capability, though less security minded distributions may allow any process running as the same uid to attach).
307+
308+
* On macOS, the interface would leverage `mach_vm_read_overwrite() <https://developer.apple.com/documentation/kernel/1402127-mach_vm_read_overwrite>`__ and
309+
`mach_vm_write() <https://developer.apple.com/documentation/kernel/1402070-mach_vm_write>`__ through the Mach task system. These operations require
293310
``task_for_pid()`` access, which is strictly controlled by the operating
294311
system. By default, access is limited to processes running as root or those
295312
with specific entitlements granted by Apple's security framework.
296313

297-
* On Windows, the ``ReadProcessMemory()`` and ``WriteProcessMemory()`` functions
314+
* On Windows, the `ReadProcessMemory() <https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-readprocessmemory>`__
315+
and `WriteProcessMemory() <https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory>`__ functions
298316
provide similar functionality. Access is controlled through the Windows
299-
security model - a process needs ``PROCESS_VM_READ`` and ``PROCESS_VM_WRITE``
317+
security model - a process needs `PROCESS_VM_READ <https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights>`__
318+
and `PROCESS_VM_WRITE <https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights>`__
300319
permissions, which typically require the same user context or appropriate
301320
privileges. These are the same permissions required by debuggers, ensuring
302321
consistent security semantics across platforms.
@@ -310,7 +329,7 @@ All mechanisms ensure that:
310329
The memory operations themselves are well-established and have been used safely
311330
for decades in tools like GDB, LLDB, and various system profilers.
312331

313-
Its important to note that any attempt to attach to a Python process via this
332+
It's important to note that any attempt to attach to a Python process via this
314333
mechanism would be detectable by system-level monitoring tools. This
315334
transparency provides an additional layer of accountability, allowing
316335
administrators to audit debugging operations in sensitive environments.
@@ -319,12 +338,12 @@ Further, the strict reliance on OS-level security controls ensures that existing
319338
system policies remain effective. For enterprise environments, this means
320339
administrators can continue to enforce debugging restrictions using standard
321340
tools and policies without requiring additional configuration. For instance,
322-
leveraging Linux’s ``ptrace_scope`` or macOS’s ``taskgated`` to restrict
323-
debugger access will equally govern the proposed interface.
341+
leveraging Linux's `ptrace_scope <https://www.kernel.org/doc/Documentation/security/Yama.txt>`__
342+
or macOS's ``taskgated`` to restrict debugger access will equally govern the
343+
proposed interface.
324344

325345
By maintaining compatibility with existing security frameworks, this design
326346
ensures that adopting the new interface requires no changes to established
327-
security practices, thereby minimizing barriers to adoption.
328347

329348
How to Teach This
330349
=================
@@ -341,7 +360,30 @@ debugging tool stability and reliability.
341360
Reference Implementation
342361
========================
343362

344-
https://github.com/pablogsal/cpython/commits/remote_pdb/
363+
A reference implementation with a prototype adding remote support for ``pdb``
364+
can be found `here
365+
<https://github.com/pablogsal/cpython/compare/60ff67d010078eca15a74b1429caf779ac4f9c74...remote_pdb>`__.
366+
367+
Rejected Ideas
368+
==============
369+
370+
Using a path as the debugger input
371+
----------------------------------
372+
373+
We have selected that the mechanism for executing remote code is that tools
374+
write the code directly in the remote process to eliminate a possible security
375+
vulnerability in which the file to be executed can be altered by parties other
376+
than the debugger process if permissions are not set correctly or filesystem
377+
configurations allow for this to happen. It is also trivial to write code that
378+
executes the contents of a file so the current mechanism doesn't disallow tools
379+
that want to just execute files to just do so if they are ok with the security
380+
profile of such operation.
381+
382+
Thanks
383+
======
384+
385+
We would like to thank Carl Friedrich Bolz-Tereick for his insightful comments and suggestions
386+
when discussing this proposal.
345387

346388

347389
Copyright

0 commit comments

Comments
 (0)