Skip to content

Commit a192547

Browse files
encukouneonene
andauthored
gh-117142: Slightly hacky fix for memory leak of StgInfo (GH-119424)
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 <[email protected]>
1 parent 406ffb5 commit a192547

File tree

2 files changed

+45
-53
lines changed

2 files changed

+45
-53
lines changed

Modules/_ctypes/_ctypes.c

+31-39
Original file line numberDiff line numberDiff line change
@@ -454,20 +454,17 @@ class _ctypes.CType_Type "PyObject *" "clinic_state()->CType_Type"
454454
static int
455455
CType_Type_traverse(PyObject *self, visitproc visit, void *arg)
456456
{
457-
ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self));
458-
if (st && st->PyCType_Type) {
459-
StgInfo *info;
460-
if (PyStgInfo_FromType(st, self, &info) < 0) {
461-
PyErr_WriteUnraisable(self);
462-
}
463-
if (info) {
464-
Py_VISIT(info->proto);
465-
Py_VISIT(info->argtypes);
466-
Py_VISIT(info->converters);
467-
Py_VISIT(info->restype);
468-
Py_VISIT(info->checker);
469-
Py_VISIT(info->module);
470-
}
457+
StgInfo *info = _PyStgInfo_FromType_NoState(self);
458+
if (!info) {
459+
PyErr_WriteUnraisable(self);
460+
}
461+
if (info) {
462+
Py_VISIT(info->proto);
463+
Py_VISIT(info->argtypes);
464+
Py_VISIT(info->converters);
465+
Py_VISIT(info->restype);
466+
Py_VISIT(info->checker);
467+
Py_VISIT(info->module);
471468
}
472469
Py_VISIT(Py_TYPE(self));
473470
return PyType_Type.tp_traverse(self, visit, arg);
@@ -488,38 +485,33 @@ ctype_clear_stginfo(StgInfo *info)
488485
static int
489486
CType_Type_clear(PyObject *self)
490487
{
491-
ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self));
492-
if (st && st->PyCType_Type) {
493-
StgInfo *info;
494-
if (PyStgInfo_FromType(st, self, &info) < 0) {
495-
PyErr_WriteUnraisable(self);
496-
}
497-
if (info) {
498-
ctype_clear_stginfo(info);
499-
}
488+
StgInfo *info = _PyStgInfo_FromType_NoState(self);
489+
if (!info) {
490+
PyErr_WriteUnraisable(self);
491+
}
492+
if (info) {
493+
ctype_clear_stginfo(info);
500494
}
501495
return PyType_Type.tp_clear(self);
502496
}
503497

504498
static void
505499
CType_Type_dealloc(PyObject *self)
506500
{
507-
ctypes_state *st = get_module_state_by_def_final(Py_TYPE(self));
508-
if (st && st->PyCType_Type) {
509-
StgInfo *info;
510-
if (PyStgInfo_FromType(st, self, &info) < 0) {
511-
PyErr_WriteUnraisable(self);
512-
}
513-
if (info) {
514-
PyMem_Free(info->ffi_type_pointer.elements);
515-
info->ffi_type_pointer.elements = NULL;
516-
PyMem_Free(info->format);
517-
info->format = NULL;
518-
PyMem_Free(info->shape);
519-
info->shape = NULL;
520-
ctype_clear_stginfo(info);
521-
}
501+
StgInfo *info = _PyStgInfo_FromType_NoState(self);
502+
if (!info) {
503+
PyErr_WriteUnraisable(self);
504+
}
505+
if (info) {
506+
PyMem_Free(info->ffi_type_pointer.elements);
507+
info->ffi_type_pointer.elements = NULL;
508+
PyMem_Free(info->format);
509+
info->format = NULL;
510+
PyMem_Free(info->shape);
511+
info->shape = NULL;
512+
ctype_clear_stginfo(info);
522513
}
514+
523515
PyTypeObject *tp = Py_TYPE(self);
524516
PyType_Type.tp_dealloc(self);
525517
Py_DECREF(tp);
@@ -5947,7 +5939,7 @@ module_free(void *module)
59475939

59485940
static PyModuleDef_Slot module_slots[] = {
59495941
{Py_mod_exec, _ctypes_mod_exec},
5950-
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
5942+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED},
59515943
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
59525944
{0, NULL}
59535945
};

Modules/_ctypes/ctypes.h

+14-14
Original file line numberDiff line numberDiff line change
@@ -101,20 +101,6 @@ get_module_state_by_def(PyTypeObject *cls)
101101
return get_module_state(mod);
102102
}
103103

104-
static inline ctypes_state *
105-
get_module_state_by_def_final(PyTypeObject *cls)
106-
{
107-
if (cls->tp_mro == NULL) {
108-
return NULL;
109-
}
110-
PyObject *mod = PyType_GetModuleByDef(cls, &_ctypesmodule);
111-
if (mod == NULL) {
112-
PyErr_Clear();
113-
return NULL;
114-
}
115-
return get_module_state(mod);
116-
}
117-
118104

119105
extern PyType_Spec carg_spec;
120106
extern PyType_Spec cfield_spec;
@@ -502,6 +488,20 @@ PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result)
502488
return _stginfo_from_type(state, Py_TYPE(obj), result);
503489
}
504490

491+
/* A variant of PyStgInfo_FromType that doesn't need the state,
492+
* so it can be called from finalization functions when the module
493+
* state is torn down. Does no checks; cannot fail.
494+
* This inlines the current implementation PyObject_GetTypeData,
495+
* so it might break in the future.
496+
*/
497+
static inline StgInfo *
498+
_PyStgInfo_FromType_NoState(PyObject *type)
499+
{
500+
size_t type_basicsize =_Py_SIZE_ROUND_UP(PyType_Type.tp_basicsize,
501+
ALIGNOF_MAX_ALIGN_T);
502+
return (StgInfo *)((char *)type + type_basicsize);
503+
}
504+
505505
// Initialize StgInfo on a newly created type
506506
static inline StgInfo *
507507
PyStgInfo_Init(ctypes_state *state, PyTypeObject *type)

0 commit comments

Comments
 (0)