8000 bpo-41180: Replace marshal code.__new__ audit event with marshal.load… · python/cpython@139de04 · GitHub
[go: up one dir, main page]

Skip to content

Commit 139de04

Browse files
authored
bpo-41180: Replace marshal code.__new__ audit event with marshal.load[s] and marshal.dumps (GH-26961)
1 parent 86eeeb4 commit 139de04

File tree

5 files changed

+65
-10
lines changed

5 files changed

+65
-10
lines changed

Doc/library/marshal.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ The module defines these functions:
6666
The *version* argument indicates the data format that ``dump`` should use
6767
(see below).
6868

69+
.. audit-event:: marshal.dumps value,version marshal.dump
70+
6971

7072
.. function:: load(file)
7173

@@ -74,6 +76,8 @@ The module defines these functions:
7476
format), raise :exc:`EOFError`, :exc:`ValueError` or :exc:`TypeError`. The
7577
file must be a readable :term:`binary file`.
7678

79+
.. audit-event:: marshal.loads bytes marshal.load
80+
7781
.. note::
7882

7983
If an object containing an unsupported type was marshalled with :func:`dump`,
@@ -89,13 +93,17 @@ The module defines these functions:
8993
The *version* argument indicates the data format that ``dumps`` should use
9094
(see below).
9195

96+
.. audit-event:: marshal.dumps value,version marshal.dump
97+
9298

9399
.. function:: loads(bytes)
94100

95101
Convert the :term:`bytes-like object` to a value. If no valid value is found, raise
96102
:exc:`EOFError`, :exc:`ValueError` or :exc:`TypeError`. Extra bytes in the
97103
input are ignored.
98104

105+
.. audit-event:: marshal.loads bytes marshal.load
106+
99107

100108
In addition, the following constants are defined:
101109

Lib/test/audit-tests.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77

88
import contextlib
9+
import os
910
import sys
1011

1112

@@ -106,6 +107,32 @@ def test_block_add_hook_baseexception():
106107
pass
107108

108109

110+
def test_marshal():
111+
import marshal
112+
o = ("a", "b", "c", 1, 2, 3)
113+
payload = marshal.dumps(o)
114+
115+
with TestHook() as hook:
116+
assertEqual(o, marshal.loads(marshal.dumps(o)))
117+
118+
try:
119+
with open("test-marshal.bin", "wb") as f:
120+
marshal.dump(o, f)
121+
with open("test-marshal.bin", "rb") as f:
122+
assertEqual(o, marshal.load(f))
123+
finally:
124+
os.unlink("test-marshal.bin")
125+
126+
actual = [(a[0], a[1]) for e, a in hook.seen if e = EDBE = "marshal.dumps"]
127+
assertSequenceEqual(actual, [(o, marshal.version)] * 2)
128+
129+
actual = [a[0] for e, a in hook.seen if e == "marshal.loads"]
130+
assertSequenceEqual(actual, [payload])
131+
132+
actual = [e for e, a in hook.seen if e == "marshal.load"]
133+
assertSequenceEqual(actual, ["marshal.load"])
134+
135+
109136
def test_pickle():
110137
import pickle
111138

Lib/test/test_audit.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ def test_block_add_hook(self):
5454
def test_block_add_hook_baseexception(self):
5555
self.do_test("test_block_add_hook_baseexception")
5656

57+
def test_marshal(self):
58+
import_helper.import_module("marshal")
59+
60+
self.do_test("test_marshal")
61+
5762
def test_pickle(self):
5863
import_helper.import_module("pickle")
5964

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Add auditing events to the :mod:`marshal` module, and stop raising
2+
``code.__init__`` events for every unmarshalled code object. Directly
3+
instantiated code objects will continue to raise an event, and audit event
4+
handlers should inspect or collect the raw marshal data. This reduces a
5+
significant performance overhead when loading from ``.pyc`` files.

Python/marshal.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -596,14 +596,18 @@ PyMarshal_WriteObjectToFile(PyObject *x, FILE *fp, int version)
596596
{
597597
char buf[BUFSIZ];
598598
WFILE wf;
599+
if (PySys_Audit("marshal.dumps", "Oi", x, version) < 0) {
600+
return; /* caller must check PyErr_Occurred() */
601+
}
599602
memset(&wf, 0, sizeof(wf));
600603
wf.fp = fp;
601604
wf.ptr = wf.buf = buf;
602605
wf.end = wf.ptr + sizeof(buf);
603606
wf.error = WFERR_OK;
604607
wf.version = version;
605-
if (w_init_refs(&wf, version))
606-
return; /* caller mush check PyErr_Occurred() */
608+
if (w_init_refs(&wf, version)) {
609+
return; /* caller must check PyErr_Occurred() */
610+
}
607611
w_object(x, &wf);
608612
w_clear_refs(&wf);
609613
w_flush(&wf);
@@ -1368,12 +1372,6 @@ r_object(RFILE *p)
13681372
goto code_error;
13691373

13701374
Py_ssize_t nlocalsplus = PyTuple_GET_SIZE(localsplusnames);
1371-
if (PySys_Audit("code.__new__", "OOOiiiiii",
1372-
code, filename, name, argcount, posonlyargcount,
1373-
kwonlyargcount, nlocalsplus, stacksize,
1374-
flags) < 0) {
1375-
goto code_error;
1376-
}
13771375

13781376
struct _PyCodeConstructor con = {
13791377
.filename = filename,
@@ -1460,6 +1458,15 @@ read_object(RFILE *p)
14601458
fprintf(stderr, "XXX readobject called with exception set\n");
14611459
return NULL;
14621460
}
1461+
if (p->ptr && p->end) {
1462+
if (PySys_Audit("marshal.loads", "y#", p->ptr, (Py_ssize_t)(p->end - p->ptr)) < 0) {
1463+
return NULL;
1464+
}
1465+
} else if (p->fp || p->readable) {
1466+
if (PySys_Audit("marshal.load", NULL) < 0) {
1467+
return NULL;
1468+
}
1469+
}
14631470
v = r_object(p);
14641471
if (v == NULL && !PyErr_Occurred())
14651472
PyErr_SetString(PyExc_TypeError, "NULL object in marshal data for object");
@@ -1556,7 +1563,7 @@ PyMarshal_ReadObjectFromFile(FILE *fp)
15561563
rf.refs = PyList_New(0);
15571564
if (rf.refs == NULL)
15581565
return NULL;
1559-
result = r_object(&rf);
1566+
result = read_object(&rf);
15601567
Py_DECREF(rf.refs);
15611568
if (rf.buf != NULL)
15621569
PyMem_Free(rf.buf);
@@ -1577,7 +1584,7 @@ PyMarshal_ReadObjectFromString(const char *str, Py_ssize_t len)
15771584
rf.refs = PyList_New(0);
15781585
if (rf.refs == NULL)
15791586
return NULL;
1580-
result = r_object(&rf);
1587+
result = read_object(&rf);
15811588
Py_DECREF(rf.refs);
15821589
if (rf.buf != NULL)
15831590
PyMem_Free(rf.buf);
@@ -1589,6 +1596,9 @@ PyMarshal_WriteObjectToString(PyObject *x, int version)
15891596
{
15901597
WFILE wf;
15911598

1599+
if (PySys_Audit("marshal.dumps", "Oi", x, version) < 0) {
1600+
return NULL;
1601+
}
15921602
memset(&wf, 0, sizeof(wf));
15931603
wf.str = PyBytes_FromStringAndSize((char *)NULL, 50);
15941604
if (wf.str == NULL)

0 commit comments

Comments
 (0)
0