mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
[3.11] gh-94510: Raise on re-entrant calls to sys.setprofile and sys.settrace (GH-94511) (GH-94578)
Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
(cherry picked from commit 40d81fd63b
)
This commit is contained in:
parent
552fc9a9ac
commit
5f4a16b291
5 changed files with 105 additions and 3 deletions
|
@ -2,6 +2,7 @@ import gc
|
||||||
import pprint
|
import pprint
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
from test import support
|
||||||
|
|
||||||
|
|
||||||
class TestGetProfile(unittest.TestCase):
|
class TestGetProfile(unittest.TestCase):
|
||||||
|
@ -415,5 +416,43 @@ def show_events(callable):
|
||||||
pprint.pprint(capture_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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest.mock import MagicMock
|
||||||
import sys
|
import sys
|
||||||
import difflib
|
import difflib
|
||||||
import gc
|
import gc
|
||||||
|
@ -2684,5 +2685,43 @@ class TestExtendedArgs(unittest.TestCase):
|
||||||
self.assertEqual(counts, {'call': 1, 'line': 2000, 'return': 1})
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Re-entrant calls to :func:`sys.setprofile` and :func:`sys.settrace` now
|
||||||
|
raise :exc:`RuntimeError`. Patch by Pablo Galindo.
|
|
@ -750,7 +750,7 @@ profiler_dealloc(ProfilerObject *op)
|
||||||
if (op->flags & POF_ENABLED) {
|
if (op->flags & POF_ENABLED) {
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
|
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
|
||||||
PyErr_WriteUnraisable((PyObject *)op);
|
_PyErr_WriteUnraisableMsg("When destroying _lsprof profiler", NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6930,10 +6930,20 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||||
/* The caller must hold the GIL */
|
/* The caller must hold the GIL */
|
||||||
assert(PyGILState_Check());
|
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,
|
/* Call _PySys_Audit() in the context of the current thread state,
|
||||||
even if tstate is not the current thread state. */
|
even if tstate is not the current thread state. */
|
||||||
PyThreadState *current_tstate = _PyThreadState_GET();
|
PyThreadState *current_tstate = _PyThreadState_GET();
|
||||||
if (_PySys_Audit(current_tstate, "sys.setprofile", NULL) < 0) {
|
if (_PySys_Audit(current_tstate, "sys.setprofile", NULL) < 0) {
|
||||||
|
reentrant = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6951,6 +6961,7 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||||
|
|
||||||
/* Flag that tracing or profiling is turned on */
|
/* Flag that tracing or profiling is turned on */
|
||||||
_PyThreadState_UpdateTracingState(tstate);
|
_PyThreadState_UpdateTracingState(tstate);
|
||||||
|
reentrant = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6971,10 +6982,21 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||||
/* The caller must hold the GIL */
|
/* The caller must hold the GIL */
|
||||||
assert(PyGILState_Check());
|
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,
|
/* Call _PySys_Audit() in the context of the current thread state,
|
||||||
even if tstate is not the current thread state. */
|
even if tstate is not the current thread state. */
|
||||||
PyThreadState *current_tstate = _PyThreadState_GET();
|
PyThreadState *current_tstate = _PyThreadState_GET();
|
||||||
if (_PySys_Audit(current_tstate, "sys.settrace", NULL) < 0) {
|
if (_PySys_Audit(current_tstate, "sys.settrace", NULL) < 0) {
|
||||||
|
reentrant = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6984,15 +7006,15 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
|
||||||
tstate->c_traceobj = NULL;
|
tstate->c_traceobj = NULL;
|
||||||
/* Must make sure that profiling is not ignored if 'traceobj' is freed */
|
/* Must make sure that profiling is not ignored if 'traceobj' is freed */
|
||||||
_PyThreadState_UpdateTracingState(tstate);
|
_PyThreadState_UpdateTracingState(tstate);
|
||||||
Py_XDECREF(traceobj);
|
|
||||||
|
|
||||||
Py_XINCREF(arg);
|
Py_XINCREF(arg);
|
||||||
|
Py_XDECREF(traceobj);
|
||||||
tstate->c_traceobj = arg;
|
tstate->c_traceobj = arg;
|
||||||
tstate->c_tracefunc = func;
|
tstate->c_tracefunc = func;
|
||||||
|
|
||||||
/* Flag that tracing or profiling is turned on */
|
/* Flag that tracing or profiling is turned on */
|
||||||
_PyThreadState_UpdateTracingState(tstate);
|
_PyThreadState_UpdateTracingState(tstate);
|
||||||
|
|
||||||
|
reentrant = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue