Backport of r66677: _lsprof crasher when a bad external timer is used during
garbage collection of a Profiler object.
diff --git a/Lib/test/test_cProfile.py b/Lib/test/test_cProfile.py
index 07b2a9b..f41f404 100644
--- a/Lib/test/test_cProfile.py
+++ b/Lib/test/test_cProfile.py
@@ -1,6 +1,5 @@
"""Test suite for the cProfile module."""
-
-import cProfile, pstats, sys
+import cProfile, pstats, sys, test.test_support
# In order to have reproducible time, we simulate a timer in the global
# variable 'ticks', which represents simulated time in milliseconds.
@@ -23,6 +22,7 @@
st.strip_dirs().sort_stats('stdname').print_stats()
st.print_callees()
st.print_callers()
+ test_bad_counter_during_dealloc()
def timer():
return ticks
@@ -119,5 +119,20 @@
ticks += 1
raise AttributeError
+# Issue 3895.
+def test_bad_counter_during_dealloc():
+ import _lsprof
+ # Must use a file as StringIO doesn't trigger the bug.
+ sys.stderr = open(test.test_support.TESTFN, 'w')
+ try:
+ obj = _lsprof.Profiler(lambda: int)
+ obj.enable()
+ obj = _lsprof.Profiler(1)
+ obj.disable()
+ finally:
+ sys.stderr = sys.__stderr__
+ test.test_support.unlink(test.test_support.TESTFN)
+
+
if __name__ == "__main__":
test_main()
diff --git a/Misc/NEWS b/Misc/NEWS
index 9ca2feb..a6ad102 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -92,6 +92,9 @@
Library
-------
+- Issue #3895: _lsprof could be crashed with an external timer that did not
+ return a float when a Profiler object is garbage collected.
+
- Issues #3968 and #3969: two minor turtle problems.
- Issue #3547: Fixed ctypes structures bitfields of varying integer
diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c
index 7eb9682..630b043 100644
--- a/Modules/_lsprof.c
+++ b/Modules/_lsprof.c
@@ -150,7 +150,16 @@
}
Py_DECREF(o);
if (PyErr_Occurred()) {
- PyErr_WriteUnraisable((PyObject *) pObj);
+ PyObject *context = (PyObject *)pObj;
+ /* May have been called by profiler_dealloc(). */
+ if (context->ob_refcnt < 1) {
+ context = PyString_FromString("profiler calling an "
+ "external timer");
+ if (context == NULL) {
+ return 0;
+ }
+ }
+ PyErr_WriteUnraisable(context);
return 0;
}
return result;