https://github.com/python/cpython/commit/957b2cca8e7dc86db873de6291767c41e57c1504
commit: 957b2cca8e7dc86db873de6291767c41e57c1504
branch: main
author: Ramin Farajpour Cami <[email protected]>
committer: vstinner <[email protected]>
date: 2026-04-07T22:56:19+02:00
summary:

gh-145846: Fix memory leak in _lsprof clearEntries() context chain (#145847)

clearEntries() only freed the top currentProfilerContext but did not
walk the previous linked list. When clear() is called during active
profiling with nested calls, all contexts except the top one were
leaked. Fix by iterating the entire linked list, matching the existing
freelistProfilerContext cleanup pattern.

Co-authored-by: Victor Stinner <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-03-12-00-00-00.gh-issue-145846.UbHxjv.rst
M Lib/test/test_profiling/test_tracing_profiler.py
M Modules/_lsprof.c

diff --git a/Lib/test/test_profiling/test_tracing_profiler.py 
b/Lib/test/test_profiling/test_tracing_profiler.py
index d09ca441d4ae46..3668e24e073ce4 100644
--- a/Lib/test/test_profiling/test_tracing_profiler.py
+++ b/Lib/test/test_profiling/test_tracing_profiler.py
@@ -142,6 +142,28 @@ def gen():
 
         self.assertTrue(any("throw" in func[2] for func in pr.stats.keys())),
 
+    def test_clear_with_nested_calls(self):
+        # Calling clear() during nested profiled calls should not leak
+        # ProfilerContexts. clearEntries() must walk the entire linked list,
+        # not just free the top context.
+        import _lsprof
+
+        def level3(profiler):
+            profiler.clear()
+
+        def level2(profiler):
+            level3(profiler)
+
+        def level1(profiler):
+            level2(profiler)
+
+        profiler = _lsprof.Profiler()
+        profiler.enable()
+        for _ in range(100):
+            level1(profiler)
+        profiler.disable()
+        profiler.clear()
+
     def test_bad_descriptor(self):
         # gh-132250
         # cProfile should not crash when the profiler callback fails to locate
diff --git 
a/Misc/NEWS.d/next/Library/2026-03-12-00-00-00.gh-issue-145846.UbHxjv.rst 
b/Misc/NEWS.d/next/Library/2026-03-12-00-00-00.gh-issue-145846.UbHxjv.rst
new file mode 100644
index 00000000000000..63cdb7686da998
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-12-00-00-00.gh-issue-145846.UbHxjv.rst
@@ -0,0 +1,3 @@
+Fix memory leak in ``_lsprof`` when ``clear()`` is called during active
+profiling with nested calls. ``clearEntries()`` now walks the entire
+``currentProfilerContext`` linked list instead of only freeing the top context.
diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c
index abb8db1acabbd5..3d341a336ab22e 100644
--- a/Modules/_lsprof.c
+++ b/Modules/_lsprof.c
@@ -292,9 +292,10 @@ static void clearEntries(ProfilerObject *pObj)
     RotatingTree_Enum(pObj->profilerEntries, freeEntry, NULL);
     pObj->profilerEntries = EMPTY_ROTATING_TREE;
     /* release the memory hold by the ProfilerContexts */
-    if (pObj->currentProfilerContext) {
-        PyMem_Free(pObj->currentProfilerContext);
-        pObj->currentProfilerContext = NULL;
+    while (pObj->currentProfilerContext) {
+        ProfilerContext *pContext = pObj->currentProfilerContext;
+        pObj->currentProfilerContext = pContext->previous;
+        PyMem_Free(pContext);
     }
     while (pObj->freelistProfilerContext) {
         ProfilerContext *c = pObj->freelistProfilerContext;

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to