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
Add try_to_owned_from_ptr to avoid &PyObject on stale ptrs
Use addr_of! to access ref_count directly from a raw pointer
without forming &PyObject first. Applied in type cache and
instruction cache hit paths where the pointer may be stale.
  • Loading branch information
youknowone committed Mar 5, 2026
commit 3e31d2230fbee85d8222c65b6587f3e91aba1de5
5 changes: 2 additions & 3 deletions crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -803,9 +803,8 @@ impl PyType {
continue;
}
// _Py_TryIncrefCompare-style validation:
// safe_inc, then ensure the source pointer is unchanged.
let obj: &PyObject = unsafe { &*ptr };
if let Some(cloned) = obj.try_to_owned() {
// safe_inc via raw pointer, then ensure source is unchanged.
if let Some(cloned) = unsafe { PyObject::try_to_owned_from_ptr(ptr) } {
let same_ptr = core::ptr::eq(entry.value.load(Ordering::Relaxed), ptr);
if same_ptr && entry.end_read(seq1) {
return Some(cloned);
Expand Down
3 changes: 1 addition & 2 deletions crates/vm/src/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6896,8 +6896,7 @@ impl ExecutingFrame<'_> {
if descr_ptr == 0 {
return None;
}
let descr = unsafe { &*(descr_ptr as *const PyObject) };
let cloned = descr.try_to_owned()?;
let cloned = unsafe { PyObject::try_to_owned_from_ptr(descr_ptr as *mut PyObject) }?;
if self.code.instructions.read_cache_u32(cache_base + 1) == expected_type_version
&& self.code.instructions.read_cache_ptr(cache_base + 5) == descr_ptr
{
Expand Down
23 changes: 23 additions & 0 deletions crates/vm/src/object/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,29 @@
None
}
}

/// Like [`try_to_owned`](Self::try_to_owned), but from a raw pointer.
///
/// Uses `addr_of!` to access `ref_count` without forming `&PyObject`,
/// minimising the borrow scope when the pointer may be stale

Check warning on line 976 in crates/vm/src/object/core.rs

View workflow job for this annotation

GitHub Actions / Lint Rust & Python code

Unknown word (minimising)
/// (e.g. cache-hit paths protected by version guards).
///
/// # Safety
/// `ptr` must point to a live (not yet deallocated) `PyObject`, or to
/// memory whose `ref_count` field is still atomically readable
/// (same guarantee as `_Py_TryIncRefShared`).
#[inline]
pub unsafe fn try_to_owned_from_ptr(ptr: *mut Self) -> Option<PyObjectRef> {
let inner = ptr.cast::<PyInner<Erased>>();
let ref_count = unsafe { &*core::ptr::addr_of!((*inner).ref_count) };
if ref_count.safe_inc() {
Some(PyObjectRef {
ptr: unsafe { NonNull::new_unchecked(ptr) },
})
} else {
None
}
}
}

impl PyObjectRef {
Expand Down
Loading