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
< 8000 header class="HeaderMktg header-logged-out js-details-container js-header Details f4 py-3" role="banner" data-is-top="true" data-color-mode=light data-light-theme=light data-dark-theme=dark>

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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 == "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