]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-145846: Fix memory leak in _lsprof clearEntries() context chain (#145847)
authorRamin Farajpour Cami <ramin.blackhat@gmail.com>
Tue, 7 Apr 2026 20:56:19 +0000 (00:26 +0330)
committerGitHub <noreply@github.com>
Tue, 7 Apr 2026 20:56:19 +0000 (22:56 +0200)
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 <vstinner@python.org>
Lib/test/test_profiling/test_tracing_profiler.py
Misc/NEWS.d/next/Library/2026-03-12-00-00-00.gh-issue-145846.UbHxjv.rst [new file with mode: 0644]
Modules/_lsprof.c

index d09ca441d4ae46e87c8dc9d7f749e2a9e81a7d36..3668e24e073ce4daab34dfc0c6f180acd3c8d8af 100644 (file)
@@ -142,6 +142,28 @@ class CProfileTest(ProfileTest):
 
         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 (file)
index 0000000..63cdb76
--- /dev/null
@@ -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.
index abb8db1acabbd5ef01bc52e74da517bbb0402952..3d341a336ab22e622c5fa0b7a0a208a886098642 100644 (file)
@@ -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;