]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-117142: Slightly hacky fix for memory leak of StgInfo (GH-119424)
authorPetr Viktorin <encukou@gmail.com>
Thu, 23 May 2024 16:01:37 +0000 (18:01 +0200)
committerGitHub <noreply@github.com>
Thu, 23 May 2024 16:01:37 +0000 (16:01 +0000)
Add a funciton that inlines PyObject_GetTypeData and skips
type-checking, so it doesn't need access to the CType_Type object.
This will break if the memory layout changes, but should
be an acceptable solution to enable ctypes in subinterpreters in
Python 3.13.

Mark _ctypes as safe for multiple interpreters

Co-authored-by: neonene <53406459+neonene@users.noreply.github.com>
Modules/_ctypes/_ctypes.c
Modules/_ctypes/ctypes.h

index 574cb8014493c44c90b1871cc40c2b77224ca5ca..6c1e5f58b9565725aa3b4fb03c94eab43b4c4ef3 100644 (file)
@@ -454,20 +454,17 @@ class _ctypes.CType_Type "PyObject *" "clinic_state()->CType_Type"
 static int
 CType_Type_traverse(PyObject *self, visitproc visit, void *arg)
 {
-    ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self));
-    if (st && st->PyCType_Type) {
-        StgInfo *info;
-        if (PyStgInfo_FromType(st, self, &info) < 0) {
-            PyErr_WriteUnraisable(self);
-        }
-        if (info) {
-            Py_VISIT(info->proto);
-            Py_VISIT(info->argtypes);
-            Py_VISIT(info->converters);
-            Py_VISIT(info->restype);
-            Py_VISIT(info->checker);
-            Py_VISIT(info->module);
-        }
+    StgInfo *info = _PyStgInfo_FromType_NoState(self);
+    if (!info) {
+        PyErr_WriteUnraisable(self);
+    }
+    if (info) {
+        Py_VISIT(info->proto);
+        Py_VISIT(info->argtypes);
+        Py_VISIT(info->converters);
+        Py_VISIT(info->restype);
+        Py_VISIT(info->checker);
+        Py_VISIT(info->module);
     }
     Py_VISIT(Py_TYPE(self));
     return PyType_Type.tp_traverse(self, visit, arg);
@@ -488,15 +485,12 @@ ctype_clear_stginfo(StgInfo *info)
 static int
 CType_Type_clear(PyObject *self)
 {
-    ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self));
-    if (st && st->PyCType_Type) {
-        StgInfo *info;
-        if (PyStgInfo_FromType(st, self, &info) < 0) {
-            PyErr_WriteUnraisable(self);
-        }
-        if (info) {
-            ctype_clear_stginfo(info);
-        }
+    StgInfo *info = _PyStgInfo_FromType_NoState(self);
+    if (!info) {
+        PyErr_WriteUnraisable(self);
+    }
+    if (info) {
+        ctype_clear_stginfo(info);
     }
     return PyType_Type.tp_clear(self);
 }
@@ -504,22 +498,20 @@ CType_Type_clear(PyObject *self)
 static void
 CType_Type_dealloc(PyObject *self)
 {
-    ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self));
-    if (st && st->PyCType_Type) {
-        StgInfo *info;
-        if (PyStgInfo_FromType(st, self, &info) < 0) {
-            PyErr_WriteUnraisable(self);
-        }
-        if (info) {
-            PyMem_Free(info->ffi_type_pointer.elements);
-            info->ffi_type_pointer.elements = NULL;
-            PyMem_Free(info->format);
-            info->format = NULL;
-            PyMem_Free(info->shape);
-            info->shape = NULL;
-            ctype_clear_stginfo(info);
-        }
+    StgInfo *info = _PyStgInfo_FromType_NoState(self);
+    if (!info) {
+        PyErr_WriteUnraisable(self);
+    }
+    if (info) {
+        PyMem_Free(info->ffi_type_pointer.elements);
+        info->ffi_type_pointer.elements = NULL;
+        PyMem_Free(info->format);
+        info->format = NULL;
+        PyMem_Free(info->shape);
+        info->shape = NULL;
+        ctype_clear_stginfo(info);
     }
+
     PyTypeObject *tp = Py_TYPE(self);
     PyType_Type.tp_dealloc(self);
     Py_DECREF(tp);
@@ -5947,7 +5939,7 @@ module_free(void *module)
 
 static PyModuleDef_Slot module_slots[] = {
     {Py_mod_exec, _ctypes_mod_exec},
-    {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
+    {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED},
     {Py_mod_gil, Py_MOD_GIL_NOT_USED},
     {0, NULL}
 };
index 7784020e9af45a5328f7bc54fedaef60dde41e0b..423120f3460113113d91e7302871218eb36fc70f 100644 (file)
@@ -101,20 +101,6 @@ get_module_state_by_def(PyTypeObject *cls)
     return get_module_state(mod);
 }
 
-static inline ctypes_state *
-get_module_state_by_def_final(PyTypeObject *cls)
-{
-    if (cls->tp_mro == NULL) {
-        return NULL;
-    }
-    PyObject *mod = PyType_GetModuleByDef(cls, &_ctypesmodule);
-    if (mod == NULL) {
-        PyErr_Clear();
-        return NULL;
-    }
-    return get_module_state(mod);
-}
-
 
 extern PyType_Spec carg_spec;
 extern PyType_Spec cfield_spec;
@@ -502,6 +488,20 @@ 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,
+ * so it can be called from finalization functions when the module
+ * state is torn down. Does no checks; cannot fail.
+ * This inlines the current implementation PyObject_GetTypeData,
+ * so it might break in the future.
+ */
+static inline StgInfo *
+_PyStgInfo_FromType_NoState(PyObject *type)
+{
+    size_t type_basicsize =_Py_SIZE_ROUND_UP(PyType_Type.tp_basicsize,
+                                             ALIGNOF_MAX_ALIGN_T);
+    return (StgInfo *)((char *)type + type_basicsize);
+}
+
 // Initialize StgInfo on a newly created type
 static inline StgInfo *
 PyStgInfo_Init(ctypes_state *state, PyTypeObject *type)