Skip to content

linecache.checkcache() is not threadsafe or GC finalizer re-entrancy safe #126775

@graingert

Description

@graingert
Contributor

Bug report

Bug description:

import linecache
import weakref


def gen_func(n):
    func_code = """
def func():
    pass
"""
    g = {}
    exec(func_code, g, g)
    func = g['func']

    filename = f"<generated-{n}>"
    linecache.cache[filename] = (len(func_code), None, func_code.splitlines(True), filename)

    def cleanup_linecache(filename):
        def _cleanup():
            if filename in linecache.cache:
                del linecache.cache[filename]
        return _cleanup

    weakref.finalize(func, cleanup_linecache(filename))

    return func

def main():
    n = 0
    while True:
        func = gen_func(n)
        del func
        linecache.checkcache()
        n += 1
        if n % 100000 == 0:
            print(n)

if __name__ == '__main__':
    main()

This crashes with a KeyError every time for me on 3.12 and 3.13, but some people report that it never crashes on CPython:

 ✘  graingert@conscientious  ~/projects/weakref-func-cycle-never-gc   main  python demo.py
Traceback (most recent call last):
  File "/home/graingert/projects/weakref-func-cycle-never-gc/demo.py", line 38, in <module>
    main()
  File "/home/graingert/projects/weakref-func-cycle-never-gc/demo.py", line 32, in main
    linecache.checkcache()
  File "/usr/lib/python3.12/linecache.py", line 64, in checkcache
    entry = cache[filename]
            ~~~~~^^^^^^^^^^
KeyError: '<generated-147>'
 ✘  graingert@conscientious  ~/projects/weakref-func-cycle-never-gc   main  phyt 
 ✘  graingert@conscientious  ~/projects/weakref-func-cycle-never-gc   main  python3.13 demo.py 
Traceback (most recent call last):
  File "/home/graingert/projects/weakref-func-cycle-never-gc/demo.py", line 38, in <module>
    main()
    ~~~~^^
  File "/home/graingert/projects/weakref-func-cycle-never-gc/demo.py", line 32, in main
    linecache.checkcache()
    ~~~~~~~~~~~~~~~~~~~~^^
  File "/usr/lib/python3.13/linecache.py", line 59, in checkcache
    entry = cache[filename]
            ~~~~~^^^^^^^^^^
KeyError: '<generated-2637>'

The script seems to run "forever" on Python3.9, 3.10 and 3.11 3.14.0a1+ (heads/main:ba088c8f9cf.

CPython versions tested on:

3.12, 3.13

Operating systems tested on:

No response

Linked PRs

important meta issue:

This reproducer absolutely should not reproduce on cpython!!

There does seem to be another issue, because this function should be deleted instantly because its refcount drops to 0, and never run the finalizer during the linecache.checkcache call

Activity

added a commit that references this issue on Nov 13, 2024
dee2dd7
mattip

mattip commented on Nov 13, 2024

@mattip
Contributor

Originally reported on pypy pypy/pypy#5109 and found in SymPy
cc @asmeurer

graingert

graingert commented on Nov 13, 2024

@graingert
ContributorAuthor

oh I totally forgot to link to the pypy issue! I thought I had done that, sorry

added
stdlibStandard Library Python modules in the Lib/ directory
on Nov 14, 2024
added
3.12only security fixes
3.13bugs and security fixes
3.14bugs and security fixes
on Dec 4, 2024
added a commit that references this issue on Dec 10, 2024
2233c30
added a commit that references this issue on Dec 10, 2024
added a commit that references this issue on Dec 10, 2024
added 2 commits that reference this issue on Dec 10, 2024
added a commit that references this issue on Jan 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12only security fixes3.13bugs and security fixes3.14bugs and security fixesstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @graingert@mattip@hugovk@picnixz

        Issue actions

          linecache.checkcache() is not threadsafe or GC finalizer re-entrancy safe · Issue #126775 · python/cpython