Skip to content

Commit c3fdc38

Browse files
committed
pythongh-117142: Slightliy hacky fix for memory leak of StgInfo
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.
1 parent 8186500 commit c3fdc38

File tree

2 files changed

+43
-38
lines changed

2 files changed

+43
-38
lines changed

Modules/_ctypes/_ctypes.c

Lines changed: 30 additions & 38 deletions
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);

Modules/_ctypes/ctypes.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,19 @@ PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result)
508508
return _stginfo_from_type(state, Py_TYPE(obj), result);
509509
}
510510

511+
/* A variant of PyStgInfo_FromType that doesn't need the state,
512+
* so it can be called from finalization functions when the module
513+
* state is torn down. Does no checks; cannot fail.
514+
* This inlines the current implementation PyObject_GetTypeData,
515+
* so it might break in the future.
516+
*/
517+
static inline StgInfo *
518+
_PyStgInfo_FromType_NoState(PyObject *type)
519+
{
520+
assert(PyType_Type.tp_basicsize % ALIGNOF_MAX_ALIGN_T == 0);
521+
return (StgInfo *)((char *)type + PyType_Type.tp_basicsize);
522+
}
523+
511524
// Initialize StgInfo on a newly created type
512525
static inline StgInfo *
513526
PyStgInfo_Init(ctypes_state *state, PyTypeObject *type)

0 commit comments

Comments
 (0)