https://github.com/python/cpython/commit/e09c4deb254a85ef2be7a0a636c2845688c915ad
commit: e09c4deb254a85ef2be7a0a636c2845688c915ad
branch: 3.14
author: Petr Viktorin <[email protected]>
committer: DinoV <[email protected]>
date: 2025-12-11T10:51:11-08:00
summary:

[3.14] gh-123241: Don't modify ref count during visitation (GH-142232) (#142567)

(cherry picked from commit da8199f8842c2830fa4015138725849863523de4)

Co-authored-by: Dino Viehland <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst
M Include/internal/pycore_typeobject.h
M Modules/_ctypes/ctypes.h
M Objects/typeobject.c

diff --git a/Include/internal/pycore_typeobject.h 
b/Include/internal/pycore_typeobject.h
index 1a4f89fd2449a0..ba66364d3e94b1 100644
--- a/Include/internal/pycore_typeobject.h
+++ b/Include/internal/pycore_typeobject.h
@@ -149,6 +149,11 @@ typedef int (*_py_validate_type)(PyTypeObject *);
 extern int _PyType_Validate(PyTypeObject *ty, _py_validate_type validate, 
unsigned int *tp_version);
 extern int _PyType_CacheGetItemForSpecialization(PyHeapTypeObject *ht, 
PyObject *descriptor, uint32_t tp_version);
 
+// Like PyType_GetBaseByToken, but does not modify refcounts.
+// Cannot fail; arguments must be valid.
+PyAPI_FUNC(int)
+_PyType_GetBaseByToken_Borrow(PyTypeObject *type, void *token, PyTypeObject 
**result);
+
 #ifdef __cplusplus
 }
 #endif
diff --git 
a/Misc/NEWS.d/next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst 
b/Misc/NEWS.d/next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst
new file mode 100644
index 00000000000000..871a03a6fd1021
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst
@@ -0,0 +1,2 @@
+Avoid reference count operations in garbage collection of :mod:`ctypes`
+objects.
diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h
index f8389f98fec780..78a33fea0192c2 100644
--- a/Modules/_ctypes/ctypes.h
+++ b/Modules/_ctypes/ctypes.h
@@ -596,7 +596,8 @@ PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, 
StgInfo **result)
     return _stginfo_from_type(state, Py_TYPE(obj), result);
 }
 
-/* A variant of PyStgInfo_FromType that doesn't need the state,
+/* A variant of PyStgInfo_FromType that doesn't need the state
+ * and doesn't modify any refcounts,
  * so it can be called from finalization functions when the module
  * state is torn down.
  */
@@ -604,17 +605,12 @@ static inline StgInfo *
 _PyStgInfo_FromType_NoState(PyObject *type)
 {
     PyTypeObject *PyCType_Type;
-    if (PyType_GetBaseByToken(Py_TYPE(type), &pyctype_type_spec, 
&PyCType_Type) < 0) {
-        return NULL;
-    }
-    if (PyCType_Type == NULL) {
-        PyErr_Format(PyExc_TypeError, "expected a ctypes type, got '%N'", 
type);
+    if (_PyType_GetBaseByToken_Borrow(Py_TYPE(type), &pyctype_type_spec, 
&PyCType_Type) < 0 ||
+        PyCType_Type == NULL) {
         return NULL;
     }
 
-    StgInfo *info = PyObject_GetTypeData(type, PyCType_Type);
-    Py_DECREF(PyCType_Type);
-    return info;
+    return PyObject_GetTypeData(type, PyCType_Type);
 }
 
 // Initialize StgInfo on a newly created type
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 05d12bf6bbaf5c..adbc0743161326 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -5533,23 +5533,15 @@ get_base_by_token_recursive(PyObject *bases, void 
*token)
 }
 
 int
-PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)
+_PyType_GetBaseByToken_Borrow(PyTypeObject *type, void *token, PyTypeObject 
**result)
 {
+    assert(token != NULL);
+    assert(PyType_Check(type));
+
     if (result != NULL) {
         *result = NULL;
     }
 
-    if (token == NULL) {
-        PyErr_Format(PyExc_SystemError,
-                     "PyType_GetBaseByToken called with token=NULL");
-        return -1;
-    }
-    if (!PyType_Check(type)) {
-        PyErr_Format(PyExc_TypeError,
-                     "expected a type, got a '%T' object", type);
-        return -1;
-    }
-
     if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
         // No static type has a heaptype superclass,
         // which is ensured by type_ready_mro().
@@ -5558,7 +5550,7 @@ PyType_GetBaseByToken(PyTypeObject *type, void *token, 
PyTypeObject **result)
     if (((PyHeapTypeObject*)type)->ht_token == token) {
 found:
         if (result != NULL) {
-            *result = (PyTypeObject *)Py_NewRef(type);
+            *result = type;
         }
         return 1;
     }
@@ -5592,6 +5584,30 @@ PyType_GetBaseByToken(PyTypeObject *type, void *token, 
PyTypeObject **result)
     return 0;
 }
 
+int
+PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result)
+{
+    if (result != NULL) {
+         *result = NULL;
+    }
+    if (token == NULL) {
+        PyErr_Format(PyExc_SystemError,
+                     "PyType_GetBaseByToken called with token=NULL");
+        return -1;
+    }
+    if (!PyType_Check(type)) {
+        PyErr_Format(PyExc_TypeError,
+                     "expected a type, got a '%T' object", type);
+        return -1;
+    }
+
+    int res = _PyType_GetBaseByToken_Borrow(type, token, result);
+    if (res > 0 && result) {
+        Py_INCREF(*result);
+    }
+    return res;
+}
+
 
 void *
 PyObject_GetTypeData(PyObject *obj, PyTypeObject *cls)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to