-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Starting new thread during finalization leads to call to PyMem_Free
without holding the GIL
#109795
Comments
thread_PyThread_start_new_thread() can easily use PyMem_RawMalloc() instead of PyMem_Malloc(). Do you want to propose a fix? |
@vstinner, yes, I'll submit a PR shortly :) |
…e using raw memory allocator (python#109808) (cherry picked from commit 1b8f236)
Ah. After many attempts, I managed to reproduce the bug without the fix:
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:
|
Thanks for the reproducer @chgnrdv! The bug is hard to trigger :-( But apparently, I cannot reproduce the bug anymore. Good :-) |
…te usin… (#109852) gh-109795: `_thread.start_new_thread`: allocate thread bootstate using raw memory allocator (#109808) (cherry picked from commit 1b8f236) Co-authored-by: Radislav Chugunov <[email protected]>
@vstinner thank you for review :) Yep, it takes precise timing for interrupt to make Python finalize before new thread will be started in |
…e using raw memory allocator (python#109808)
…e using raw memory allocator (python#109808) (cherry picked from commit 1b8f236)
) (#110342) * gh-108987: Fix _thread.start_new_thread() race condition (#109135) Fix _thread.start_new_thread() race condition. If a thread is created during Python finalization, the newly spawned thread now exits immediately instead of trying to access freed memory and lead to a crash. thread_run() calls PyEval_AcquireThread() which checks if the thread must exit. The problem was that tstate was dereferenced earlier in _PyThreadState_Bind() which leads to a crash most of the time. Move _PyThreadState_CheckConsistency() from thread_run() to _PyThreadState_Bind(). (cherry picked from commit 517cd82) * gh-109795: `_thread.start_new_thread`: allocate thread bootstate using raw memory allocator (#109808) (cherry picked from commit 1b8f236) --------- Co-authored-by: Radislav Chugunov <[email protected]>
…e using raw memory allocator (python#109808)
Crash report
What happened?
Since #109135,
thread_run
checks if it is called during finalization, and in this case it frees thread bootstate inthread_bootstate_free
and returns. The problem is thatPyMem_Free
shouldn't be called if GIL is not held.Repro and error message (just in case):
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
_thread.start_new_thread
: allocate thread bootstate using raw memory allocator #109808_thread.start_new_thread
: allocate thread bootstate usin… #109852The text was updated successfully, but these errors were encountered: