-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Open
Description
What happened?
array.tofile holds a read lock on the array buffer while calling the user-provided file object's write method. If that write re-enters and mutates the array (e.g., append), it tries to take a write lock that is blocked by the outstanding read lock, causing the interpreter to hang.
Proof of Concept:
import array
class Writer:
def __init__(self, arr):
self.arr = arr
self.reentered = False
def write(self, chunk):
if not self.reentered:
self.reentered = True
self.arr.append(0)
return len(chunk)
arr = array.array('b', range(128))
arr.tofile(Writer(arr))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 tofile(&self, f: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
const BLOCKSIZE: usize = 64 * 1024;
let bytes = self.read(); // Holds the array read lock for the whole write
let bytes = bytes.get_bytes();
for b in bytes.chunks(BLOCKSIZE) {
let b = PyBytes::from(b.to_vec()).into_ref(&vm.ctx);
vm.call_method(&f, "write", (b,))?; // Re-enters Python; user `write` may mutate the same array
}
Ok(())
}
#[pymethod]
fn append(zelf: &Py<Self>, x: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
zelf.try_resizable(vm)?.push(x, vm) // Requires a write lock while `tofile` still holds the read lock
}
fn try_resizable_opt(&self) -> Option<PyRwLockWriteGuard<'_, ArrayContentType>> {
let w = self.write(); // Blocks forever when `append` runs during `tofile`'s read lock
(self.exports.load(atomic::Ordering::SeqCst) == 0).then_some(w)
}Rust Output
Program hangs forever
CPython Output
(No output)
coderabbitai
Metadata
Metadata
Assignees
Labels
No labels