Skip to content

Deadlock when sentinel equality re-enters iterator via iter(callable, sentinel) #6589

@jackfromeast

Description

@jackfromeast

What happened?

PyCallableIterator::next keeps an upgradable read lock while comparing the callable result to the sentinel with vm.bool_eq. If the sentinel’s __eq__ calls next() on the same iterator (as in the PoC), the re-entrant next blocks on the same lock, hanging the interpreter instead of finishing iteration.

Proof of Concept:

class Evil:
    def __init__(self):
        self.it = None
    def __eq__(self, other):
        next(self.it)
        return True

evil = Evil()
itr = iter(lambda: evil, evil)
evil.it = itr
next(itr)
Affected Versions
RustPython Version Status Exit Code
Python 3.13.0alpha (heads/main-dirty:21300f689, Dec 13 2025, 22:16:49) [RustPython 0.4.0 with rustc 1.90.0-nightly (11ad40bb8 2025-06-28)] Deadlock 124
Vulnerable Code
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyIterReturn> {
    let status = zelf.status.upgradable_read(); // holds the lock across user comparisons
    let next = if let IterStatus::Active(callable) = &*status {
        let ret = callable.invoke((), vm)?; // user callable returns a value that may be sentinel
        if vm.bool_eq(&ret, &zelf.sentinel)? { // runs sentinel.__eq__, which can call next() again
            *PyRwLockUpgradableReadGuard::upgrade(status) = IterStatus::Exhausted;
            PyIterReturn::StopIteration(None)
        } else {
            PyIterReturn::Return(ret)
        }
    } else {
        PyIterReturn::StopIteration(None)
    };
    Ok(next) // re-entrant next() blocks on the upgradable_read above, causing deadlock
}
Rust Output
Error: Execution timed out
CPython Output
Traceback (most recent call last):
  File "<string>", line 11, in <module>
StopIteration

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions