python / python/cpython

itertools.count use-after-free via re-entrant step.__radd__

未关闭
#154,670 6 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

extension-modules type-crash
主要语言
Python
星标
77.2k
派生
35.9k
PR 合并指标
PR 指标待抓取

描述

Bug description

itertools.count has a use-after-free when the step object's __radd__ re-enters the iterator.

count_nextlong() borrows lz->long_cnt (the running total) without Py_INCREF, then calls PyNumber_Add(result, step). If step.__radd__ calls next() on the same count, the inner call moves lz->long_cnt to the new value and hands the old total's only reference back, which is then dropped and freed. The outer call still returns that freed total as a dangling pointer.

The step needs __index__ so count() accepts it as a number, and the returned value has to be used to hit the freed memory:

from itertools import count

class Step:
    armed = True
    def __index__(self):        # so count() accepts it as a number
        return 1
    def __radd__(self, other):
        if Step.armed:
            Step.armed = False
            inner = next(c)     # re-enter; steals the running total's ref
            f"{inner!r}"        # churn the heap so the freed slot is reused
        return other + 1

c = count(1 << 100, Step())
val = next(c)
val + 1                         # use the returned (dangling) total

Run with PYTHONMALLOC=debug python repro.py -> SIGSEGV. With the fix it prints the correct value.

CPython versions tested on

main

Operating systems tested on

macOS

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

首先,跟踪报告中描述的 count_nextlong() 路径,并使用 PYTHONMALLOC=debug 运行提供的 reproducer。调查可重入的 next() 调用以及运行总计周围的引用生命周期;当 reproducer 在不崩溃的情况下打印出正确值时,工作即完成。

由索引模型根据 Issue 内容生成。

评估

技术栈
c, python
领域
backend
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
冷清
描述清晰度
基本清楚
新手友好度
52/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。