◐ Shell
reader mode source ↗
Skip to content

Panic during reentrant writelines via TextIOWrapper.reconfigure #6588

New issue
New issue
@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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions