Skip to content

Panic during reentrant writelines via TextIOWrapper.reconfigure #6588

@jackfromeast

Description

@jackfromeast

What happened?

_IOBase.writelines calls TextIOWrapper.write, which flushes buffered data by calling the user-provided raw write while the wrapper’s mutex is held. If that raw write re-enters TextIOWrapper.reconfigure (e.g., by calling textio.reconfigure), the inner call attempts self.data.lock().unwrap() on an already-held mutex and panics instead of rejecting the reentrant call or raising a Python exception.

Proof of Concept:

import io

class Raw:
    def __init__(self):
        self.textio = None
        self.closed = False
    def writable(self): return True
    def readable(self): return False
    def seekable(self): return False
    def write(self, data):
        self.textio.reconfigure(encoding="utf-8")
        return len(data)

raw = Raw()
textio = io.TextIOWrapper(raw, encoding="utf-8", write_through=True)
raw.textio = textio
textio.writelines(["x"])
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)] Panic 101
Vulnerable Code
fn writelines(instance: PyObjectRef, lines: ArgIterable, vm: &VirtualMachine) -> PyResult<()> {
    check_closed(&instance, vm)?;
    for line in lines.iter(vm)? {
        vm.call_method(&instance, "write", (line?,))?; // Re-enters TextIOWrapper.write while wrapper lock is held
    }
    Ok(())
}

impl TextIOData {
    fn write_pending(&mut self, vm: &VirtualMachine) -> PyResult<()> {
        if self.pending.num_bytes == 0 {
            return Ok(());
        }
        let data = self.pending.take(vm);
        vm.call_method(&self.buffer, "write", (data,))?; // Calls raw write. User code can call textio.reconfigure reentrantly
        Ok(())
    }
}

#[pymethod]
fn reconfigure(&self, args: TextIOWrapperArgs, vm: &VirtualMachine) -> PyResult<()> {
    let mut data = self.data.lock().unwrap(); // Reentrant lock returns None; unwrap panics instead of raising a Python error
    if let Some(data) = data.as_mut() {
        // ...
    }
    Ok(())
}
Rust Output
thread 'main' panicked at crates/vm/src/stdlib/io.rs:2514:45:
called `Option::unwrap()` on a `None` value
stack backtrace:
   0: __rustc::rust_begin_unwind
             at /rustc/11ad40bb839ca16f74784b4ab72596ad85587298/library/std/src/panicking.rs:697:5
   1: core::panicking::panic_fmt
             at /rustc/11ad40bb839ca16f74784b4ab72596ad85587298/library/core/src/panicking.rs:75:14
   2: core::panicking::panic
             at /rustc/11ad40bb839ca16f74784b4ab72596ad85587298/library/core/src/panicking.rs:145:5
   3: core::option::unwrap_failed
             at /rustc/11ad40bb839ca16f74784b4ab72596ad85587298/library/core/src/option.rs:2072:5
   4: core::option::Option<T>::unwrap
             at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:1005:21
   5: rustpython_vm::stdlib::io::_io::TextIOWrapper::reconfigure
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/stdlib/io.rs:2514:45
   6: core::ops::function::Fn::call
             at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:79:5
   7: rustpython_vm::function::builtin::<impl rustpython_vm::function::builtin::sealed::PyNativeFnInternal<(rustpython_vm::function::builtin::RefParam<S>,rustpython_vm::function::builtin::OwnedParam<T1>),R,rustpython_vm::vm::VirtualMachine> for F>::call_
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:154:17
   8: <F as rustpython_vm::function::builtin::IntoPyNativeFn<(T,R,VM)>>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:92:14
   9: rustpython_vm::function::builtin::into_func::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:50:40
  10: <rustpython_vm::builtins::builtin_func::PyNativeMethod as rustpython_vm::types::slot::Callable>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/builtins/builtin_func.rs:234:9
  11: rustpython_vm::types::slot::Callable::slot_call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/types/slot.rs:1028:9
  12: rustpython_vm::protocol::callable::PyCallable::invoke
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:52:22
  13: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:33:18
  14: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:22:14
  15: rustpython_vm::frame::ExecutingFrame::execute_method_call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:1903:26
  16: rustpython_vm::frame::ExecutingFrame::execute_instruction
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:688:22
  17: rustpython_vm::frame::ExecutingFrame::run
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:372:31
  18: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:247:40
  19: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::with_exec
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:242:9
  20: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:247:14
  21: rustpython_vm::vm::VirtualMachine::run_frame::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:467:44
  22: rustpython_vm::vm::VirtualMachine::with_frame::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:495:26
  23: rustpython_vm::vm::VirtualMachine::with_recursion
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:483:22
  24: rustpython_vm::vm::VirtualMachine::with_frame
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:493:14
  25: rustpython_vm::vm::VirtualMachine::run_frame
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:467:20
  26: rustpython_vm::builtins::function::<impl rustpython_vm::object::core::Py<rustpython_vm::builtins::function::PyFunction>>::invoke_with_locals
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/builtins/function.rs:437:34
  27: rustpython_vm::builtins::function::<impl rustpython_vm::object::core::Py<rustpython_vm::builtins::function::PyFunction>>::invoke
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/builtins/function.rs:443:14
  28: <rustpython_vm::builtins::function::PyFunction as rustpython_vm::types::slot::Callable>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/builtins/function.rs:645:14
  29: rustpython_vm::types::slot::Callable::slot_call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/types/slot.rs:1028:9
  30: rustpython_vm::protocol::callable::PyCallable::invoke
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:52:22
  31: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:33:18
  32: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:22:14
  33: <rustpython_vm::builtins::function::PyBoundMethod as rustpython_vm::types::slot::Callable>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/builtins/function.rs:735:23
  34: rustpython_vm::types::slot::Callable::slot_call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/types/slot.rs:1028:9
  35: rustpython_vm::protocol::callable::PyCallable::invoke
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:52:22
  36: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:33:18
  37: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:22:14
  38: rustpython_vm::vm::method::PyMethod::invoke
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/method.rs:127:14
  39: rustpython_vm::vm::vm_object::<impl rustpython_vm::vm::VirtualMachine>::call_method
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/vm_object.rs:130:52
  40: rustpython_vm::stdlib::io::_io::TextIOData::write_pending
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/stdlib/io.rs:3246:16
  41: rustpython_vm::stdlib::io::_io::TextIOWrapper::write
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/stdlib/io.rs:2982:24
  42: core::ops::function::Fn::call
             at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:79:5
  43: rustpython_vm::function::builtin::<impl rustpython_vm::function::builtin::sealed::PyNativeFnInternal<(rustpython_vm::function::builtin::RefParam<S>,rustpython_vm::function::builtin::OwnedParam<T1>),R,rustpython_vm::vm::VirtualMachine> for F>::call_
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:154:17
  44: <F as rustpython_vm::function::builtin::IntoPyNativeFn<(T,R,VM)>>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:92:14
  45: rustpython_vm::function::builtin::into_func::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:50:40
  46: <rustpython_vm::builtins::descriptor::PyMethodDescriptor as rustpython_vm::types::slot::Callable>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/builtins/descriptor.rs:97:9
  47: rustpython_vm::types::slot::Callable::slot_call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/types/slot.rs:1028:9
  48: rustpython_vm::protocol::callable::PyCallable::invoke
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:52:22
  49: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:33:18
  50: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:22:14
  51: rustpython_vm::vm::method::PyMethod::invoke
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/method.rs:127:14
  52: rustpython_vm::vm::vm_object::<impl rustpython_vm::vm::VirtualMachine>::call_method
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/vm_object.rs:130:52
  53: rustpython_vm::stdlib::io::_io::_IOBase::writelines
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/stdlib/io.rs:567:20
  54: core::ops::function::Fn::call
             at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:79:5
  55: rustpython_vm::function::builtin::<impl rustpython_vm::function::builtin::sealed::PyNativeFnInternal<(rustpython_vm::function::builtin::OwnedParam<T1>,rustpython_vm::function::builtin::OwnedParam<T2>),R,rustpython_vm::vm::VirtualMachine> for F>::call_
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:126:17
  56: <F as rustpython_vm::function::builtin::IntoPyNativeFn<(T,R,VM)>>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:92:14
  57: rustpython_vm::function::builtin::into_func::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/function/builtin.rs:50:40
  58: <rustpython_vm::builtins::builtin_func::PyNativeMethod as rustpython_vm::types::slot::Callable>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/builtins/builtin_func.rs:234:9
  59: rustpython_vm::types::slot::Callable::slot_call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/types/slot.rs:1028:9
  60: rustpython_vm::protocol::callable::PyCallable::invoke
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:52:22
  61: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call_with_args
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:33:18
  62: rustpython_vm::protocol::callable::<impl rustpython_vm::object::core::PyObject>::call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/protocol/callable.rs:22:14
  63: rustpython_vm::frame::ExecutingFrame::execute_method_call
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:1903:26
  64: rustpython_vm::frame::ExecutingFrame::execute_instruction
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:692:22
  65: rustpython_vm::frame::ExecutingFrame::run
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:372:31
  66: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:247:40
  67: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::with_exec
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:242:9
  68: rustpython_vm::frame::<impl rustpython_vm::object::core::Py<rustpython_vm::frame::Frame>>::run
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/frame.rs:247:14
  69: rustpython_vm::vm::VirtualMachine::run_frame::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:467:44
  70: rustpython_vm::vm::VirtualMachine::with_frame::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:495:26
  71: rustpython_vm::vm::VirtualMachine::with_recursion
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:483:22
  72: rustpython_vm::vm::VirtualMachine::with_frame
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:493:14
  73: rustpython_vm::vm::VirtualMachine::run_frame
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:467:20
  74: rustpython_vm::vm::VirtualMachine::run_code_obj
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/mod.rs:442:14
  75: rustpython_vm::vm::compile::<impl rustpython_vm::vm::VirtualMachine>::run_simple_file
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/compile.rs:93:26
  76: rustpython_vm::vm::compile::<impl rustpython_vm::vm::VirtualMachine>::run_any_file
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/compile.rs:57:14
  77: rustpython_vm::vm::compile::<impl rustpython_vm::vm::VirtualMachine>::run_script
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/compile.rs:50:14
  78: rustpython::run_rustpython
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/src/lib.rs:231:16
  79: rustpython::run::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/src/lib.rs:112:41
  80: rustpython_vm::vm::interpreter::Interpreter::run::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/interpreter.rs:103:35
  81: rustpython_vm::vm::interpreter::Interpreter::enter::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/interpreter.rs:72:39
  82: scoped_tls::ScopedKey<T>::set
             at /home/jackfromeast/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/scoped-tls-1.0.1/src/lib.rs:137:9
  83: rustpython_vm::vm::thread::enter_vm::{{closure}}
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/thread.rs:30:20
  84: std::thread::local::LocalKey<T>::try_with
             at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:315:12
  85: std::thread::local::LocalKey<T>::with
             at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/local.rs:279:20
  86: rustpython_vm::vm::thread::enter_vm
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/thread.rs:27:14
  87: rustpython_vm::vm::interpreter::Interpreter::enter
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/interpreter.rs:72:9
  88: rustpython_vm::vm::interpreter::Interpreter::run
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/crates/vm/src/vm/interpreter.rs:103:24
  89: rustpython::run
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/src/lib.rs:112:27
  90: rustpython::main
             at /home/jackfromeast/Desktop/entropy/targets/rustpython/RustPython/src/main.rs:2:5
  91: core::ops::function::FnOnce::call_once
             at /home/jackfromeast/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
CPython Output
Traceback (most recent call last):
  File "<string>", line 17, in <module>
  File "<string>", line 11, in write
AttributeError: 'Raw' object has no attribute 'flush'

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