mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-135543: Emit sys.remote_exec audit event when sys.remote_exec is called (GH-135544)
(cherry picked from commit 1ddfe59320
)
Co-authored-by: Nadeshiko Manju <me@manjusaka.me>
This commit is contained in:
parent
83e0ab17f6
commit
0370cb42da
8 changed files with 79 additions and 20 deletions
|
@ -1933,12 +1933,21 @@ always available. Unless explicitly noted otherwise, all variables are read-only
|
|||
interpreter is pre-release (alpha, beta, or release candidate) then the
|
||||
local and remote interpreters must be the same exact version.
|
||||
|
||||
.. audit-event:: remote_debugger_script script_path
|
||||
.. audit-event:: sys.remote_exec pid script_path
|
||||
|
||||
When the code is executed in the remote process, an
|
||||
:ref:`auditing event <auditing>` ``sys.remote_exec`` is raised with
|
||||
the *pid* and the path to the script file.
|
||||
This event is raised in the process that called :func:`sys.remote_exec`.
|
||||
|
||||
.. audit-event:: cpython.remote_debugger_script script_path
|
||||
|
||||
When the script is executed in the remote process, an
|
||||
:ref:`auditing event <auditing>`
|
||||
``sys.remote_debugger_script`` is raised
|
||||
``cpython.remote_debugger_script`` is raised
|
||||
with the path in the remote process.
|
||||
This event is raised in the remote process, not the one
|
||||
that called :func:`sys.remote_exec`.
|
||||
|
||||
.. availability:: Unix, Windows.
|
||||
.. versionadded:: 3.14
|
||||
|
|
|
@ -643,6 +643,34 @@ def test_assert_unicode():
|
|||
else:
|
||||
raise RuntimeError("Expected sys.audit(9) to fail.")
|
||||
|
||||
def test_sys_remote_exec():
|
||||
import tempfile
|
||||
|
||||
pid = os.getpid()
|
||||
event_pid = -1
|
||||
event_script_path = ""
|
||||
remote_event_script_path = ""
|
||||
def hook(event, args):
|
||||
if event not in ["sys.remote_exec", "cpython.remote_debugger_script"]:
|
||||
return
|
||||
print(event, args)
|
||||
match event:
|
||||
case "sys.remote_exec":
|
||||
nonlocal event_pid, event_script_path
|
||||
event_pid = args[0]
|
||||
event_script_path = args[1]
|
||||
case "cpython.remote_debugger_script":
|
||||
nonlocal remote_event_script_path
|
||||
remote_event_script_path = args[0]
|
||||
|
||||
sys.addaudithook(hook)
|
||||
with tempfile.NamedTemporaryFile(mode='w+', delete=True) as tmp_file:
|
||||
tmp_file.write("a = 1+1\n")
|
||||
tmp_file.flush()
|
||||
sys.remote_exec(pid, tmp_file.name)
|
||||
assertEqual(event_pid, pid)
|
||||
assertEqual(event_script_path, tmp_file.name)
|
||||
assertEqual(remote_event_script_path, tmp_file.name)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from test.support import suppress_msvcrt_asserts
|
||||
|
|
|
@ -46,6 +46,7 @@ __all__ = [
|
|||
# sys
|
||||
"MS_WINDOWS", "is_jython", "is_android", "is_emscripten", "is_wasi",
|
||||
"is_apple_mobile", "check_impl_detail", "unix_shell", "setswitchinterval",
|
||||
"support_remote_exec_only",
|
||||
# os
|
||||
"get_pagesize",
|
||||
# network
|
||||
|
@ -3070,6 +3071,27 @@ def is_libssl_fips_mode():
|
|||
return False # more of a maybe, unless we add this to the _ssl module.
|
||||
return get_fips_mode() != 0
|
||||
|
||||
def _supports_remote_attaching():
|
||||
PROCESS_VM_READV_SUPPORTED = False
|
||||
|
||||
try:
|
||||
from _remote_debugging import PROCESS_VM_READV_SUPPORTED
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
return PROCESS_VM_READV_SUPPORTED
|
||||
|
||||
def _support_remote_exec_only_impl():
|
||||
if not sys.is_remote_debug_enabled():
|
||||
return unittest.skip("Remote debugging is not enabled")
|
||||
if sys.platform not in ("darwin", "linux", "win32"):
|
||||
return unittest.skip("Test only runs on Linux, Windows and macOS")
|
||||
if sys.platform == "linux" and not _supports_remote_attaching():
|
||||
return unittest.skip("Test only runs on Linux with process_vm_readv support")
|
||||
return _id
|
||||
|
||||
def support_remote_exec_only(test):
|
||||
return _support_remote_exec_only_impl()(test)
|
||||
|
||||
class EqualToForwardRef:
|
||||
"""Helper to ease use of annotationlib.ForwardRef in tests.
|
||||
|
|
|
@ -322,6 +322,14 @@ class AuditTest(unittest.TestCase):
|
|||
if returncode:
|
||||
self.fail(stderr)
|
||||
|
||||
@support.support_remote_exec_only
|
||||
@support.cpython_only
|
||||
def test_sys_remote_exec(self):
|
||||
returncode, events, stderr = self.run_python("test_sys_remote_exec")
|
||||
self.assertTrue(any(["sys.remote_exec" in event for event in events]))
|
||||
self.assertTrue(any(["cpython.remote_debugger_script" in event for event in events]))
|
||||
if returncode:
|
||||
self.fail(stderr)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
@ -1942,22 +1942,7 @@ class SizeofTest(unittest.TestCase):
|
|||
self.assertEqual(out, b"")
|
||||
self.assertEqual(err, b"")
|
||||
|
||||
|
||||
def _supports_remote_attaching():
|
||||
PROCESS_VM_READV_SUPPORTED = False
|
||||
|
||||
try:
|
||||
from _remote_debugging import PROCESS_VM_READV_SUPPORTED
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
return PROCESS_VM_READV_SUPPORTED
|
||||
|
||||
@unittest.skipIf(not sys.is_remote_debug_enabled(), "Remote debugging is not enabled")
|
||||
@unittest.skipIf(sys.platform != "darwin" and sys.platform != "linux" and sys.platform != "win32",
|
||||
"Test only runs on Linux, Windows and MacOS")
|
||||
@unittest.skipIf(sys.platform == "linux" and not _supports_remote_attaching(),
|
||||
"Test only runs on Linux with process_vm_readv support")
|
||||
@test.support.support_remote_exec_only
|
||||
@test.support.cpython_only
|
||||
class TestRemoteExec(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
|
@ -2116,7 +2101,7 @@ print("Remote script executed successfully!")
|
|||
returncode, stdout, stderr = self._run_remote_exec_test(script, prologue=prologue)
|
||||
self.assertEqual(returncode, 0)
|
||||
self.assertIn(b"Remote script executed successfully!", stdout)
|
||||
self.assertIn(b"Audit event: remote_debugger_script, arg: ", stdout)
|
||||
self.assertIn(b"Audit event: cpython.remote_debugger_script, arg: ", stdout)
|
||||
self.assertEqual(stderr, b"")
|
||||
|
||||
def test_remote_exec_with_exception(self):
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Emit ``sys.remote_exec`` audit event when :func:`sys.remote_exec` is called
|
||||
and migrate ``remote_debugger_script`` to ``cpython.remote_debugger_script``.
|
|
@ -1220,7 +1220,7 @@ static inline int run_remote_debugger_source(PyObject *source)
|
|||
// that would be an easy target for a ROP gadget.
|
||||
static inline void run_remote_debugger_script(PyObject *path)
|
||||
{
|
||||
if (0 != PySys_Audit("remote_debugger_script", "O", path)) {
|
||||
if (0 != PySys_Audit("cpython.remote_debugger_script", "O", path)) {
|
||||
PyErr_FormatUnraisable(
|
||||
"Audit hook failed for remote debugger script %U", path);
|
||||
return;
|
||||
|
|
|
@ -2487,6 +2487,11 @@ sys_remote_exec_impl(PyObject *module, int pid, PyObject *script)
|
|||
if (PyUnicode_FSConverter(script, &path) == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (PySys_Audit("sys.remote_exec", "iO", pid, script) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
debugger_script_path = PyBytes_AS_STRING(path);
|
||||
#ifdef MS_WINDOWS
|
||||
PyObject *unicode_path;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue