Skip to content

Commit bee08f5

Browse files
committed
pythonGH-108390: Prevent non-local events being set with sys.monitoring.set_local_events() (pythonGH-108420)
1 parent 3b1a4c1 commit bee08f5

11 files changed

+182
-78
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
@@ -81,9 +86,9 @@ typedef struct {
8186
*/
8287
typedef struct {
8388
/* Monitoring specific to this code object */
84-
_Py_Monitors local_monitors;
89+
_Py_LocalMonitors local_monitors;
8590
/* Monitoring that is active on this code object */
86-
_Py_Monitors active_monitors;
91+
_Py_LocalMonitors active_monitors;
8792
/* The tools that are to be notified for events for the matching code unit */
8893
uint8_t *tools;
8994
/* Information to support line events */

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ struct _Py_global_strings {
269269
STRUCT_FOR_ID(a)
270270
STRUCT_FOR_ID(abs_tol)
271271
STRUCT_FOR_ID(access)
272+
STRUCT_FOR_ID(aclose)
272273
STRUCT_FOR_ID(add)
273274
STRUCT_FOR_ID(add_done_callback)
274275
STRUCT_FOR_ID(after_in_child)
@@ -282,7 +283,9 @@ struct _Py_global_strings {
282283
STRUCT_FOR_ID(arguments)
283284
STRUCT_FOR_ID(argv)
284285
STRUCT_FOR_ID(as_integer_ratio)
286+
STRUCT_FOR_ID(asend)
285287
STRUCT_FOR_ID(ast)
288+
STRUCT_FOR_ID(athrow)
286289
STRUCT_FOR_ID(attribute)
287290
STRUCT_FOR_ID(authorizer_callback)
288291
STRUCT_FOR_ID(autocommit)
@@ -408,6 +411,7 @@ struct _Py_global_strings {
408411
STRUCT_FOR_ID(exp)
409412
STRUCT_FOR_ID(extend)
410413
STRUCT_FOR_ID(extra_tokens)
414+
STRUCT_FOR_ID(f)
411415
STRUCT_FOR_ID(facility)
412416
STRUCT_FOR_ID(factory)
413417
STRUCT_FOR_ID(false)
@@ -452,6 +456,7 @@ struct _Py_global_strings {
452456
STRUCT_FOR_ID(globals)
453457
STRUCT_FOR_ID(groupindex)
454458
STRUCT_FOR_ID(groups)
459+
STRUCT_FOR_ID(h)
455460
STRUCT_FOR_ID(handle)
456461
STRUCT_FOR_ID(hash_name)
457462
STRUCT_FOR_ID(header)

Include/internal/pycore_instruments.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ extern "C" {
2828
#define PY_MONITORING_EVENT_BRANCH 8
2929
#define PY_MONITORING_EVENT_STOP_ITERATION 9
3030

31-
#define PY_MONITORING_INSTRUMENTED_EVENTS 10
31+
#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \
32+
((ev) < _PY_MONITORING_LOCAL_EVENTS)
3233

3334
/* Other events, mainly exceptions */
3435

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;

Include/internal/pycore_runtime_init_generated.h

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_monitoring.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,9 +1136,11 @@ def test_instruction_then_line(self):
11361136
self.check_events(self.func2,
11371137
recorders = recorders, must_include = self.MUST_INCLUDE_CI)
11381138

1139+
LOCAL_RECORDERS = CallRecorder, LineRecorder, CReturnRecorder, CRaiseRecorder
1140+
11391141
class TestLocalEvents(MonitoringTestBase, unittest.TestCase):
11401142

1141-
def check_events(self, func, expected, tool=TEST_TOOL, recorders=(ExceptionRecorder,)):
1143+
def check_events(self, func, expected, tool=TEST_TOOL, recorders=()):
11421144
try:
11431145
self.assertEqual(sys.monitoring._all_events(), {})
11441146
event_list = []
@@ -1166,7 +1168,7 @@ def func1():
11661168
line2 = 2
11671169
line3 = 3
11681170

1169-
self.check_events(func1, recorders = MANY_RECORDERS, expected = [
1171+
self.check_events(func1, recorders = LOCAL_RECORDERS, expected = [
11701172
('line', 'func1', 1),
11711173
('line', 'func1', 2),
11721174
('line', 'func1', 3)])
@@ -1178,7 +1180,7 @@ def func2():
11781180
[].append(2)
11791181
line3 = 3
11801182

1181-
self.check_events(func2, recorders = MANY_RECORDERS, expected = [
1183+
self.check_events(func2, recorders = LOCAL_RECORDERS, expected = [
11821184
('line', 'func2', 1),
11831185
('line', 'func2', 2),
11841186
('call', 'append', [2]),
@@ -1195,15 +1197,17 @@ def func3():
11951197
line = 5
11961198
line = 6
11971199

1198-
self.check_events(func3, recorders = MANY_RECORDERS, expected = [
1200+
self.check_events(func3, recorders = LOCAL_RECORDERS, expected = [
11991201
('line', 'func3', 1),
12001202
('line', 'func3', 2),
12011203
('line', 'func3', 3),
1202-
('raise', KeyError),
12031204
('line', 'func3', 4),
12041205
('line', 'func3', 5),
12051206
('line', 'func3', 6)])
12061207

1208+
def test_set_non_local_event(self):
1209+
with self.assertRaises(ValueError):
1210+
sys.monitoring.set_local_events(TEST_TOOL, just_call.__code__, E.RAISE)
12071211

12081212
def line_from_offset(code, offset):
12091213
for start, end, line in code.co_lines():
@@ -1616,3 +1620,19 @@ def run():
16161620
self.assertEqual(caught, "inner")
16171621
finally:
16181622
sys.monitoring.set_events(TEST_TOOL, 0)
1623+
1624+
def test_108390(self):
1625+
1626+
class Foo:
1627+
def __init__(self, set_event):
1628+
if set_event:
1629+
sys.monitoring.set_events(TEST_TOOL, E.PY_RESUME)
1630+
1631+
def make_foo_optimized_then_set_event():
1632+
for i in range(100):
1633+
Foo(i == 99)
1634+
1635+
try:
1636+
make_foo_optimized_then_set_event()
1637+
finally:
1638+
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
@@ -2012,28 +2012,30 @@ do_monitor_exc(PyThreadState *tstate, _PyInterpreterFrame *frame,
20122012
return err;
20132013
}
20142014

2015-
static inline int
2016-
no_tools_for_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
2015+
static inline bool
2016+
no_tools_for_global_event(PyThreadState *tstate, int event)
20172017
{
2018+
return tstate->interp->monitors.tools[event] == 0;
2019+
}
2020+
2021+
static inline bool
2022+
no_tools_for_local_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
2023+
{
2024+
assert(event < _PY_MONITORING_LOCAL_EVENTS);
20182025
_PyCoMonitoringData *data = frame->f_code->_co_monitoring;
20192026
if (data) {
2020-
if (data->active_monitors.tools[event] == 0) {
2021-
return 1;
2022-
}
2027+
return data->active_monitors.tools[event] == 0;
20232028
}
20242029
else {
2025-
if (tstate->interp->monitors.tools[event] == 0) {
2026-
return 1;
2027-
}
2030+
return no_tools_for_global_event(tstate, event);
20282031
}
2029-
return 0;
20302032
}
20312033

20322034
static void
20332035
monitor_raise(PyThreadState *tstate, _PyInterpreterFrame *frame,
20342036
_Py_CODEUNIT *instr)
20352037
{
2036-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RAISE)) {
2038+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RAISE)) {
20372039
return;
20382040
}
20392041
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE);
@@ -2043,7 +2045,7 @@ static void
20432045
monitor_reraise(PyThreadState *tstate, _PyInterpreterFrame *frame,
20442046
_Py_CODEUNIT *instr)
20452047
{
2046-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RERAISE)) {
2048+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RERAISE)) {
20472049
return;
20482050
}
20492051
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RERAISE);
@@ -2053,7 +2055,7 @@ static int
20532055
monitor_stop_iteration(PyThreadState *tstate, _PyInterpreterFrame *frame,
20542056
_Py_CODEUNIT *instr)
20552057
{
2056-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
2058+
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
20572059
return 0;
20582060
}
20592061
return do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_STOP_ITERATION);
@@ -2064,7 +2066,7 @@ monitor_unwind(PyThreadState *tstate,
20642066
_PyInterpreterFrame *frame,
20652067
_Py_CODEUNIT *instr)
20662068
{
2067-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND)) {
2069+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND)) {
20682070
return;
20692071
}
20702072
_Py_call_instrumentation_exc0(tstate, PY_MONITORING_EVENT_PY_UNWIND, frame, instr);
@@ -2076,7 +2078,7 @@ monitor_handled(PyThreadState *tstate,
20762078
_PyInterpreterFrame *frame,
20772079
_Py_CODEUNIT *instr, PyObject *exc)
20782080
{
2079-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
2081+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
20802082
return;
20812083
}
20822084
_Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc);
@@ -2087,7 +2089,7 @@ monitor_throw(PyThreadState *tstate,
20872089
_PyInterpreterFrame *frame,
20882090
_Py_CODEUNIT *instr)
20892091
{
2090-
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_THROW)) {
2092+
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_THROW)) {
20912093
return;
20922094
}
20932095
_Py_call_instrumentation_exc0(tstate, PY_MONITORING_EVENT_PY_THROW, frame, instr);

0 commit comments

Comments
 (0)