diff --git a/newsfragments/4355.fixed.md b/newsfragments/4355.fixed.md new file mode 100644 index 00000000000..9a141bc6b96 --- /dev/null +++ b/newsfragments/4355.fixed.md @@ -0,0 +1,2 @@ +Avoid creating temporary borrowed reference in dict.get_item bindings. Borrowed +references like this are unsafe in the free-threading build. diff --git a/pyo3-ffi/src/dictobject.rs b/pyo3-ffi/src/dictobject.rs index 99fc56b246b..9a5c4abb4d2 100644 --- a/pyo3-ffi/src/dictobject.rs +++ b/pyo3-ffi/src/dictobject.rs @@ -1,4 +1,6 @@ use crate::object::*; +#[cfg(not(Py_3_13))] +use crate::pyerrors::PyErr_Occurred; use crate::pyport::Py_ssize_t; use std::os::raw::{c_char, c_int}; use std::ptr::addr_of_mut; @@ -58,6 +60,12 @@ extern "C" { pub fn PyDict_MergeFromSeq2(d: *mut PyObject, seq2: *mut PyObject, _override: c_int) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyDict_GetItemString")] pub fn PyDict_GetItemString(dp: *mut PyObject, key: *const c_char) -> *mut PyObject; + #[cfg(Py_3_13)] + pub fn PyDict_GetItemRef( + dp: *mut PyObject, + key: *mut PyObject, + result: *mut *mut PyObject, + ) -> c_int; #[cfg_attr(PyPy, link_name = "PyPyDict_SetItemString")] pub fn PyDict_SetItemString( dp: *mut PyObject, @@ -96,6 +104,24 @@ pub unsafe fn PyDictViewSet_Check(op: *mut PyObject) -> c_int { (PyDictKeys_Check(op) != 0 || PyDictItems_Check(op) != 0) as c_int } +#[cfg(not(Py_3_13))] +pub unsafe fn PyDict_GetItemRef( + dp: *mut PyObject, + key: *mut PyObject, + result: *mut *mut PyObject, +) -> c_int { + let item: *mut PyObject = PyDict_GetItemWithError(dp, key); + if !item.is_null() { + *result = _Py_NewRef(item); + return 1; // found + } + *result = std::ptr::null_mut(); + if !PyErr_Occurred().is_null() { + return 0; // not found + } + -1 +} + #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { pub static mut PyDictIterKey_Type: PyTypeObject; diff --git a/src/types/dict.rs b/src/types/dict.rs index 850e468c672..172aa55ccc2 100644 --- a/src/types/dict.rs +++ b/src/types/dict.rs @@ -430,13 +430,12 @@ impl<'py> PyDictMethods<'py> for Bound<'py, PyDict> { key: Bound<'_, PyAny>, ) -> PyResult>> { let py = dict.py(); - match unsafe { - ffi::PyDict_GetItemWithError(dict.as_ptr(), key.as_ptr()) - .assume_borrowed_or_opt(py) - .map(Borrowed::to_owned) - } { - some @ Some(_) => Ok(some), - None => PyErr::take(py).map(Err).transpose(), + unsafe { + let mut result: *mut ffi::PyObject = std::ptr::null_mut(); + match ffi::PyDict_GetItemRef(dict.as_ptr(), key.as_ptr(), &mut result) { + std::os::raw::c_int::MIN..=0 => PyErr::take(py).map(Err).transpose(), + 1..=std::os::raw::c_int::MAX => Ok(result.assume_owned_or_opt(py)), + } } }