Skip to content

GH-108390: Prevent non-local events being set with sys.monitoring.set_local_events() #108420

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions Include/cpython/code.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@
extern "C" {
#endif


/* Count of all local monitoring events */
#define _PY_MONITORING_LOCAL_EVENTS 10
/* Count of all "real" monitoring events (not derived from other events) */
#define _PY_MONITORING_UNGROUPED_EVENTS 15
/* Count of all monitoring events */
#define _PY_MONITORING_EVENTS 17

/* Table of which tools are active for each monitored event. */
typedef struct _Py_Monitors {
/* Tables of which tools are active for each monitored event. */
typedef struct _Py_LocalMonitors {
uint8_t tools[_PY_MONITORING_LOCAL_EVENTS];
} _Py_LocalMonitors;

typedef struct _Py_GlobalMonitors {
uint8_t tools[_PY_MONITORING_UNGROUPED_EVENTS];
} _Py_Monitors;
} _Py_GlobalMonitors;

/* Each instruction in a code object is a fixed-width value,
* currently 2 bytes: 1-byte opcode + 1-byte oparg. The EXTENDED_ARG
Expand Down Expand Up @@ -88,9 +93,9 @@ typedef struct {
*/
typedef struct {
/* Monitoring specific to this code object */
_Py_Monitors local_monitors;
_Py_LocalMonitors local_monitors;
/* Monitoring that is active on this code object */
_Py_Monitors active_monitors;
_Py_LocalMonitors active_monitors;
/* The tools that are to be notified for events for the matching code unit */
uint8_t *tools;
/* Information to support line events */
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_instruments.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extern "C" {
#define PY_MONITORING_EVENT_STOP_ITERATION 9

#define PY_MONITORING_IS_INSTRUMENTED_EVENT(ev) \
((ev) <= PY_MONITORING_EVENT_STOP_ITERATION)
((ev) < _PY_MONITORING_LOCAL_EVENTS)

/* Other events, mainly exceptions */

Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ struct _is {
uint16_t optimizer_resume_threshold;
uint16_t optimizer_backedge_threshold;

_Py_Monitors monitors;
_Py_GlobalMonitors monitors;
bool f_opcode_trace_set;
bool sys_profile_initialized;
bool sys_trace_initialized;
Expand Down
30 changes: 25 additions & 5 deletions Lib/test/test_monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -1218,9 +1218,11 @@ def test_instruction_then_line(self):
self.check_events(self.func2,
recorders = recorders, must_include = self.MUST_INCLUDE_CI)

LOCAL_RECORDERS = CallRecorder, LineRecorder, CReturnRecorder, CRaiseRecorder

class TestLocalEvents(MonitoringTestBase, unittest.TestCase):

def check_events(self, func, expected, tool=TEST_TOOL, recorders=(ExceptionRecorder,)):
def check_events(self, func, expected, tool=TEST_TOOL, recorders=()):
try:
self.assertEqual(sys.monitoring._all_events(), {})
event_list = []
Expand Down Expand Up @@ -1248,7 +1250,7 @@ def func1():
line2 = 2
line3 = 3

self.check_events(func1, recorders = MANY_RECORDERS, expected = [
self.check_events(func1, recorders = LOCAL_RECORDERS, expected = [
('line', 'func1', 1),
('line', 'func1', 2),
('line', 'func1', 3)])
Expand All @@ -1260,7 +1262,7 @@ def func2():
[].append(2)
line3 = 3

self.check_events(func2, recorders = MANY_RECORDERS, expected = [
self.check_events(func2, recorders = LOCAL_RECORDERS, expected = [
('line', 'func2', 1),
('line', 'func2', 2),
('call', 'append', [2]),
Expand All @@ -1277,15 +1279,17 @@ def func3():
line = 5
line = 6

self.check_events(func3, recorders = MANY_RECORDERS, expected = [
self.check_events(func3, recorders = LOCAL_RECORDERS, expected = [
('line', 'func3', 1),
('line', 'func3', 2),
('line', 'func3', 3),
('raise', KeyError),
('line', 'func3', 4),
('line', 'func3', 5),
('line', 'func3', 6)])

def test_set_non_local_event(self):
with self.assertRaises(ValueError):
sys.monitoring.set_local_events(TEST_TOOL, just_call.__code__, E.RAISE)

def line_from_offset(code, offset):
for start, end, line in code.co_lines():
Expand Down Expand Up @@ -1698,3 +1702,19 @@ def run():
self.assertEqual(caught, "inner")
finally:
sys.monitoring.set_events(TEST_TOOL, 0)

def test_108390(self):

class Foo:
def __init__(self, set_event):
if set_event:
sys.monitoring.set_events(TEST_TOOL, E.PY_RESUME)

def make_foo_optimized_then_set_event():
for i in range(100):
Foo(i == 99)

try:
make_foo_optimized_then_set_event()
finally:
sys.monitoring.set_events(TEST_TOOL, 0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Raise an exception when setting a non-local event (``RAISE``, ``EXCEPTION_HANDLED``,
etc.) in ``sys.monitoring.set_local_events``.

Fixes crash when tracing in recursive calls to Python classes.
32 changes: 17 additions & 15 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1968,28 +1968,30 @@ do_monitor_exc(PyThreadState *tstate, _PyInterpreterFrame *frame,
return err;
}

static inline int
no_tools_for_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
static inline bool
no_tools_for_global_event(PyThreadState *tstate, int event)
{
return tstate->interp->monitors.tools[event] == 0;
}

static inline bool
no_tools_for_local_event(PyThreadState *tstate, _PyInterpreterFrame *frame, int event)
{
assert(event < _PY_MONITORING_LOCAL_EVENTS);
_PyCoMonitoringData *data = _PyFrame_GetCode(frame)->_co_monitoring;
if (data) {
if (data->active_monitors.tools[event] == 0) {
return 1;
}
return data->active_monitors.tools[event] == 0;
}
else {
if (tstate->interp->monitors.tools[event] == 0) {
return 1;
}
return no_tools_for_global_event(tstate, event);
}
return 0;
}

static void
monitor_raise(PyThreadState *tstate, _PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RAISE)) {
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RAISE)) {
return;
}
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE);
Expand All @@ -1999,7 +2001,7 @@ static void
monitor_reraise(PyThreadState *tstate, _PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_RERAISE)) {
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_RERAISE)) {
return;
}
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RERAISE);
Expand All @@ -2009,7 +2011,7 @@ static int
monitor_stop_iteration(PyThreadState *tstate, _PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_STOP_ITERATION)) {
return 0;
}
return do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_STOP_ITERATION);
Expand All @@ -2020,7 +2022,7 @@ monitor_unwind(PyThreadState *tstate,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_UNWIND)) {
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND)) {
return;
}
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND);
Expand All @@ -2032,7 +2034,7 @@ monitor_handled(PyThreadState *tstate,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr, PyObject *exc)
{
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED)) {
return 0;
}
return _Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc);
Expand All @@ -2043,7 +2045,7 @@ monitor_throw(PyThreadState *tstate,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *instr)
{
if (no_tools_for_event(tstate, frame, PY_MONITORING_EVENT_PY_THROW)) {
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_THROW)) {
return;
}
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_THROW);
Expand Down
Loading