Skip to content

Commit 309d7cc

Browse files
authored
bpo-35370: Add _PyEval_SetTrace() function (GH-18975)
* sys.settrace(), sys.setprofile() and _lsprof.Profiler.enable() now properly report PySys_Audit() error if "sys.setprofile" or "sys.settrace" audit event is denied. * Add _PyEval_SetProfile() and _PyEval_SetTrace() function: similar to PyEval_SetProfile() and PyEval_SetTrace() but take a tstate parameter and return -1 on error. * Add _PyObject_FastCallTstate() function.
1 parent 9ee88cd commit 309d7cc

File tree

7 files changed

+143
-52
lines changed

7 files changed

+143
-52
lines changed

Doc/c-api/init.rst

+5
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,8 @@ Python-level trace functions in previous versions.
14981498
profile function is called for all monitored events except :const:`PyTrace_LINE`
14991499
:const:`PyTrace_OPCODE` and :const:`PyTrace_EXCEPTION`.
15001500
1501+
The caller must hold the :term:`GIL`.
1502+
15011503
15021504
.. c:function:: void PyEval_SetTrace(Py_tracefunc func, PyObject *obj)
15031505
@@ -1508,6 +1510,9 @@ Python-level trace functions in previous versions.
15081510
will not receive :const:`PyTrace_C_CALL`, :const:`PyTrace_C_EXCEPTION` or
15091511
:const:`PyTrace_C_RETURN` as a value for the *what* parameter.
15101512
1513+
The caller must hold the :term:`GIL`.
1514+
1515+
15111516
.. _advanced-debugging:
15121517
15131518
Advanced Debugger Support

Include/cpython/abstract.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,18 @@ PyAPI_FUNC(PyObject *) PyObject_VectorcallDict(
142142
"tuple" and keyword arguments "dict". "dict" may also be NULL */
143143
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
144144

145+
static inline PyObject *
146+
_PyObject_FastCallTstate(PyThreadState *tstate, PyObject *func, PyObject *const *args, Py_ssize_t nargs)
147+
{
148+
return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL);
149+
}
150+
145151
/* Same as PyObject_Vectorcall except without keyword arguments */
146152
static inline PyObject *
147153
_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
148154
{
149155
PyThreadState *tstate = PyThreadState_GET();
150-
return _PyObject_VectorcallTstate(tstate, func, args, (size_t)nargs, NULL);
156+
return _PyObject_FastCallTstate(tstate, func, args, nargs);
151157
}
152158

153159
/* Call a callable without any arguments

Include/cpython/ceval.h

+2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ extern "C" {
77
#endif
88

99
PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *);
10+
PyAPI_DATA(int) _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg);
1011
PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *);
12+
PyAPI_FUNC(int) _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg);
1113
PyAPI_FUNC(int) _PyEval_GetCoroutineOriginTrackingDepth(void);
1214
PyAPI_FUNC(void) _PyEval_SetAsyncGenFirstiter(PyObject *);
1315
PyAPI_FUNC(PyObject *) _PyEval_GetAsyncGenFirstiter(void);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
sys.settrace(), sys.setprofile() and _lsprof.Profiler.enable() now properly
2+
report :c:func:`PySys_Audit` error if "sys.setprofile" or "sys.settrace"
3+
audit event is denied.

Modules/_lsprof.c

+24-7
Original file line numberDiff line numberDiff line change
@@ -578,8 +578,9 @@ static PyObject*
578578
profiler_getstats(ProfilerObject *pObj, PyObject* noarg)
579579
{
580580
statscollector_t collect;
581-
if (pending_exception(pObj))
581+
if (pending_exception(pObj)) {
582582
return NULL;
583+
}
583584
if (!pObj->externalTimer || pObj->externalTimerUnit == 0.0) {
584585
_PyTime_t onesec = _PyTime_FromSeconds(1);
585586
collect.factor = (double)1 / onesec;
@@ -639,9 +640,15 @@ profiler_enable(ProfilerObject *self, PyObject *args, PyObject *kwds)
639640
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii:enable",
640641
kwlist, &subcalls, &builtins))
641642
return NULL;
642-
if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0)
643+
if (setSubcalls(self, subcalls) < 0 || setBuiltins(self, builtins) < 0) {
644+
return NULL;
645+
}
646+
647+
PyThreadState *tstate = PyThreadState_GET();
648+
if (_PyEval_SetProfile(tstate, profiler_callback, (PyObject*)self) < 0) {
643649
return NULL;
644-
PyEval_SetProfile(profiler_callback, (PyObject*)self);
650+
}
651+
645652
self->flags |= POF_ENABLED;
646653
Py_RETURN_NONE;
647654
}
@@ -671,11 +678,16 @@ Stop collecting profiling information.\n\
671678
static PyObject*
672679
profiler_disable(ProfilerObject *self, PyObject* noarg)
673680
{
681+
PyThreadState *tstate = PyThreadState_GET();
682+
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
683+
return NULL;
684+
}
674685
self->flags &= ~POF_ENABLED;
675-
PyEval_SetProfile(NULL, NULL);
686+
676687
flush_unmatched(self);
677-
if (pending_exception(self))
688+
if (pending_exception(self)) {
678689
return NULL;
690+
}
679691
Py_RETURN_NONE;
680692
}
681693

@@ -695,8 +707,13 @@ profiler_clear(ProfilerObject *pObj, PyObject* noarg)
695707
static void
696708
profiler_dealloc(ProfilerObject *op)
697709
{
698-
if (op->flags & POF_ENABLED)
699-
PyEval_SetProfile(NULL, NULL);
710+
if (op->flags & POF_ENABLED) {
711+
PyThreadState *tstate = PyThreadState_GET();
712+
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
713+
PyErr_WriteUnraisable((PyObject *)op);
714+
}
715+
}
716+
700717
flush_unmatched(op);
701718
clearEntries(op);
702719
Py_XDECREF(op->externalTimer);

Python/ceval.c

+54-20
Original file line numberDiff line numberDiff line change
@@ -4586,51 +4586,85 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
45864586
return result;
45874587
}
45884588

4589-
void
4590-
PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
4589+
int
4590+
_PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
45914591
{
4592+
assert(tstate != NULL);
4593+
/* The caller must hold the GIL */
4594+
assert(PyGILState_Check());
4595+
4596+
/* Call PySys_Audit() in the context of the current thread state,
4597+
even if tstate is not the current thread state. */
45924598
if (PySys_Audit("sys.setprofile", NULL) < 0) {
4593-
return;
4599+
return -1;
45944600
}
45954601

4596-
PyThreadState *tstate = _PyThreadState_GET();
4597-
PyObject *temp = tstate->c_profileobj;
4598-
Py_XINCREF(arg);
4602+
PyObject *profileobj = tstate->c_profileobj;
4603+
45994604
tstate->c_profilefunc = NULL;
46004605
tstate->c_profileobj = NULL;
4601-
/* Must make sure that tracing is not ignored if 'temp' is freed */
4606+
/* Must make sure that tracing is not ignored if 'profileobj' is freed */
46024607
tstate->use_tracing = tstate->c_tracefunc != NULL;
4603-
Py_XDECREF(temp);
4604-
tstate->c_profilefunc = func;
4608+
Py_XDECREF(profileobj);
4609+
4610+
Py_XINCREF(arg);
46054611
tstate->c_profileobj = arg;
4612+
tstate->c_profilefunc = func;
4613+
46064614
/* Flag that tracing or profiling is turned on */
46074615
tstate->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL);
4616+
return 0;
46084617
}
46094618

46104619
void
4611-
PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
4620+
PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
4621+
{
4622+
PyThreadState *tstate = _PyThreadState_GET();
4623+
(void)_PyEval_SetProfile(tstate, func, arg);
4624+
}
4625+
4626+
int
4627+
_PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
46124628
{
4629+
assert(tstate != NULL);
4630+
/* The caller must hold the GIL */
4631+
assert(PyGILState_Check());
4632+
4633+
/* Call PySys_Audit() in the context of the current thread state,
4634+
even if tstate is not the current thread state. */
46134635
if (PySys_Audit("sys.settrace", NULL) < 0) {
4614-
return;
4636+
return -1;
46154637
}
46164638

4617-
_PyRuntimeState *runtime = &_PyRuntime;
4618-
PyThreadState *tstate = _PyRuntimeState_GetThreadState(runtime);
4619-
PyObject *temp = tstate->c_traceobj;
4620-
runtime->ceval.tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL);
4621-
Py_XINCREF(arg);
4639+
struct _ceval_runtime_state *ceval = &tstate->interp->runtime->ceval;
4640+
PyObject *traceobj = tstate->c_traceobj;
4641+
ceval->tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL);
4642+
46224643
tstate->c_tracefunc = NULL;
46234644
tstate->c_traceobj = NULL;
4624-
/* Must make sure that profiling is not ignored if 'temp' is freed */
4625-
tstate->use_tracing = tstate->c_profilefunc != NULL;
4626-
Py_XDECREF(temp);
4627-
tstate->c_tracefunc = func;
4645+
/* Must make sure that profiling is not ignored if 'traceobj' is freed */
4646+
tstate->use_tracing = (tstate->c_profilefunc != NULL);
4647+
Py_XDECREF(traceobj);
4648+
4649+
Py_XINCREF(arg);
46284650
tstate->c_traceobj = arg;
4651+
tstate->c_tracefunc = func;
4652+
46294653
/* Flag that tracing or profiling is turned on */
46304654
tstate->use_tracing = ((func != NULL)
46314655
|| (tstate->c_profilefunc != NULL));
4656+
4657+
return 0;
4658+
}
4659+
4660+
void
4661+
PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
4662+
{
4663+
PyThreadState *tstate = _PyThreadState_GET();
4664+
(void)_PyEval_SetTrace(tstate, func, arg);
46324665
}
46334666

4667+
46344668
void
46354669
_PyEval_SetCoroutineOriginTrackingDepth(PyThreadState *tstate, int new_depth)
46364670
{

Python/sysmodule.c

+48-24
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,7 @@ trace_init(void)
876876

877877

878878
static PyObject *
879-
call_trampoline(PyObject* callback,
879+
call_trampoline(PyThreadState *tstate, PyObject* callback,
880880
PyFrameObject *frame, int what, PyObject *arg)
881881
{
882882
if (PyFrame_FastToLocalsWithError(frame) < 0) {
@@ -889,7 +889,7 @@ call_trampoline(PyObject* callback,
889889
stack[2] = (arg != NULL) ? arg : Py_None;
890890

891891
/* call the Python-level function */
892-
PyObject *result = _PyObject_FastCall(callback, stack, 3);
892+
PyObject *result = _PyObject_FastCallTstate(tstate, callback, stack, 3);
893893

894894
PyFrame_LocalsToFast(frame, 1);
895895
if (result == NULL) {
@@ -903,15 +903,17 @@ static int
903903
profile_trampoline(PyObject *self, PyFrameObject *frame,
904904
int what, PyObject *arg)
905905
{
906-
PyObject *result;
907-
908-
if (arg == NULL)
906+
if (arg == NULL) {
909907
arg = Py_None;
910-
result = call_trampoline(self, frame, what, arg);
908+
}
909+
910+
PyThreadState *tstate = _PyThreadState_GET();
911+
PyObject *result = call_trampoline(tstate, self, frame, what, arg);
911912
if (result == NULL) {
912-
PyEval_SetProfile(NULL, NULL);
913+
_PyEval_SetProfile(tstate, NULL, NULL);
913914
return -1;
914915
}
916+
915917
Py_DECREF(result);
916918
return 0;
917919
}
@@ -921,20 +923,24 @@ trace_trampoline(PyObject *self, PyFrameObject *frame,
921923
int what, PyObject *arg)
922924
{
923925
PyObject *callback;
924-
PyObject *result;
925-
926-
if (what == PyTrace_CALL)
926+
if (what == PyTrace_CALL) {
927927
callback = self;
928-
else
928+
}
929+
else {
929930
callback = frame->f_trace;
930-
if (callback == NULL)
931+
}
932+
if (callback == NULL) {
931933
return 0;
932-
result = call_trampoline(callback, frame, what, arg);
934+
}
935+
936+
PyThreadState *tstate = _PyThreadState_GET();
937+
PyObject *result = call_trampoline(tstate, callback, frame, what, arg);
933938
if (result == NULL) {
934-
PyEval_SetTrace(NULL, NULL);
939+
_PyEval_SetTrace(tstate, NULL, NULL);
935940
Py_CLEAR(frame->f_trace);
936941
return -1;
937942
}
943+
938944
if (result != Py_None) {
939945
Py_XSETREF(frame->f_trace, result);
940946
}
@@ -947,12 +953,21 @@ trace_trampoline(PyObject *self, PyFrameObject *frame,
947953
static PyObject *
948954
sys_settrace(PyObject *self, PyObject *args)
949955
{
950-
if (trace_init() == -1)
956+
if (trace_init() == -1) {
951957
return NULL;
952-
if (args == Py_None)
953-
PyEval_SetTrace(NULL, NULL);
954-
else
955-
PyEval_SetTrace(trace_trampoline, args);
958+
}
959+
960+
PyThreadState *tstate = _PyThreadState_GET();
961+
if (args == Py_None) {
962+
if (_PyEval_SetTrace(tstate, NULL, NULL) < 0) {
963+
return NULL;
964+
}
965+
}
966+
else {
967+
if (_PyEval_SetTrace(tstate, trace_trampoline, args) < 0) {
968+
return NULL;
969+
}
970+
}
956971
Py_RETURN_NONE;
957972
}
958973

@@ -987,12 +1002,21 @@ sys_gettrace_impl(PyObject *module)
9871002
static PyObject *
9881003
sys_setprofile(PyObject *self, PyObject *args)
9891004
{
990-
if (trace_init() == -1)
1005+
if (trace_init() == -1) {
9911006
return NULL;
992-
if (args == Py_None)
993-
PyEval_SetProfile(NULL, NULL);
994-
else
995-
PyEval_SetProfile(profile_trampoline, args);
1007+
}
1008+
1009+
PyThreadState *tstate = _PyThreadState_GET();
1010+
if (args == Py_None) {
1011+
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
1012+
return NULL;
1013+
}
1014+
}
1015+
else {
1016+
if (_PyEval_SetProfile(tstate, profile_trampoline, args) < 0) {
1017+
return NULL;
1018+
}
1019+
}
9961020
Py_RETURN_NONE;
9971021
}
9981022

0 commit comments

Comments
 (0)