Race condition in `itertools.islice` under free-threading
未关闭
还没有人认领这个 Issue。
extension-modules
topic-free-threading
type-bug
- 主要语言
- Python
- 星标
- 77.2k
- 派生
- 36k
- PR 合并指标
- PR 指标待抓取
描述
Bug report
Bug description:
islice_next reads and writes three fields -- lz->cnt, lz->next, and lz->it -- and do operations on them without a critical section.
Two threads calling next on the same islice object concurrently can race:
- Both read
lz->cnt = N < lz->next, both execute the skip-loop body for the same slot, advancing the underlying iterator twice for one step. In that case, the step arithmetic gets corrupted. - Both read
lz->cnt = N < stop, both pass the stop check, both calliternext(it)and return an item -- the total number of yielded items exceedsstop. - There's a race on
lz->cnt++andlz->next += step: both threads can read the same value, then both can increment locally, and both store the same result. After that, the counter is permanently incorrect (such that subsequent skip/stop decisions use a wrong baseline). - One of the threads reaches exhaustion (
goto empty) and callsPy_CLEAR(lz->it), which setslz->it = NULLand DECREF's the iterator, which frees it (since each thread has readyit = lz->it. and not INCREF'd it). Another thread can have readlz->itbefore the clear and be in the middle ofiternext(it)on the now-freed object in which case there is a use-after-free.
chain_next for example wraps its body in Py_BEGIN_CRITICAL_SECTION(op), which I believe is what islice should do as well.
Reproducer
import itertools
import threading
STOP = 100
NTHREADS = 8
data = iter(range(STOP + NTHREADS * 2))
sl = itertools.islice(data, STOP)
results: list[int] = []
lock = threading.Lock()
def consume() -> None:
while True:
v = next(sl, None)
if v is None:
break
with lock:
results.append(v)
threads = [threading.Thread(target=consume) for _ in range(NTHREADS)]
for t in threads: t.start()
for t in threads: t.join()
The reproducer, on a free-threaded build, gets reports like this one.
WARNING: ThreadSanitizer: data race (pid=49886)
Read of size 8 at 0x00030275f8b0 by thread T2:
#0 islice_next itertoolsmodule.c:1663 (python.exe:arm64+0x10042bce4)
#1 builtin_next bltinmodule.c:1770 (python.exe:arm64+0x10028a900)
#2 cfunction_vectorcall_FASTCALL methodobject.c:449 (python.exe:arm64+0x1001379dc)
#3 _PyObject_VectorcallTstate pycore_call.h:144 (python.exe:arm64+0x100090e80)
#4 PyObject_Vectorcall call.c:327 (python.exe:arm64+0x100090e80)
...
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS
Linked PRs
- gh-151410
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 Modules/itertoolsmodule.c 中的 islice_next 开始,并比较 chain_next 对关键区段的处理。使用 free-threaded CPython 构建运行随附的多线程复现程序,并检查相关的 itertools 测试。当并发调用 next() 不再触发报告的竞争条件,也不再超出 islice 的 stop 边界时,即表示完成。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- python
- 领域
- backend
- Issue 类型
- 缺陷
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 活跃度
- 停滞
- 描述清晰度
- 描述清楚
- 新手友好度
- 35/100