Skip to content

Frame teardown can create frame objects #99729

Description

@graingert

Crash report

using https://github.com/graingert/segfault-repro running pytest yields a segfault in about 1 in 3 runs

================================================================================================================================== test session starts ===================================================================================================================================
platform linux -- Python 3.11.0, pytest-7.2.0, pluggy-1.0.0
rootdir: /home/graingert/projects/segfault-repro, configfile: pyproject.toml
collected 1 item                                                                                                                                                                                                                                                                         

test_ssltransport.py Fatal Python error: Segmentation fault

Current thread 0x00007f874b2df000 (most recent call first):
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/unraisableexception.py", line 43 in _hook
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/python.py", line 195 in pytest_pyfunc_call
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_callers.py", line 39 in _multicall
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_manager.py", line 80 in _hookexec
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_hooks.py", line 265 in __call__
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/python.py", line 1789 in runtest
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/runner.py", line 167 in pytest_runtest_call
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_callers.py", line 39 in _multicall
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_manager.py", line 80 in _hookexec
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_hooks.py", line 265 in __call__
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/runner.py", line 260 in <lambda>
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/runner.py", line 339 in from_call
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/runner.py", line 259 in call_runtest_hook
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/runner.py", line 220 in call_and_report
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/runner.py", line 131 in runtestprotocol
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/runner.py", line 112 in pytest_runtest_protocol
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_callers.py", line 39 in _multicall
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_manager.py", line 80 in _hookexec
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_hooks.py", line 265 in __call__
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/main.py", line 349 in pytest_runtestloop
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_callers.py", line 39 in _multicall
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_manager.py", line 80 in _hookexec
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_hooks.py", line 265 in __call__
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/main.py", line 324 in _main
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/main.py", line 270 in wrap_session
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/main.py", line 317 in pytest_cmdline_main
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_callers.py", line 39 in _multicall
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_manager.py", line 80 in _hookexec
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/pluggy/_hooks.py", line 265 in __call__
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/config/__init__.py", line 167 in main
  File "/home/graingert/.virtualenvs/segfault-repro/lib/python3.11/site-packages/_pytest/config/__init__.py", line 190 in console_main
  File "/home/graingert/.virtualenvs/segfault-repro/bin/pytest", line 8 in <module>
[1]    50315 segmentation fault (core dumped)  pytest

Error messages

Enter any relevant error message caused by the crash, including a core dump if there is one.

Your environment

  • CPython versions tested on: Python 3.11.0 (main, Oct 24 2022, 19:56:13) [GCC 11.2.0] on linux
  • Operating system and architecture: 5.15.0-53-generic #59-Ubuntu SMP Mon Oct 17 18:53:30 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Linked PRs

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Nov 23, 2022
  2. graingert commented on Nov 23, 2022

    @graingert
    ContributorAuthor
  3. pablogsal commented on Nov 23, 2022

    @pablogsal
    Member

    This looks like another frame lifetime issue. Here is the stack:

    Thread 1 "python" received signal SIGSEGV, Segmentation fault.
    [Switching to Thread 0x7ffff7cc5740 (LWP 223005)]
    0x00005555556cbc8b in PyFrame_GetCode (frame=<error reading variable: Cannot access memory at address 0x7ffff5e4c210>) at Objects/frameobject.c:1303
    1303    Objects/frameobject.c: Directory not empty.
    (gdb) up
    #1  frame_getcode (f=<error reading variable: Cannot access memory at address 0x7ffff5e4c210>, closure=<optimized out>)
        at Objects/frameobject.c:97
    97      in Objects/frameobject.c
    (gdb)
    #2  0x0000555555702309 in _PyObject_GenericGetAttrWithDict (Python Exception <class 'gdb.error'>: There is no member named f_frame.
    obj=, name='f_code', dict=0x0, suppress=0) at ./Include/object.h:133
    133     ./Include/object.h: Directory not empty.
    (gdb)
    #3  0x0000555555701836 in PyObject_GetAttr (Python Exception <class 'gdb.error'>: There is no member named f_frame.
    v=v@entry=, name='f_code') at Objects/object.c:916
    916     Objects/object.c: Directory not empty.
    (gdb)
    #4  0x00005555556440a1 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>)
        at Python/ceval.c:3466
    3466    Python/ceval.c: Directory not empty.
    (gdb)
    #5  0x00005555556c1bd5 in _PyEval_EvalFrame (throwflag=0, frame=0x7ffff4840330, tstate=0x555555adfa18 <_PyRuntime+166328>)
        at ./Include/internal/pycore_ceval.h:73
    73      ./Include/internal/pycore_ceval.h: Directory not empty.
    (gdb)
    #6  gen_send_ex2 (closing=0, exc=0, presult=<synthetic pointer>, arg=0x0, gen=0x7ffff48402e0) at Objects/genobject.c:219
    219     Objects/genobject.c: Directory not empty.
    (gdb)
    #7  gen_iternext (gen=0x7ffff48402e0) at Objects/genobject.c:594
    
  4. pablogsal commented on Nov 23, 2022

    @pablogsal
    Member
  5. pablogsal commented on Nov 23, 2022

    @pablogsal
    Member

    I can confirm that still happens with 3.11 current.

  6. brandtbucher commented on Nov 23, 2022

    @brandtbucher
    Member

    Thanks, I'll look at this today (Mark's out right now).

  7. self-assigned this
    on Nov 23, 2022
  8. graingert commented on Nov 23, 2022

    @graingert
    ContributorAuthor

    I've now eliminated pytest from my reproducer: graingert/segfault-repro@34b5962

  9. brandtbucher commented on Nov 23, 2022

    @brandtbucher
    Member

    I've now eliminated pytest from my reproducer: graingert/segfault-repro@34b5962

    Oh thank God. You have no idea how helpful this is.

  10. brandtbucher commented on Nov 23, 2022

    @brandtbucher
    Member

    @graingert, how should I run the new reproducer? I'm unable to get 3.11 head to crash with pip install -r requirements.txt; python test_ssltransport.py.

  11. brandtbucher commented on Nov 23, 2022

    @brandtbucher
    Member

    I see the segfault intermittently when running pytest, though.

  12. brandtbucher commented on Nov 23, 2022

    @brandtbucher
    Member

    In the meantime, I'm using pytest to reproduce. I've gotten test_ssltransport.py down to 270 lines, removed __init__.py entirely, and gotten pyproject.toml down to just:

    [tool.pytest.ini_options]
    filterwarnings = ["error"]
  13. brandtbucher commented on Nov 23, 2022

    @brandtbucher
    Member

    Let me know if you want me to open a PR with the simplified reproducers (or, even better, if you want me to just push them).

  14. graingert commented on Nov 23, 2022

    @graingert
    ContributorAuthor

    @graingert, how should I run the new reproducer? I'm unable to get 3.11 head to crash with pip install -r requirements.txt; python test_ssltransport.py.

    You need to run it with python -W error

  15. 18 remaining items

  16. pablogsal commented on Nov 30, 2022

    @pablogsal
    Member

    I still don't undetstand why this doesn't crash:

    import sys
    
    class DelRaises:
        def __del__(self):
            global sneaky
            sneaky = sys._getframe()
    
    def run():
        _ = DelRaises()
    
    print("Running...")
    run()
    print("Crashing...")
    

    Unfortunately, I am currently dealing with a health issue so I could not find time to deal with this in detail :(

  17. brandtbucher commented on Nov 30, 2022

    @brandtbucher
    Member

    Sorry, to hear that. :(

    I was able to figure this out yesterday, but was waiting to share in the meeting. I’ll post here in case you can’t come:

    I was able to get a single-threaded version to crash, too. It’s just easier to do with multiple threads since the frame object has a stale pointer into the thread’s frame stack. In our threaded example, after the thread finishes, the frame stack ceases to exist, so things can go south pretty quickly. When single-threaded, we just have a pointer into an old part of the current chunk, so things appear valid for longer if you don’t try to do much.

    I’m on my phone now, but if I remember correctly, doing something like sneaky.f_back.f_locals after the run() makes the single-threaded version crash.

    Basically, we should mark this frame as “incomplete” (or just unlink it, not sure yet which is easier) after checking for existing frame objects, but before clearing stuff. This restores the behavior of 3.10, which made it look like __del__ was called from run’s caller, not from run itself.

    I’ll have a PR up for review this week.

  18. pablogsal commented on Dec 3, 2022

    @pablogsal
    Member

    Marking this as 3.11 release blocker

  19. added 2 commits that reference this issue on Dec 6, 2022
  20. pablogsal commented on Dec 6, 2022

    @pablogsal
    Member

    PRs merged so I am closing the issue. Thanks a lot to everyone that participated in this bug, from identifying it to fixing it. You all rock 🤘

  21. Repository owner moved this from Todo to Done in Release and Deferred blockers 🚫on Dec 6, 2022
  22. brandtbucher commented on Dec 6, 2022

    @brandtbucher
    Member

    I've confirmed that the 3.11 patch fixes the full original reproducer, as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

3.11only security fixes3.12only security fixestype-crashA hard crash of the interpreter, possibly with a core dump

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions