Skip to content

gh-117142: Slightly hacky fix for memory leak of StgInfo #119424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-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.
  • Loading branch information
encukou committed May 22, 2024
commit c3fdc38fa664dbe239858fc477dcd1b0b51ad4ac
68 changes: 30 additions & 38 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -488,38 +485,33 @@ 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);
}

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);
Expand Down
13 changes: 13 additions & 0 deletions Modules/_ctypes/ctypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,19 @@ 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)
{
assert(PyType_Type.tp_basicsize % ALIGNOF_MAX_ALIGN_T == 0);
return (StgInfo *)((char *)type + PyType_Type.tp_basicsize);
}

// Initialize StgInfo on a newly created type
static inline StgInfo *
PyStgInfo_Init(ctypes_state *state, PyTypeObject *type)
Expand Down
Loading