python / python/cpython

Memory leak on interpreter shutdown when reference cycle exists between `structseq` type and its instance

Open
#157,176 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

3.14 3.15 3.16 type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:
Environment
  • Commit/Branch: main (rev: 2d9fb5f2f70b18ce9e5d9f7f76fd5ef3f118d838)
  • OS: Linux
  • Build flags:
    CC=clang CXX=clang++ LDFLAGS='-fuse-ld=lld' ./configure --with-address-sanitizer --with-undefined-behavior-sanitizer --with-pydebug && make -j$(nproc)
    
Steps to reproduce

Run:

./python -c "import time; t = time.gmtime(); type(t).refcyle = t;"

Note: Explicitly breaking the cycle avoids the leak:

./python -c "import time; t = time.gmtime(); type(t).refcyle = t; del type(t).refcyle"
Expected behavior

The garbage collector or interpreter finalization breaks the reference cycle during shutdown; no memory leaks reported by LeakSanitizer.

Actual behavior

LeakSanitizer detects memory leaks (indirect leaks of the type, its dict, descriptors, and the instance):

=================================================================
==1164172==ERROR: LeakSanitizer: detected memory leaks

Indirect leak of 1472 byte(s) in 1 object(s) allocated from:
    #0 0x55571ba2f651 in malloc
    #1 0x55571c0c8db7 in _PyMem_DebugRawAlloc Objects/obmalloc.c:3103:24
    #2 0x55571c130728 in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46:17
    #3 0x55571c130728 in _PyType_AllocNoTrack Objects/typeobject.c:2495:19
    #4 0x55571c13040d in PyType_GenericAlloc Objects/typeobject.c:2526:21
    #5 0x55571c1357f9 in type_from_slots_or_spec Objects/typeobject.c:5591:30
    #6 0x55571c1148ec in _PyStructSequence_NewType Objects/structseq.c:780:28
    #7 0x55571ca6ea5c in time_exec Modules/timemodule.c:2113:31
...
Indirect leak of 176 byte(s) in 1 object(s) allocated from:
    #0 0x55571ba2f651 in malloc
    #1 0x55571c0c8db7 in _PyMem_DebugRawAlloc Objects/obmalloc.c:3103:24
    #2 0x55571c5d4028 in _PyObject_MallocWithType Include/internal/pycore_object_alloc.h:46:17
    #3 0x55571c5d4028 in gc_alloc Python/gc.c:2013:17
    #4 0x55571c5d4440 in _PyObject_GC_NewVar Python/gc.c:2055:25
    #5 0x55571c110247 in PyStructSequence_New Objects/structseq.c:77:11
    #6 0x55571ca69e1d in tmtotuple Modules/timemodule.c:463:19
    #7 0x55571ca67616 in time_gmtime Modules/timemodule.c:541:12
...
SUMMARY: AddressSanitizer: 6946 byte(s) leaked in 50 allocation(s).

Full LeakSanitizer output - logs.txt

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-157179

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reproducer under the ASan/UBSan pydebug build, then inspect Objects/structseq.c and the allocation paths in Objects/typeobject.c, Python/gc.c, and Modules/timemodule.c. Compare the behavior with linked PR gh-157179. Done means the cycle no longer produces LeakSanitizer leaks during interpreter shutdown.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.