From da68262c59201e3e83c045d3d22d9a2b7f9bb767 Mon Sep 17 00:00:00 2001 From: raminfp Date: Thu, 12 Mar 2026 11:44:34 +0330 Subject: [PATCH 1/2] gh-145846: Fix memory leak in _lsprof clearEntries() context chain 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. --- .../test_profiling/test_tracing_profiler.py | 22 +++++++++++++++++++ ...-03-12-00-00-00.gh-issue-145846.UbHxjv.rst | 3 +++ Modules/_lsprof.c | 7 +++--- 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-03-12-00-00-00.gh-issue-145846.UbHxjv.rst diff --git a/Lib/test/test_profiling/test_tracing_profiler.py b/Lib/test/test_profiling/test_tracing_profiler.py index d09ca441d4ae46..a00b4e6a747bc8 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) + + p = _lsprof.Profiler() + p.enable() + for _ in range(100): + level1(p) + p.disable() + p.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..869899fd175bd9 --- /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 :mod:`_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 a2d1aefb1611b3..4f2a545a09382a 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 *c = pObj->currentProfilerContext; + pObj->currentProfilerContext = c->previous; + PyMem_Free(c); } while (pObj->freelistProfilerContext) { ProfilerContext *c = pObj->freelistProfilerContext; From 389e3690456dbf769a28e877978b276d3424c678 Mon Sep 17 00:00:00 2001 From: raminfp Date: Thu, 12 Mar 2026 11:49:49 +0330 Subject: [PATCH 2/2] gh-145846: Fix NEWS entry to use double backticks for _lsprof --- .../next/Library/2026-03-12-00-00-00.gh-issue-145846.UbHxjv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 869899fd175bd9..63cdb7686da998 100644 --- 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 @@ -1,3 +1,3 @@ -Fix memory leak in :mod:`_lsprof` when ``clear()`` is called during active +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.