0
0
mirror of https://github.com/python/cpython.git synced 2024-11-24 00:38:00 +01:00

gh-94510: Raise on re-entrant calls to sys.setprofile and sys.settrace (GH-94511)

Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
Pablo Galindo Salgado 2022-07-05 19:18:47 +01:00 committed by GitHub
parent 5f319308a8
commit 40d81fd63b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 3 deletions

View File

@ -2,6 +2,7 @@ import gc
import pprint
import sys
import unittest
from test import support
class TestGetProfile(unittest.TestCase):
@ -415,5 +416,43 @@ def show_events(callable):
pprint.pprint(capture_events(callable))
class TestEdgeCases(unittest.TestCase):
def setUp(self):
self.addCleanup(sys.setprofile, sys.getprofile())
sys.setprofile(None)
def test_reentrancy(self):
def foo(*args):
...
def bar(*args):
...
class A:
def __call__(self, *args):
pass
def __del__(self):
sys.setprofile(bar)
sys.setprofile(A())
with support.catch_unraisable_exception() as cm:
sys.setprofile(foo)
self.assertEqual(cm.unraisable.object, A.__del__)
self.assertIsInstance(cm.unraisable.exc_value, RuntimeError)
self.assertEqual(sys.getprofile(), foo)
def test_same_object(self):
def foo(*args):
...
sys.setprofile(foo)
del foo
sys.setprofile(sys.getprofile())
if __name__ == "__main__":
unittest.main()

View File

@ -2,6 +2,7 @@
from test import support
import unittest
from unittest.mock import MagicMock
import sys
import difflib
import gc
@ -2684,5 +2685,43 @@ class TestExtendedArgs(unittest.TestCase):
self.assertEqual(counts, {'call': 1, 'line': 2000, 'return': 1})
class TestEdgeCases(unittest.TestCase):
def setUp(self):
self.addCleanup(sys.settrace, sys.gettrace())
sys.settrace(None)
def test_reentrancy(self):
def foo(*args):
...
def bar(*args):
...
class A:
def __call__(self, *args):
pass
def __del__(self):
sys.settrace(bar)
sys.settrace(A())
with support.catch_unraisable_exception() as cm:
sys.settrace(foo)
self.assertEqual(cm.unraisable.object, A.__del__)
self.assertIsInstance(cm.unraisable.exc_value, RuntimeError)
self.assertEqual(sys.gettrace(), foo)
def test_same_object(self):
def foo(*args):
...
sys.settrace(foo)
del foo
sys.settrace(sys.gettrace())
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,2 @@
Re-entrant calls to :func:`sys.setprofile` and :func:`sys.settrace` now
raise :exc:`RuntimeError`. Patch by Pablo Galindo.

View File

@ -750,7 +750,7 @@ profiler_dealloc(ProfilerObject *op)
if (op->flags & POF_ENABLED) {
PyThreadState *tstate = _PyThreadState_GET();
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
PyErr_WriteUnraisable((PyObject *)op);
_PyErr_WriteUnraisableMsg("When destroying _lsprof profiler", NULL);
}
}

View File

@ -6973,10 +6973,20 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
/* The caller must hold the GIL */
assert(PyGILState_Check());
static int reentrant = 0;
if (reentrant) {
_PyErr_SetString(tstate, PyExc_RuntimeError, "Cannot install a profile function "
"while another profile function is being installed");
reentrant = 0;
return -1;
}
reentrant = 1;
/* Call _PySys_Audit() in the context of the current thread state,
even if tstate is not the current thread state. */
PyThreadState *current_tstate = _PyThreadState_GET();
if (_PySys_Audit(current_tstate, "sys.setprofile", NULL) < 0) {
reentrant = 0;
return -1;
}
@ -6994,6 +7004,7 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
/* Flag that tracing or profiling is turned on */
_PyThreadState_UpdateTracingState(tstate);
reentrant = 0;
return 0;
}
@ -7014,10 +7025,21 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
/* The caller must hold the GIL */
assert(PyGILState_Check());
static int reentrant = 0;
if (reentrant) {
_PyErr_SetString(tstate, PyExc_RuntimeError, "Cannot install a trace function "
"while another trace function is being installed");
reentrant = 0;
return -1;
}
reentrant = 1;
/* Call _PySys_Audit() in the context of the current thread state,
even if tstate is not the current thread state. */
PyThreadState *current_tstate = _PyThreadState_GET();
if (_PySys_Audit(current_tstate, "sys.settrace", NULL) < 0) {
reentrant = 0;
return -1;
}
@ -7027,15 +7049,15 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
tstate->c_traceobj = NULL;
/* Must make sure that profiling is not ignored if 'traceobj' is freed */
_PyThreadState_UpdateTracingState(tstate);
Py_XDECREF(traceobj);
Py_XINCREF(arg);
Py_XDECREF(traceobj);
tstate->c_traceobj = arg;
tstate->c_tracefunc = func;
/* Flag that tracing or profiling is turned on */
_PyThreadState_UpdateTracingState(tstate);
reentrant = 0;
return 0;
}