@@ -136,19 +136,20 @@ A new structure is added to PyThreadState to support remote debugging:
136136
137137 typedef struct _remote_debugger_support {
138138 int debugger_pending_call;
139- char debugger_script [MAX_SCRIPT_SIZE];
139+ char debugger_script_path [MAX_SCRIPT_SIZE];
140140 } _PyRemoteDebuggerSupport;
141141
142142
143143 This structure is appended to ``PyThreadState ``, adding only a few fields that
144144are **never accessed during normal execution **. The ``debugger_pending_call `` field
145- indicates when a debugger has requested execution, while ``debugger_script ``
146- provides Python code to be executed when the interpreter reaches a safe point.
145+ indicates when a debugger has requested execution, while ``debugger_script_path ``
146+ provides a filesystem path to a Python source file (.py) that will be executed when
147+ the interpreter reaches a safe point. The path must point to a Python source file,
148+ not compiled Python code (.pyc) or any other format.
147149
148150The value for ``MAX_SCRIPT_SIZE `` will be a trade-off between binary size and
149- how big debugging scripts can be. As most of the logic should be in libraries
150- and arbitrary code can be executed with very short amount of Python we are
151- proposing to start with 4kb initially. This value can be extended in the future
151+ how long the path can be. As most paths are relatively short on modern systems,
152+ we are proposing to start with 4kb initially. This value can be extended in the future
152153if we ever need to.
153154
154155
@@ -171,13 +172,13 @@ debugger support:
171172 uint64_t eval_breaker; // Location of the eval breaker flag
172173 uint64_t remote_debugger_support; // Offset to our support structure
173174 uint64_t debugger_pending_call; // Where to write the pending flag
174- uint64_t debugger_script; // Where to write the script path
175+ uint64_t debugger_script_path; // Where to write the script path
175176 } debugger_support;
176177
177178 These offsets allow debuggers to locate critical debugging control structures in
178179the target process's memory space. The ``eval_breaker `` and ``remote_debugger_support ``
179180offsets are relative to each ``PyThreadState ``, while the ``debugger_pending_call ``
180- and ``debugger_script `` offsets are relative to each ``_PyRemoteDebuggerSupport ``
181+ and ``debugger_script_path `` offsets are relative to each ``_PyRemoteDebuggerSupport ``
181182structure, allowing the new structure and its fields to be found regardless of
182183where they are in memory.
183184
@@ -199,13 +200,19 @@ When a debugger wants to attach to a Python process, it follows these steps:
199200
2002015. Write control information:
201202
202- - Write a filename containing Python code to be executed into the
203- ``debugger_script `` field in ``_PyRemoteDebuggerSupport ``.
203+ - Most debuggers will pause the process before writing to its memory. This is
204+ standard practice for tools like GDB, which use SIGSTOP or ptrace to pause the process.
205+ This approach prevents races when writing to process memory. Profilers and other tools
206+ that don't wish to stop the process can still use this interface, but they need to
207+ handle possible races, which is a normal consideration for profilers in general.
208+
209+ - Write a file path to a Python source file (.py) into the
210+ ``debugger_script_path `` field in ``_PyRemoteDebuggerSupport ``.
204211 - Set ``debugger_pending_call `` flag in ``_PyRemoteDebuggerSupport ``
205212 - Set ``_PY_EVAL_PLEASE_STOP_BIT `` in the ``eval_breaker `` field
206213
207- Once the interpreter reaches the next safe point, it will execute the script
208- provided by the debugger.
214+ Once the interpreter reaches the next safe point, it will execute the Python code
215+ contained in the file specified by the debugger.
209216
210217Interpreter Integration
211218-----------------------
@@ -233,7 +240,7 @@ to be audited or disabled if desired by a system's administrator.
233240 if (tstate->eval_breaker) {
234241 if (tstate->remote_debugger_support.debugger_pending_call) {
235242 tstate->remote_debugger_support.debugger_pending_call = 0;
236- const char *path = tstate->remote_debugger_support.debugger_script ;
243+ const char *path = tstate->remote_debugger_support.debugger_script_path ;
237244 if (*path) {
238245 if (0 != PySys_Audit("debugger_script", "%s", path)) {
239246 PyErr_Clear();
@@ -269,16 +276,17 @@ arbitrary Python code within the context of a specified Python process:
269276
270277.. code-block :: python
271278
272- def remote_exec (pid : int , code : str , timeout : int = 0 ) -> None :
279+ def remote_exec (pid : int , code : str ) -> None :
273280 """
274281 Executes a block of Python code in a given remote Python process.
275282
283+ This function returns immediately, and the code will be executed at the next
284+ available opportunity in the target process, similar to how signals are handled.
285+ There is no way to determine when or if the code has been executed.
286+
276287 Args:
277288 pid (int): The process ID of the target Python process.
278289 code (str): A string containing the Python code to be executed.
279- timeout (int): An optional timeout for waiting for the remote
280- process to execute the code. If the timeout is exceeded a
281- ``TimeoutError`` will be raised.
282290 """
283291
284292 An example usage of the API would look like:
@@ -288,9 +296,7 @@ An example usage of the API would look like:
288296 import sys
289297 # Execute a print statement in a remote Python process with PID 12345
290298 try :
291- sys.remote_exec(12345 , " print('Hello from remote execution!')" , timeout = 3 )
292- except TimeoutError :
293- print (f " The remote process took too long to execute the code " )
299+ sys.remote_exec(12345 , " print('Hello from remote execution!')" )
294300 except Exception as e:
295301 print (f " Failed to execute code: { e} " )
296302
@@ -393,8 +399,8 @@ Rejected Ideas
393399Writing Python code into the buffer
394400-----------------------------------
395401
396- We have chosen to have debuggers write the code to be executed into a file
397- whose path is written into a buffer in the remote process. This has been deemed
402+ We have chosen to have debuggers write the path to a file containing Python code
403+ into a buffer in the remote process. This has been deemed
398404more secure than writing the Python code to be executed itself into a buffer in
399405the remote process, because it means that an attacker who has gained arbitrary
400406writes in a process but not arbitrary code execution or file system
0 commit comments