]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-121528: Fix _PyObject_Init() assertion for stable ABI (GH-121725) (#121936)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Wed, 17 Jul 2024 20:14:22 +0000 (22:14 +0200)
committerGitHub <noreply@github.com>
Wed, 17 Jul 2024 20:14:22 +0000 (20:14 +0000)
gh-121528: Fix _PyObject_Init() assertion for stable ABI (GH-121725)

Add _Py_IsImmortalLoose() function for assertions.
(cherry picked from commit b826e459ca6b640f896c2a9551bb2c78d10f0e2b)

Co-authored-by: Victor Stinner <vstinner@python.org>
Include/internal/pycore_object.h

index 74c1916804aca4489ba106a88fd1b55a0fbdaae2..62d63878082386d84a88b9966ca5e8e45239cf52 100644 (file)
@@ -15,6 +15,30 @@ extern "C" {
 #include "pycore_pyatomic_ft_wrappers.h"  // FT_ATOMIC_STORE_PTR_RELAXED
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
 
+
+#define _Py_IMMORTAL_REFCNT_LOOSE ((_Py_IMMORTAL_REFCNT >> 1) + 1)
+
+// gh-121528, gh-118997: Similar to _Py_IsImmortal() but be more loose when
+// comparing the reference count to stay compatible with C extensions built
+// with the stable ABI 3.11 or older. Such extensions implement INCREF/DECREF
+// as refcnt++ and refcnt-- without taking in account immortal objects. For
+// example, the reference count of an immortal object can change from
+// _Py_IMMORTAL_REFCNT to _Py_IMMORTAL_REFCNT+1 (INCREF) or
+// _Py_IMMORTAL_REFCNT-1 (DECREF).
+//
+// This function should only be used in assertions. Otherwise, _Py_IsImmortal()
+// must be used instead.
+static inline int _Py_IsImmortalLoose(PyObject *op)
+{
+#if defined(Py_GIL_DISABLED)
+    return _Py_IsImmortal(op);
+#else
+    return (op->ob_refcnt >= _Py_IMMORTAL_REFCNT_LOOSE);
+#endif
+}
+#define _Py_IsImmortalLoose(op) _Py_IsImmortalLoose(_PyObject_CAST(op))
+
+
 /* Check if an object is consistent. For example, ensure that the reference
    counter is greater than or equal to 1, and ensure that ob_type is not NULL.
 
@@ -134,7 +158,7 @@ extern void _Py_SetImmortalUntracked(PyObject *op);
 static inline void _Py_SetMortal(PyObject *op, Py_ssize_t refcnt)
 {
     if (op) {
-        assert(_Py_IsImmortal(op));
+        assert(_Py_IsImmortalLoose(op));
 #ifdef Py_GIL_DISABLED
         op->ob_tid = _Py_UNOWNED_TID;
         op->ob_ref_local = 0;
@@ -281,7 +305,7 @@ _PyObject_Init(PyObject *op, PyTypeObject *typeobj)
 {
     assert(op != NULL);
     Py_SET_TYPE(op, typeobj);
-    assert(_PyType_HasFeature(typeobj, Py_TPFLAGS_HEAPTYPE) || _Py_IsImmortal(typeobj));
+    assert(_PyType_HasFeature(typeobj, Py_TPFLAGS_HEAPTYPE) || _Py_IsImmortalLoose(typeobj));
     Py_INCREF(typeobj);
     _Py_NewReference(op);
 }