Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
check exports in __setstate__
  • Loading branch information
youknowone committed Jan 16, 2026
commit 1fc57afdcc7dd67118c010b813ff50be0f6c7409
39 changes: 20 additions & 19 deletions crates/vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,7 @@ fn type_slot_names(typ: &Py<PyType>, vm: &VirtualMachine) -> PyResult<Option<sup
fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine) -> PyResult {
// Check itemsize
if required && obj.class().slots.itemsize > 0 {
return Err(vm.new_type_error(format!(
"cannot pickle {:.200} objects",
obj.class().name()
)));
return Err(vm.new_type_error(format!("cannot pickle {:.200} objects", obj.class().name())));
}

let state = if obj.dict().is_none_or(|d| d.is_empty()) {
Expand Down Expand Up @@ -237,9 +234,7 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine)

// Fail if actual type's basicsize > expected basicsize
if obj.class().slots.basicsize > basicsize {
return Err(
vm.new_type_error(format!("cannot pickle '{}' object", obj.class().name()))
);
return Err(vm.new_type_error(format!("cannot pickle '{}' object", obj.class().name())));
}
}

Expand Down Expand Up @@ -683,9 +678,7 @@ fn reduce_newobj(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
// Check if type has tp_new
let cls = obj.class();
if cls.slots.new.load().is_none() {
return Err(
vm.new_type_error(format!("cannot pickle '{}' object", cls.name()))
);
return Err(vm.new_type_error(format!("cannot pickle '{}' object", cls.name())));
}

let (args, kwargs) = get_new_arguments(&obj, vm)?;
Expand All @@ -694,13 +687,13 @@ fn reduce_newobj(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {

let has_args = args.is_some();

let (newobj, newargs): (PyObjectRef, PyObjectRef) = if kwargs.is_none() || kwargs.as_ref().is_some_and(|k| k.is_empty()) {
let (newobj, newargs): (PyObjectRef, PyObjectRef) = if kwargs.is_none()
|| kwargs.as_ref().is_some_and(|k| k.is_empty())
{
// Use copyreg.__newobj__
let newobj = copyreg.get_attr("__newobj__", vm)?;

let args_vec: Vec<PyObjectRef> = args
.map(|a| a.as_slice().to_vec())
.unwrap_or_default();
let args_vec: Vec<PyObjectRef> = args.map(|a| a.as_slice().to_vec()).unwrap_or_default();

// Create (cls, *args) tuple
let mut newargs_vec: Vec<PyObjectRef> = vec![cls.to_owned().into()];
Expand All @@ -711,10 +704,16 @@ fn reduce_newobj(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
} else {
// Use copyreg.__newobj_ex__
let newobj = copyreg.get_attr("__newobj_ex__", vm)?;
let args_tuple: PyObjectRef = args.map(|a| a.into()).unwrap_or_else(|| vm.ctx.empty_tuple.clone().into());
let kwargs_dict: PyObjectRef = kwargs.map(|k| k.into()).unwrap_or_else(|| vm.ctx.new_dict().into());

let newargs = vm.ctx.new_tuple(vec![cls.to_owned().into(), args_tuple, kwargs_dict]);
let args_tuple: PyObjectRef = args
.map(|a| a.into())
.unwrap_or_else(|| vm.ctx.empty_tuple.clone().into());
let kwargs_dict: PyObjectRef = kwargs
.map(|k| k.into())
.unwrap_or_else(|| vm.ctx.new_dict().into());

let newargs = vm
.ctx
.new_tuple(vec![cls.to_owned().into(), args_tuple, kwargs_dict]);
(newobj, newargs.into())
};

Expand All @@ -728,7 +727,9 @@ fn reduce_newobj(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {

let (listitems, dictitems) = get_items_iter(&obj, vm)?;

let result = vm.ctx.new_tuple(vec![newobj, newargs, state, listitems, dictitems]);
let result = vm
.ctx
.new_tuple(vec![newobj, newargs, state, listitems, dictitems]);
Ok(result.into())
}

Expand Down
24 changes: 14 additions & 10 deletions crates/vm/src/stdlib/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4104,6 +4104,10 @@ mod _io {

#[pymethod]
fn __setstate__(zelf: PyRef<Self>, state: PyTupleRef, vm: &VirtualMachine) -> PyResult<()> {
// Check closed state first (like CHECK_CLOSED)
if zelf.closed.load() {
return Err(vm.new_value_error("__setstate__ on closed file"));
}
if state.len() != 4 {
return Err(vm.new_type_error(format!(
"__setstate__ argument should be 4-tuple, got {}",
Expand All @@ -4116,14 +4120,14 @@ mod _io {
let pos: u64 = state[2].clone().try_into_value(vm)?;
let dict = &state[3];

// Set content
// Set content and position
let raw_bytes = content.as_bytes().to_vec();
*zelf.buffer.write() = BufferedIO::new(Cursor::new(raw_bytes));

// Set position
zelf.buffer(vm)?
let mut buffer = zelf.buffer.write();
*buffer = BufferedIO::new(Cursor::new(raw_bytes));
buffer
.seek(SeekFrom::Start(pos))
.map_err(|err| os_err(vm, err))?;
drop(buffer);

// Set __dict__ if provided
if !vm.is_none(dict) {
Expand Down Expand Up @@ -4324,13 +4328,13 @@ mod _io {
let pos: u64 = state[1].clone().try_into_value(vm)?;
let dict = &state[2];

// Set content
*zelf.buffer.write() = BufferedIO::new(Cursor::new(content.as_bytes().to_vec()));

// Set position
zelf.buffer(vm)?
// Check exports and set content (like CHECK_EXPORTS)
let mut buffer = zelf.try_resizable(vm)?;
*buffer = BufferedIO::new(Cursor::new(content.as_bytes().to_vec()));
buffer
.seek(SeekFrom::Start(pos))
.map_err(|err| os_err(vm, err))?;
drop(buffer);

// Set __dict__ if provided
if !vm.is_none(dict) {
Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/stdlib/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ pub(crate) mod _thread {
let mut handles = vm.state.shutdown_handles.lock();
// Clean up finished entries
handles.retain(|(inner_weak, _): &ShutdownEntry| {
inner_weak.upgrade().map_or(false, |inner| {
inner_weak.upgrade().is_some_and(|inner| {
let guard = inner.lock();
guard.state != ThreadHandleState::Done && guard.ident != current_ident
})
Expand Down