GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Starting new thread during finalization leads to call to `PyMem_Free` without holding the GIL · Issue #109795 · python/cpython

Starting new thread during finalization leads to call to PyMem_Free without holding the GIL #109795

New issue
Copy link
New issue
Copy link
Closed
#109808
Closed
Starting new thread during finalization leads to call to PyMem_Free without holding the GIL#109795
#109808
Copy link
Labels
type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump

Description

@chgnrdv
chgnrdv
opened on Sep 23, 2023
Issue body actions

Crash report

What happened?

Since #109135, thread_run checks if it is called during finalization, and in this case it frees thread bootstate in thread_bootstate_free and returns. The problem is that PyMem_Free shouldn't be called if GIL is not held.

Repro and error message (just in case):

import os
import random
import signal
import subprocess
import sys
import time

script = """
import threading
while True:
    t = threading.Thread()
    t.start()
    t.join()
"""

while True:
    p = subprocess.Popen([sys.executable, '-c', script], stderr=subprocess.PIPE)
    time.sleep(random.random())
    os.kill(p.pid, signal.SIGINT)
    _, err = p.communicate()
    if p.returncode == -signal.SIGABRT:
        print(err.decode('utf-8'))
        break
Traceback (most recent call last):
  File "<string>", line 5, in <module>
  File "/home/radislav/projects/cpython/Lib/threading.py", line 983, in start
    self._started.wait()
  File "/home/radislav/projects/cpython/Lib/threading.py", line 641, in wait
    signaled = self._cond.wait(timeout)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/radislav/projects/cpython/Lib/threading.py", line 341, in wait
    waiter.acquire()
KeyboardInterrupt
Fatal Python error: _PyMem_DebugFree: Python memory allocator called without holding the GIL
Python runtime state: finalizing (tstate=0x0000561b61b67338)

Thread 0x00007f8f890b8280 (most recent call first):
  <no Python frame>

cc @vstinner

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.13.0a0 (heads/main:3e8fcb7df7, Sep 23 2023, 01:43:45) [GCC 10.2.1 20210110]

Linked PRs

  • gh-109795: _thread.start_new_thread: allocate thread bootstate using raw memory allocator #109808
  • [3.11] gh-109795: _thread.start_new_thread: allocate thread bootstate usin… #109852

Activity

  1. chgnrdv
    added
    type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
    on Sep 23, 2023
  2. vstinner commented on Sep 24, 2023

    @vstinner
    vstinner
    on Sep 24, 2023
    Member
    More actions

    thread_PyThread_start_new_thread() can easily use PyMem_RawMalloc() instead of PyMem_Malloc(). Do you want to propose a fix?

  3. chgnrdv commented on Sep 24, 2023

    @chgnrdv
    chgnrdv
    on Sep 24, 2023
    ContributorAuthor
    More actions

    @vstinner, yes, I'll submit a PR shortly :)

  4. vstinner
    closed this as completedin #109808on Sep 25, 2023
  5. vstinner
    added 2 commits that reference this issue on Sep 25, 2023

    gh-109795: `_thread.start_new_thread`: allocate thread bootstate usin…

    1b8f236

    pythongh-109795: `_thread.start_new_thread`: allocate thread bootstat…

    75e36a4
  6. vstinner commented on Sep 25, 2023

    @vstinner
    vstinner
    on Sep 25, 2023
    Member
    More actions

    Ah. After many attempts, I managed to reproduce the bug without the fix:

    $ ./python repro.py 
    ...............failed with exit code -6
    Fatal Python error: _PyMem_DebugFree: Python memory allocator called without holding the GIL
    Python runtime state: finalizing (tstate=0x0000000000b062f8)
    
    Thread 0x00007fd52873a740 (most recent call first):
      <no Python frame>
    

    I modified the reproducer script:

    import os
    import random
    import signal
    import subprocess
    import sys
    import time
    
    script = """
    try:
        import threading
        while True:
            t = threading.Thread()
            t.start()
            t.join()
    except KeyboardInterrupt:
        pass
    """
    
    line = 0
    
    while True:
        if line % 40 == 0:
            sys.stdout.write(f"\n{line} ")
        sys.stdout.write(".")
        sys.stdout.flush()
        line += 1
    
        p = subprocess.Popen([sys.executable, '-c', script], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        time.sleep(0.10 + random.random()*0.5)
        os.kill(p.pid, signal.SIGINT)
        with p:
            try:
                out = p.communicate(timeout=1.0)[0]
            except subprocess.TimeoutExpired:
                sys.stdout.write("*** timeout ***")
                sys.stdout.flush()
                p.kill()
                continue
        if p.returncode:
            print(f"failed with exit code {p.returncode}")
            print(out.decode('utf-8'))
            break

    Sometimes, I get other bugs like:

    $ ./python repro.py 
    
    0 ...................................failed with exit code 1
    Traceback (most recent call last):
      File "/home/vstinner/python/main/Lib/threading.py", line 641, in wait
        signaled = self._cond.wait(timeout)
                   ^^^^^^^^^^^^^^^^^^^^^^^^
      File "/home/vstinner/python/main/Lib/threading.py", line 337, in wait
        saved_state = self._release_save()
                      ^^^^^^^^^^^^^^^^^^^^
      File "/home/vstinner/python/main/Lib/threading.py", line 295, in _release_save
        self._lock.release()           # No state to save
        ^^^^^^^^^^^^^^^^^^^^
    KeyboardInterrupt
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "<string>", line 6, in <module>
      File "/home/vstinner/python/main/Lib/threading.py", line 983, in start
        self._started.wait()
      File "/home/vstinner/python/main/Lib/threading.py", line 638, in wait
        with self._cond:
      File "/home/vstinner/python/main/Lib/threading.py", line 289, in __exit__
        return self._lock.__exit__(*args)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
    RuntimeError: release unlocked lock
    
  7. vstinner commented on Sep 25, 2023

    @vstinner
    vstinner
    on Sep 25, 2023
    Member
    More actions

    Thanks for the reproducer @chgnrdv!

    The bug is hard to trigger :-( But apparently, I cannot reproduce the bug anymore. Good :-)

  8. vstinner
    added a commit that references this issue on Sep 25, 2023

    [3.11] gh-109795: `_thread.start_new_thread`: allocate thread bootsta…

    0afae89
  9. chgnrdv commented on Sep 25, 2023

    @chgnrdv
    chgnrdv
    on Sep 25, 2023
    ContributorAuthor
    More actions

    @vstinner thank you for review :)

    Yep, it takes precise timing for interrupt to make Python finalize before new thread will be started in thread_PyThread_start_new_thread. The repro I attached is fittable for my machine, but may not work on others.

    Reacted by Victor Stinner
  10. csm10495
    added a commit that references this issue on Sep 28, 2023

    pythongh-109795: `_thread.start_new_thread`: allocate thread bootstat…

    f3d62b3
  11. vstinner
    added 2 commits that reference this issue on Oct 4, 2023

    pythongh-109795: `_thread.start_new_thread`: allocate thread bootstat…

    b4da602

    [3.12] gh-108987: Fix _thread.start_new_thread() race condition (#109135

    4936fa9
  12. Glyphack
    added a commit that references this issue on Sep 2, 2024

    pythongh-109795: `_thread.start_new_thread`: allocate thread bootstat…

    c709195
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

    type-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402520 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.