-
-
Notifications
You must be signed in to change notification settings - Fork 34.4k
Segfault in _pickle.c:batch_list_exact when pickling list with concurrent mutation (free-threading) #146533
Description
Bug report
Bug description
Same class of bug as gh-146452 but in the list pickling path. batch_list_exact() uses PyList_GET_ITEM() which returns a borrowed reference, and a concurrent list.clear() or mutation can free the backing array before Py_INCREF gets to it.
Found while working on the dict fix in gh-146452 — checked the list path and it has the same pattern.
Reproducer
import pickle
import threading
shared = list(range(200))
for trial in range(2000):
barrier = threading.Barrier(4)
def dumper():
try:
barrier.wait()
pickle.dumps(shared)
except Exception:
pass
def mutator():
try:
barrier.wait()
for j in range(500):
shared.append([j] * 20)
if len(shared) > 100:
shared.clear()
shared.extend(range(50))
except Exception:
pass
threads = [
threading.Thread(target=dumper),
threading.Thread(target=dumper),
threading.Thread(target=mutator),
threading.Thread(target=mutator),
]
for t in threads: t.start()
for t in threads: t.join()Tested on
3.14.0a4 free-threading (stock install, no sanitizers):
$ python3.14t reproducer.py
Segmentation fault (core dumped)
3.15.0a7+ free-threading (built with --with-address-sanitizer):
==2034815==ERROR: AddressSanitizer: SEGV on unknown address 0x00000000000c
#0 _Py_atomic_load_uint32_relaxed Include/cpython/pyatomic_gcc.h:367
#1 Py_INCREF Include/refcount.h:267
#2 batch_list_exact Modules/_pickle.c:3213
#3 save_list Modules/_pickle.c:3268
#4 save Modules/_pickle.c:4595
#5 dump Modules/_pickle.c:4768
#6 _pickle_dumps_impl Modules/_pickle.c:7992
Root cause
batch_list_exact() iterates list items using PyList_GET_ITEM() (borrowed ref) at lines 3196 and 3212. A concurrent list mutation can invalidate the borrowed reference before Py_INCREF. Same pattern as the dict issue in gh-146452.
Possible fix
Same helper function approach as gh-146452 — wrap batch_list_exact in Py_BEGIN_CRITICAL_SECTION(obj) / Py_END_CRITICAL_SECTION(). Happy to send a PR.
CPython versions tested on
3.14, CPython main branch
Operating systems tested on
Linux