Skip to content

Commit 5a2a046

Browse files
authored
GH-108390: Prevent non-local events being set with sys.monitoring.set_local_events() (GH-108420)
1 parent 04a0830 commit 5a2a046

File tree

7 files changed

+143
-74
lines changed

7 files changed

+143
-74
lines changed

Include/cpython/code.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,21 @@
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+
typedef struct _Py_LocalMonitors {
20+
uint8_t tools[_PY_MONITORING_LOCAL_EVENTS];
21+
} _Py_LocalMonitors;
22+
23+
typedef struct _Py_GlobalMonitors {
1924
uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
20-
} _Py_Monitors;
25+
} _Py_GlobalMonitors;
2126

2227
/* Each instruction in a code object is a fixed-width value,
2328
* currently 2 bytes: 1-byte opcode + 1-byte oparg. The EXTENDED_ARG
@@ -88,9 +93,9 @@ typedef struct {
8893
*/
8994
typedef struct {
9095
/* Monitoring specific to this code object */
91-
_Py_Monitors local_monitors;
96+
_Py_LocalMonitors local_monitors;
9297
/* Monitoring that is active on this code object */
93-
_Py_Monitors active_monitors;
98+
_Py_LocalMonitors active_monitors;
9499
/* The tools that are to be notified for events for the matching code unit */
95100
uint8_t *tools;
96101
/* 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
@@ -187,7 +187,7 @@ struct _is {
187187
uint16_t optimizer_resume_threshold;
188188
uint16_t optimizer_backedge_threshold;
189189

190-
_Py_Monitors monitors;
190+
_Py_GlobalMonitors monitors;
191191
bool f_opcode_trace_set;
192192
bool sys_profile_initialized;
193193
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
@@ -1978,28 +1978,30 @@ do_monitor_exc(PyThreadState *tstate, _PyInterpreterFrame *frame,
19781978
return err;
19791979
}
19801980

1981-
static inline int
1982-
no_tools_for_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
1981+
static inline bool
1982+
no_tools_for_global_event(PyThreadState *tstate, int event)
19831983
{
1984+
return tstate->interp->monitors.tools[event] == 0;
1985+
}
1986+
1987+
static inline bool
1988+
no_tools_for_local_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
1989+
{
1990+
assert(event < _PY_MONITORING_LOCAL_EVENTS);
19841991
_PyCoMonitoringData *data = _PyFrame_GetCode(frame)->_co_monitoring;
19851992
if (data) {
1986-
if (data->active_monitors.tools[event] == 0) {
1987-
return 1;
1988-
}
1993+
return data->active_monitors.tools[event] == 0;
19891994
}
19901995
else {
1991-
if (tstate->interp->monitors.tools[event] == 0) {
1992-
return 1;
1993-
}
1996+
return no_tools_for_global_event(tstate, event);
19941997
}
1995-
return 0;
19961998
}
19971999

19982000
static void
19992001
monitor_raise(PyThreadState *tstate, _PyInterpreterFrame *frame,
20002002
_Py_CODEUNIT *instr)
20012003
{
2002-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RAISE)) {
2004+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RAISE)) {
20032005
return;
20042006
}
20052007
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE);
@@ -2009,7 +2011,7 @@ static void
20092011
monitor_reraise(PyThreadState *tstate, _PyInterpreterFrame *frame,
20102012
_Py_CODEUNIT *instr)
20112013
{
2012-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RERAISE)) {
2014+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RERAISE)) {
20132015
return;
20142016
}
20152017
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RERAISE);
@@ -2019,7 +2021,7 @@ static int
20192021
monitor_stop_iteration(PyThreadState *tstate, _PyInterpreterFrame *frame,
20202022
_Py_CODEUNIT *instr)
20212023
{
2022-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
2024+
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
20232025
return 0;
20242026
}
20252027
return do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_STOP_ITERATION);
@@ -2030,7 +2032,7 @@ monitor_unwind(PyThreadState *tstate,
20302032
_PyInterpreterFrame *frame,
20312033
_Py_CODEUNIT *instr)
20322034
{
2033-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND)) {
2035+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND)) {
20342036
return;
20352037
}
20362038
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND);
@@ -2042,7 +2044,7 @@ monitor_handled(PyThreadState *tstate,
20422044
_PyInterpreterFrame *frame,
20432045
_Py_CODEUNIT *instr, PyObject *exc)
20442046
{
2045-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
2047+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
20462048
return 0;
20472049
}
20482050
return _Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc);
@@ -2053,7 +2055,7 @@ monitor_throw(PyThreadState *tstate,
20532055
_PyInterpreterFrame *frame,
20542056
_Py_CODEUNIT *instr)
20552057
{
2056-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_THROW)) {
2058+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_THROW)) {
20572059
return;
20582060
}
20592061
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_THROW);

0 commit comments

Comments
 (0)