summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Galindo Salgado <Pablogsal@gmail.com>2022-07-05 18:52:33 (GMT)
committerGitHub <noreply@github.com>2022-07-05 18:52:33 (GMT)
commit5e24c80b948da6995990296cf262d9eae265e8ec (patch)
treeca0c20c8012eea7e14d34c7f1488587b88977b05
parentfd34bfe48444fdb22ff1ae78941cf621854b351f (diff)
downloadcpython-5e24c80b948da6995990296cf262d9eae265e8ec.zip
cpython-5e24c80b948da6995990296cf262d9eae265e8ec.tar.gz
cpython-5e24c80b948da6995990296cf262d9eae265e8ec.tar.bz2
[3.10] gh-94510: Raise on re-entrant calls to sys.setprofile and syssettrace (GH-94511) (#94579)
Co-authored-by: Ɓukasz Langa <lukasz@langa.pl>. Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
-rw-r--r--Lib/test/test_sys_setprofile.py39
-rw-r--r--Lib/test/test_sys_settrace.py39
-rw-r--r--Misc/NEWS.d/next/Library/2022-07-02-19-46-30.gh-issue-94510.xOatDC.rst2
-rw-r--r--Modules/_lsprof.c2
-rw-r--r--Python/ceval.c26
5 files changed, 105 insertions, 3 deletions
diff --git a/Lib/test/test_sys_setprofile.py b/Lib/test/test_sys_setprofile.py
index 21a09b5..4c3053a 100644
--- a/Lib/test/test_sys_setprofile.py
+++ b/Lib/test/test_sys_setprofile.py
@@ -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()
diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index 4b3d096..1f509ee 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -2,6 +2,7 @@
from test import support
import unittest
+from unittest.mock import MagicMock
import sys
import difflib
import gc
@@ -2196,5 +2197,43 @@ output.append(4)
output.append(8)
+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()
diff --git a/Misc/NEWS.d/next/Library/2022-07-02-19-46-30.gh-issue-94510.xOatDC.rst b/Misc/NEWS.d/next/Library/2022-07-02-19-46-30.gh-issue-94510.xOatDC.rst
new file mode 100644
index 0000000..55856d5
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-07-02-19-46-30.gh-issue-94510.xOatDC.rst
@@ -0,0 +1,2 @@
+Re-entrant calls to :func:`sys.setprofile` and :func:`sys.settrace` now
+raise :exc:`RuntimeError`. Patch by Pablo Galindo.
diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c
index 703067c..8d754aa 100644
--- a/Modules/_lsprof.c
+++ b/Modules/_lsprof.c
@@ -744,7 +744,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);
}
}
diff --git a/Python/ceval.c b/Python/ceval.c
index 9a193c9..df4b9a8 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -5525,10 +5525,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;
}
@@ -5546,6 +5556,7 @@ _PyEval_SetProfile(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
/* Flag that tracing or profiling is turned on */
tstate->cframe->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL);
+ reentrant = 0;
return 0;
}
@@ -5566,10 +5577,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;
}
@@ -5579,9 +5601,8 @@ _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 */
tstate->cframe->use_tracing = (tstate->c_profilefunc != NULL);
- Py_XDECREF(traceobj);
-
Py_XINCREF(arg);
+ Py_XDECREF(traceobj);
tstate->c_traceobj = arg;
tstate->c_tracefunc = func;
@@ -5589,6 +5610,7 @@ _PyEval_SetTrace(PyThreadState *tstate, Py_tracefunc func, PyObject *arg)
tstate->cframe->use_tracing = ((func != NULL)
|| (tstate->c_profilefunc != NULL));
+ reentrant = 0;
return 0;
}