Skip to content

Commit 7ee021f

Browse files
authored
[3.12] GH-108390: Prevent non-local events being set with sys.monitoring.set_local_events() (GH-108420) (#108899)
* GH-108390: Prevent non-local events being set with `sys.monitoring.set_local_events()` (GH-108420) * Restore generated objects * Restore size of monitoring arrays in code object for 3.12 ABI compatibility. * Update ABI file
1 parent 5121faa commit 7ee021f

File tree

8 files changed

+3066
-2966
lines changed

8 files changed

+3066
-2966
lines changed

Doc/data/python3.12.abi

Lines changed: 2908 additions & 2888 deletions
Large diffs are not rendered by default.

Include/cpython/code.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,23 @@
88
extern "C" {
99
#endif
1010

11-
11+
/* Count of all local monitoring events */
12+
#define _PY_MONITORING_LOCAL_EVENTS 10
1213
/* Count of all "real" monitoring events (not derived from other events) */
1314
#define _PY_MONITORING_UNGROUPED_EVENTS 15
1415
/* Count of all monitoring events */
1516
#define _PY_MONITORING_EVENTS 17
1617

17-
/* Table of which tools are active for each monitored event. */
18-
typedef struct _Py_Monitors {
18+
/* Tables of which tools are active for each monitored event. */
19+
/* For 3.12 ABI compatibility this is over sized */
20+
typedef struct _Py_LocalMonitors {
21+
/* Only _PY_MONITORING_LOCAL_EVENTS of these are used */
22+
uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
23+
} _Py_LocalMonitors;
24+
25+
typedef struct _Py_GlobalMonitors {
1926
uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
20-
} _Py_Monitors;
27+
} _Py_GlobalMonitors;
2128

2229
/* Each instruction in a code object is a fixed-width value,
2330
* currently 2 bytes: 1-byte opcode + 1-byte oparg. The EXTENDED_ARG
@@ -81,9 +88,9 @@ typedef struct {
8188
*/
8289
typedef struct {
8390
/* Monitoring specific to this code object */
84-
_Py_Monitors local_monitors;
91+
_Py_LocalMonitors local_monitors;
8592
/* Monitoring that is active on this code object */
86-
_Py_Monitors active_monitors;
93+
_Py_LocalMonitors active_monitors;
8794
/* The tools that are to be notified for events for the matching code unit */
8895
uint8_t *tools;
8996
/* Information to support line events */

Include/internal/pycore_instruments.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extern "C" {
2929
#define PY_MONITORING_EVENT_STOP_ITERATION 9
3030

3131
#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \
32-
((ev) <= PY_MONITORING_EVENT_STOP_ITERATION)
32+
((ev) < _PY_MONITORING_LOCAL_EVENTS)
3333

3434
/* Other events, mainly exceptions */
3535

Include/internal/pycore_interp.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ struct _is {
182182
struct callable_cache callable_cache;
183183
PyCodeObject *interpreter_trampoline;
184184

185-
_Py_Monitors monitors;
185+
_Py_GlobalMonitors monitors;
186186
bool f_opcode_trace_set;
187187
bool sys_profile_initialized;
188188
bool sys_trace_initialized;

Lib/test/test_monitoring.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,9 +1218,11 @@ def test_instruction_then_line(self):
12181218
self.check_events(self.func2,
12191219
recorders = recorders, must_include = self.MUST_INCLUDE_CI)
12201220

1221+
LOCAL_RECORDERS = CallRecorder, LineRecorder, CReturnRecorder, CRaiseRecorder
1222+
12211223
class TestLocalEvents(MonitoringTestBase, unittest.TestCase):
12221224

1223-
def check_events(self, func, expected, tool=TEST_TOOL, recorders=(ExceptionRecorder,)):
1225+
def check_events(self, func, expected, tool=TEST_TOOL, recorders=()):
12241226
try:
12251227
self.assertEqual(sys.monitoring._all_events(), {})
12261228
event_list = []
@@ -1248,7 +1250,7 @@ def func1():
12481250
line2 = 2
12491251
line3 = 3
12501252

1251-
self.check_events(func1, recorders = MANY_RECORDERS, expected = [
1253+
self.check_events(func1, recorders = LOCAL_RECORDERS, expected = [
12521254
('line', 'func1', 1),
12531255
('line', 'func1', 2),
12541256
('line', 'func1', 3)])
@@ -1260,7 +1262,7 @@ def func2():
12601262
[].append(2)
12611263
line3 = 3
12621264

1263-
self.check_events(func2, recorders = MANY_RECORDERS, expected = [
1265+
self.check_events(func2, recorders = LOCAL_RECORDERS, expected = [
12641266
('line', 'func2', 1),
12651267
('line', 'func2', 2),
12661268
('call', 'append', [2]),
@@ -1277,15 +1279,17 @@ def func3():
12771279
line = 5
12781280
line = 6
12791281

1280-
self.check_events(func3, recorders = MANY_RECORDERS, expected = [
1282+
self.check_events(func3, recorders = LOCAL_RECORDERS, expected = [
12811283
('line', 'func3', 1),
12821284
('line', 'func3', 2),
12831285
('line', 'func3', 3),
1284-
('raise', KeyError),
12851286
('line', 'func3', 4),
12861287
('line', 'func3', 5),
12871288
('line', 'func3', 6)])
12881289

1290+
def test_set_non_local_event(self):
1291+
with self.assertRaises(ValueError):
1292+
sys.monitoring.set_local_events(TEST_TOOL, just_call.__code__, E.RAISE)
12891293

12901294
def line_from_offset(code, offset):
12911295
for start, end, line in code.co_lines():
@@ -1698,3 +1702,19 @@ def run():
16981702
self.assertEqual(caught, "inner")
16991703
finally:
17001704
sys.monitoring.set_events(TEST_TOOL, 0)
1705+
1706+
def test_108390(self):
1707+
1708+
class Foo:
1709+
def __init__(self, set_event):
1710+
if set_event:
1711+
sys.monitoring.set_events(TEST_TOOL, E.PY_RESUME)
1712+
1713+
def make_foo_optimized_then_set_event():
1714+
for i in range(100):
1715+
Foo(i == 99)
1716+
1717+
try:
1718+
make_foo_optimized_then_set_event()
1719+
finally:
1720+
sys.monitoring.set_events(TEST_TOOL, 0)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Raise an exception when setting a non-local event (``RAISE``, ``EXCEPTION_HANDLED``,
2+
etc.) in ``sys.monitoring.set_local_events``.
3+
4+
Fixes crash when tracing in recursive calls to Python classes.

Python/ceval.c

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,28 +2021,30 @@ do_monitor_exc(PyThreadState *tstate, _PyInterpreterFrame *frame,
20212021
return err;
20222022
}
20232023

2024-
static inline int
2025-
no_tools_for_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
2024+
static inline bool
2025+
no_tools_for_global_event(PyThreadState *tstate, int event)
20262026
{
2027+
return tstate->interp->monitors.tools[event] == 0;
2028+
}
2029+
2030+
static inline bool
2031+
no_tools_for_local_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
2032+
{
2033+
assert(event < _PY_MONITORING_LOCAL_EVENTS);
20272034
_PyCoMonitoringData *data = frame->f_code->_co_monitoring;
20282035
if (data) {
2029-
if (data->active_monitors.tools[event] == 0) {
2030-
return 1;
2031-
}
2036+
return data->active_monitors.tools[event] == 0;
20322037
}
20332038
else {
2034-
if (tstate->interp->monitors.tools[event] == 0) {
2035-
return 1;
2036-
}
2039+
return no_tools_for_global_event(tstate, event);
20372040
}
2038-
return 0;
20392041
}
20402042

20412043
static void
20422044
monitor_raise(PyThreadState *tstate, _PyInterpreterFrame *frame,
20432045
_Py_CODEUNIT *instr)
20442046
{
2045-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RAISE)) {
2047+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RAISE)) {
20462048
return;
20472049
}
20482050
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE);
@@ -2052,7 +2054,7 @@ static void
20522054
monitor_reraise(PyThreadState *tstate, _PyInterpreterFrame *frame,
20532055
_Py_CODEUNIT *instr)
20542056
{
2055-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RERAISE)) {
2057+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RERAISE)) {
20562058
return;
20572059
}
20582060
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RERAISE);
@@ -2062,7 +2064,7 @@ static int
20622064
monitor_stop_iteration(PyThreadState *tstate, _PyInterpreterFrame *frame,
20632065
_Py_CODEUNIT *instr)
20642066
{
2065-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
2067+
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
20662068
return 0;
20672069
}
20682070
return do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_STOP_ITERATION);
@@ -2073,7 +2075,7 @@ monitor_unwind(PyThreadState *tstate,
20732075
_PyInterpreterFrame *frame,
20742076
_Py_CODEUNIT *instr)
20752077
{
2076-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND)) {
2078+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND)) {
20772079
return;
20782080
}
20792081
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND);
@@ -2085,7 +2087,7 @@ monitor_handled(PyThreadState *tstate,
20852087
_PyInterpreterFrame *frame,
20862088
_Py_CODEUNIT *instr, PyObject *exc)
20872089
{
2088-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
2090+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
20892091
return 0;
20902092
}
20912093
return _Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc);
@@ -2096,7 +2098,7 @@ monitor_throw(PyThreadState *tstate,
20962098
_PyInterpreterFrame *frame,
20972099
_Py_CODEUNIT *instr)
20982100
{
2099-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_THROW)) {
2101+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_THROW)) {
21002102
return;
21012103
}
21022104
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_THROW);

0 commit comments

Comments
 (0)