Skip to content

Commit 9dfc029

Browse files
committed
fix bpo-37619
1 parent 6df4f0f commit 9dfc029

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

crates/vm/src/builtins/descriptor.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,15 @@ impl Callable for PyMethodWrapper {
774774
type Args = FuncArgs;
775775

776776
fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
777+
// bpo-37619: Check type compatibility before calling wrapped slot
778+
if !zelf.obj.fast_isinstance(zelf.wrapper.typ) {
779+
return Err(vm.new_type_error(format!(
780+
"descriptor '{}' requires a '{}' object but received a '{}'",
781+
zelf.wrapper.name.as_str(),
782+
zelf.wrapper.typ.name(),
783+
zelf.obj.class().name()
784+
)));
785+
}
777786
zelf.wrapper.wrapped.call(zelf.obj.clone(), args, vm)
778787
}
779788
}

crates/vm/src/types/slot.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,28 +1362,44 @@ impl PyType {
13621362
) -> Option<T> {
13631363
use crate::builtins::descriptor::PyWrapper;
13641364

1365+
// Helper to check if a class is a subclass of another by checking MRO
1366+
let is_subclass_of = |subclass_mro: &[PyRef<PyType>], superclass: &Py<PyType>| -> bool {
1367+
subclass_mro.iter().any(|c| c.is(superclass))
1368+
};
1369+
13651370
// Helper to extract slot from an attribute if it's a wrapper descriptor
1366-
let try_extract = |attr: &PyObjectRef| -> Option<T> {
1371+
// and the wrapper's type is compatible with the given class.
1372+
// bpo-37619: wrapper descriptor from wrong class should not be used directly.
1373+
let try_extract = |attr: &PyObjectRef, for_class_mro: &[PyRef<PyType>]| -> Option<T> {
13671374
if attr.class().is(ctx.types.wrapper_descriptor_type) {
1368-
attr.downcast_ref::<PyWrapper>()
1369-
.and_then(|wrapper| extract(&wrapper.wrapped))
1375+
attr.downcast_ref::<PyWrapper>().and_then(|wrapper| {
1376+
// Only extract slot if for_class is a subclass of wrapper.typ
1377+
if is_subclass_of(for_class_mro, wrapper.typ) {
1378+
extract(&wrapper.wrapped)
1379+
} else {
1380+
None
1381+
}
1382+
})
13701383
} else {
13711384
None
13721385
}
13731386
};
13741387

1388+
let mro = self.mro.read();
1389+
13751390
// Look up in self's dict first
13761391
if let Some(attr) = self.attributes.read().get(name).cloned() {
1377-
if let Some(func) = try_extract(&attr) {
1392+
if let Some(func) = try_extract(&attr, &mro) {
13781393
return Some(func);
13791394
}
13801395
return None;
13811396
}
13821397

13831398
// Look up in MRO (mro[0] is self, so skip it)
1384-
for cls in self.mro.read()[1..].iter() {
1399+
for (i, cls) in mro[1..].iter().enumerate() {
13851400
if let Some(attr) = cls.attributes.read().get(name).cloned() {
1386-
if let Some(func) = try_extract(&attr) {
1401+
// Use the slice starting from this class in MRO
1402+
if let Some(func) = try_extract(&attr, &mro[i + 1..]) {
13871403
return Some(func);
13881404
}
13891405
return None;

0 commit comments

Comments
 (0)