@@ -141,8 +141,10 @@ A new structure is added to PyThreadState to support remote debugging:
141141
142142 This structure is appended to ``PyThreadState ``, adding only a few fields that
143143are **never accessed during normal execution **. The ``debugger_pending_call `` field
144- indicates when a debugger has requested execution, while ``debugger_script ``
145- provides Python code to be executed when the interpreter reaches a safe point.
144+ indicates when a debugger has requested execution, while ``debugger_script_path ``
145+ provides a filesystem path to a Python source file (.py) that will be executed when
146+ the interpreter reaches a safe point. The path must point to a Python source file,
147+ not compiled Python code (.pyc) or any other format.
146148
147149The value for ``MAX_SCRIPT_PATH_SIZE `` will be a trade-off between binary size
148150and how big debugging scripts' paths can be. To limit the memory overhead per
@@ -177,7 +179,7 @@ debugger support:
177179 These offsets allow debuggers to locate critical debugging control structures in
178180the target process's memory space. The ``eval_breaker `` and ``remote_debugger_support ``
179181offsets are relative to each ``PyThreadState ``, while the ``debugger_pending_call ``
180- and ``debugger_script `` offsets are relative to each ``_PyRemoteDebuggerSupport ``
182+ and ``debugger_script_path `` offsets are relative to each ``_PyRemoteDebuggerSupport ``
181183structure, allowing the new structure and its fields to be found regardless of
182184where they are in memory. ``debugger_script_path_size `` informs the attaching
183185tool of the size of the buffer.
@@ -200,13 +202,20 @@ When a debugger wants to attach to a Python process, it follows these steps:
200202
2012035. Write control information:
202204
203- - Write a filename containing Python code to be executed into the
204- ``debugger_script `` field in ``_PyRemoteDebuggerSupport ``.
205+ - Most debuggers will pause the process before writing to its memory. This is
206+ standard practice for tools like GDB, which use SIGSTOP or ptrace to pause the process.
207+ This approach prevents races when writing to process memory. Profilers and other tools
208+ that don't wish to stop the process can still use this interface, but they need to
209+ handle possible races, which is a normal consideration for profilers in general.
210+
211+ - Write a file path to a Python source file (.py) into the
212+ ``debugger_script_path `` field in ``_PyRemoteDebuggerSupport ``.
205213 - Set ``debugger_pending_call `` flag in ``_PyRemoteDebuggerSupport `` to 1
214+ - Set ``debugger_pending_call `` flag in ``_PyRemoteDebuggerSupport ``
206215 - Set ``_PY_EVAL_PLEASE_STOP_BIT `` in the ``eval_breaker `` field
207216
208- Once the interpreter reaches the next safe point, it will execute the script
209- provided by the debugger.
217+ Once the interpreter reaches the next safe point, it will execute the Python code
218+ contained in the file specified by the debugger.
210219
211220Interpreter Integration
212221-----------------------
@@ -237,7 +246,7 @@ to be audited or disabled if desired by a system's administrator.
237246 if (tstate->eval_breaker) {
238247 if (tstate->remote_debugger_support.debugger_pending_call) {
239248 tstate->remote_debugger_support.debugger_pending_call = 0;
240- const char *path = tstate->remote_debugger_support.debugger_script ;
249+ const char *path = tstate->remote_debugger_support.debugger_script_path ;
241250 if (*path) {
242251 if (0 != PySys_Audit("debugger_script", "%s", path)) {
243252 PyErr_Clear();
@@ -273,16 +282,17 @@ arbitrary Python code within the context of a specified Python process:
273282
274283.. code-block :: python
275284
276- def remote_exec (pid : int , code : str , timeout : int = 0 ) -> None :
285+ def remote_exec (pid : int , code : str ) -> None :
277286 """
278287 Executes a block of Python code in a given remote Python process.
279288
289+ This function returns immediately, and the code will be executed at the next
290+ available opportunity in the target process, similar to how signals are handled.
291+ There is no way to determine when or if the code has been executed.
292+
280293 Args:
281294 pid (int): The process ID of the target Python process.
282295 code (str): A string containing the Python code to be executed.
283- timeout (int): An optional timeout for waiting for the remote
284- process to execute the code. If the timeout is exceeded a
285- ``TimeoutError`` will be raised.
286296 """
287297
288298 An example usage of the API would look like:
@@ -292,9 +302,7 @@ An example usage of the API would look like:
292302 import sys
293303 # Execute a print statement in a remote Python process with PID 12345
294304 try :
295- sys.remote_exec(12345 , " print('Hello from remote execution!')" , timeout = 3 )
296- except TimeoutError :
297- print (f " The remote process took too long to execute the code " )
305+ sys.remote_exec(12345 , " print('Hello from remote execution!')" )
298306 except Exception as e:
299307 print (f " Failed to execute code: { e} " )
300308
@@ -454,8 +462,8 @@ Rejected Ideas
454462Writing Python code into the buffer
455463-----------------------------------
456464
457- We have chosen to have debuggers write the code to be executed into a file
458- whose path is written into a buffer in the remote process. This has been deemed
465+ We have chosen to have debuggers write the path to a file containing Python code
466+ into a buffer in the remote process. This has been deemed
459467more secure than writing the Python code to be executed itself into a buffer in
460468the remote process, because it means that an attacker who has gained arbitrary
461469writes in a process but not arbitrary code execution or file system
0 commit comments