FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Prevent __class__ reassignment across incompatible layouts by Copilot · Pull Request #6521 · RustPython/RustPython · GitHub

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

Filter by extension

Filter by extension .rs  (2) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
Prev Previous commit
Next Next commit
fix: guard __class__ reassignment layout
Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com>
  • Loading branch information
Copilot and youknowone committed Dec 26, 2025
commit a3fb6fed740a862e216e674b52932663f8dde1f6
19 changes: 16 additions & 3 deletions crates/vm/src/builtins/object.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -497,16 +497,29 @@ impl PyBaseObject {
) -> PyResult<()> {
match value.downcast::<PyType>() {
Ok(cls) => {
let both_module = instance.class().fast_issubclass(vm.ctx.types.module_type)
let current_cls = instance.class();
let both_module = current_cls.fast_issubclass(vm.ctx.types.module_type)
&& cls.fast_issubclass(vm.ctx.types.module_type);
let both_mutable = !instance
.class()
let both_mutable = !current_cls
.slots
.flags
.has_feature(PyTypeFlags::IMMUTABLETYPE)
&& !cls.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE);
// FIXME(#1979) cls instances might have a payload
if both_mutable || both_module {
let has_dict = |typ: &Py<PyType>| typ.slots.flags.has_feature(PyTypeFlags::HAS_DICT);
if current_cls.slots.basicsize != cls.slots.basicsize
|| current_cls.slots.member_count != cls.slots.member_count
|| has_dict(current_cls) != has_dict(&cls)
|| (cls.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
&& instance.typeid() != PyBaseObject::payload_type_id())
{
return Err(vm.new_type_error(format!(
"__class__ assignment: '{}' object layout differs from '{}'",
cls.name(),
current_cls.name()
)));
}
instance.set_class(cls, vm);
Ok(())
} else {
Expand Down
27 changes: 27 additions & 0 deletions crates/vm/src/vm/interpreter.rs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Instead of adding test in rust code, add test test in builtin_type.py

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,31 @@ mod tests {
assert_eq!(value.as_str(), "Hello Hello Hello Hello ")
})
}

#[test]
fn class_assignment_layout_mismatch_error() {
Interpreter::without_stdlib(Default::default()).enter(|vm| {
let scope = vm.new_scope_with_builtins();
let source = r#"
class TypeA:
def __init__(self):
self.a = 1

class TypeB:
__slots__ = "b"
def __init__(self):
self.b = 2

obj = TypeA()
try:
obj.__class__ = TypeB
except TypeError as e:
assert str(e) == "__class__ assignment: 'TypeB' object layout differs from 'TypeA'"
else:
raise AssertionError("TypeError not raised")
"#;
vm.run_code_string(scope, source, "<test>".to_owned())
.expect("script should complete without uncaught exceptions");
})
}
}

Back | FazBrowse Home | New Git URL