From cfcd5ea02c130573e558f9e039d518935eb0182b Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Mon, 21 Jul 2025 22:36:43 +0200 Subject: [PATCH] fix possibly leaked borow in `PyRef::into_super` --- newsfragments/5253.fixed.md | 1 + src/pycell.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 newsfragments/5253.fixed.md diff --git a/newsfragments/5253.fixed.md b/newsfragments/5253.fixed.md new file mode 100644 index 00000000000..aa6ae24cfd2 --- /dev/null +++ b/newsfragments/5253.fixed.md @@ -0,0 +1 @@ +fixed a leaked borrow, when converting a mutable sub class into a frozen base class using `PyRef::into_super` \ No newline at end of file diff --git a/src/pycell.rs b/src/pycell.rs index 215bfeedc8c..ff2dbf654ed 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -366,6 +366,15 @@ where /// ``` pub fn into_super(self) -> PyRef<'p, U> { let py = self.py(); + if ::VALUE { + // Frozen classes to not participate in borrow checking. We need to + // release the borrow here, because the BASE does not need it and so + // will also not release it, causing it to be leaked otherwise. + self.inner + .get_class_object() + .borrow_checker() + .release_borrow() + }; PyRef { inner: unsafe { ManuallyDrop::new(self) @@ -813,4 +822,31 @@ mod tests { crate::py_run!(py, obj, "assert obj.get_values() == (20, 30, 40)"); }); } + + #[test] + fn test_into_frozen_super_released_borrow() { + #[crate::pyclass] + #[pyo3(crate = "crate", subclass, frozen)] + struct BaseClass {} + + #[crate::pyclass] + #[pyo3(crate = "crate", extends=BaseClass, subclass)] + struct SubClass {} + + #[crate::pymethods] + #[pyo3(crate = "crate")] + impl SubClass { + #[new] + fn new(py: Python<'_>) -> Bound<'_, SubClass> { + let init = crate::PyClassInitializer::from(BaseClass {}).add_subclass(SubClass {}); + Bound::new(py, init).expect("allocation error") + } + } + + Python::attach(|py| { + let obj = SubClass::new(py); + drop(obj.borrow().into_super()); + assert!(obj.try_borrow_mut().is_ok()); + }) + } }