diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index d1ab6deeb4..5576ad1300 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -601,6 +601,15 @@ enum class return_value_policy : uint8_t { PYBIND11_NAMESPACE_BEGIN(detail) +// Py_IsFinalizing() is a public API since 3.13; before that use _Py_IsFinalizing(). +inline bool py_is_finalizing() { +#if PY_VERSION_HEX >= 0x030D0000 + return Py_IsFinalizing() != 0; +#else + return _Py_IsFinalizing() != 0; +#endif +} + static constexpr int log2(size_t n, int k = 0) { return (n <= 1) ? k : log2(n >> 1, k + 1); } // Returns the size as a multiple of sizeof(void *), rounded up. diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 76d998a3ba..454638e299 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -846,8 +846,11 @@ class cpp_function : public function { std::free(const_cast(arg.descr)); } } - for (auto &arg : rec->args) { - arg.value.dec_ref(); + // During finalization, default arg values may already be freed by GC. + if (!detail::py_is_finalizing()) { + for (auto &arg : rec->args) { + arg.value.dec_ref(); + } } if (rec->def) { std::free(const_cast(rec->def->ml_doc)); @@ -1342,9 +1345,21 @@ PYBIND11_NAMESPACE_BEGIN(function_record_PyTypeObject_methods) // This implementation needs the definition of `class cpp_function`. inline void tp_dealloc_impl(PyObject *self) { + // Skip dealloc during finalization — GC may have already freed objects + // reachable from the function record (e.g. default arg values), causing + // use-after-free in destruct(). + if (detail::py_is_finalizing()) { + return; + } + // Save type before PyObject_Free invalidates self. + auto *type = Py_TYPE(self); auto *py_func_rec = reinterpret_cast(self); cpp_function::destruct(py_func_rec->cpp_func_rec); py_func_rec->cpp_func_rec = nullptr; + // PyObject_New increments the heap type refcount and allocates via + // PyObject_Malloc; balance both here + PyObject_Free(self); + Py_DECREF(type); } PYBIND11_NAMESPACE_END(function_record_PyTypeObject_methods)