bpo-38823: Fix refleaks in _ctypes extension init (GH-23247)

Fix reference leaks in the error path of the initialization function
the _ctypes extension module: call Py_DECREF(mod) on error.

Change PyCFuncPtr_Type name from _ctypes.PyCFuncPtr to
_ctypes.CFuncPtr to be consistent with the name exposed in the
_ctypes namespace (_ctypes.CFuncPtr).

Split PyInit__ctypes() function into sub-functions and add macros for
readability.
This commit is contained in:
Victor Stinner 2020-11-12 14:09:57 +01:00 committed by GitHub
parent c6409156c4
commit d19fa7a337
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 153 additions and 159 deletions

View File

@ -125,9 +125,13 @@ PyObject *_ctypes_ptrtype_cache = NULL;
static PyTypeObject Simple_Type; static PyTypeObject Simple_Type;
/* a callable object used for unpickling */ /* a callable object used for unpickling:
strong reference to _ctypes._unpickle() function */
static PyObject *_unpickle; static PyObject *_unpickle;
#ifdef MS_WIN32
PyObject *ComError; // Borrowed reference to: &PyComError_Type
#endif
/****************************************************************/ /****************************************************************/
@ -4307,7 +4311,7 @@ static PyNumberMethods PyCFuncPtr_as_number = {
PyTypeObject PyCFuncPtr_Type = { PyTypeObject PyCFuncPtr_Type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"_ctypes.PyCFuncPtr", "_ctypes.CFuncPtr",
sizeof(PyCFuncPtrObject), /* tp_basicsize */ sizeof(PyCFuncPtrObject), /* tp_basicsize */
0, /* tp_itemsize */ 0, /* tp_itemsize */
(destructor)PyCFuncPtr_dealloc, /* tp_dealloc */ (destructor)PyCFuncPtr_dealloc, /* tp_dealloc */
@ -5555,20 +5559,7 @@ static PyTypeObject PyComError_Type = {
0, /* tp_alloc */ 0, /* tp_alloc */
0, /* tp_new */ 0, /* tp_new */
}; };
#endif // MS_WIN32
static int
create_comerror(void)
{
PyComError_Type.tp_base = (PyTypeObject*)PyExc_Exception;
if (PyType_Ready(&PyComError_Type) < 0)
return -1;
Py_INCREF(&PyComError_Type);
ComError = (PyObject*)&PyComError_Type;
return 0;
}
#endif
static PyObject * static PyObject *
string_at(const char *ptr, int size) string_at(const char *ptr, int size)
@ -5679,128 +5670,70 @@ wstring_at(const wchar_t *ptr, int size)
static struct PyModuleDef _ctypesmodule = { static struct PyModuleDef _ctypesmodule = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
"_ctypes", .m_name = "_ctypes",
module_docs, .m_doc = module_docs,
-1, .m_size = -1,
_ctypes_module_methods, .m_methods = _ctypes_module_methods,
NULL,
NULL,
NULL,
NULL
}; };
PyMODINIT_FUNC
PyInit__ctypes(void)
{
PyObject *m;
/* Note: static int
_ctypes_add_types(PyObject *mod)
{
#define TYPE_READY(TYPE) \
if (PyType_Ready(TYPE) < 0) { \
return -1; \
}
#define TYPE_READY_BASE(TYPE_EXPR, TP_BASE) \
do { \
PyTypeObject *type = (TYPE_EXPR); \
type->tp_base = (TP_BASE); \
TYPE_READY(type); \
} while (0)
#define MOD_ADD_TYPE(TYPE_EXPR, TP_TYPE, TP_BASE) \
do { \
PyTypeObject *type = (TYPE_EXPR); \
Py_SET_TYPE(type, TP_TYPE); \
type->tp_base = TP_BASE; \
if (PyModule_AddType(mod, type) < 0) { \
return -1; \
} \
} while (0)
/* Note:
ob_type is the metatype (the 'type'), defaults to PyType_Type, ob_type is the metatype (the 'type'), defaults to PyType_Type,
tp_base is the base type, defaults to 'object' aka PyBaseObject_Type. tp_base is the base type, defaults to 'object' aka PyBaseObject_Type.
*/ */
m = PyModule_Create(&_ctypesmodule); TYPE_READY(&PyCArg_Type);
if (!m) TYPE_READY(&PyCThunk_Type);
return NULL; TYPE_READY(&PyCData_Type);
_ctypes_ptrtype_cache = PyDict_New();
if (_ctypes_ptrtype_cache == NULL)
return NULL;
PyModule_AddObject(m, "_pointer_type_cache", (PyObject *)_ctypes_ptrtype_cache);
_unpickle = PyObject_GetAttrString(m, "_unpickle");
if (_unpickle == NULL)
return NULL;
if (PyType_Ready(&PyCArg_Type) < 0)
return NULL;
if (PyType_Ready(&PyCThunk_Type) < 0)
return NULL;
/* StgDict is derived from PyDict_Type */ /* StgDict is derived from PyDict_Type */
PyCStgDict_Type.tp_base = &PyDict_Type; TYPE_READY_BASE(&PyCStgDict_Type, &PyDict_Type);
if (PyType_Ready(&PyCStgDict_Type) < 0)
return NULL;
/************************************************* /*************************************************
* *
* Metaclasses * Metaclasses
*/ */
TYPE_READY_BASE(&PyCStructType_Type, &PyType_Type);
PyCStructType_Type.tp_base = &PyType_Type; TYPE_READY_BASE(&UnionType_Type, &PyType_Type);
if (PyType_Ready(&PyCStructType_Type) < 0) TYPE_READY_BASE(&PyCPointerType_Type, &PyType_Type);
return NULL; TYPE_READY_BASE(&PyCArrayType_Type, &PyType_Type);
TYPE_READY_BASE(&PyCSimpleType_Type, &PyType_Type);
UnionType_Type.tp_base = &PyType_Type; TYPE_READY_BASE(&PyCFuncPtrType_Type, &PyType_Type);
if (PyType_Ready(&UnionType_Type) < 0)
return NULL;
PyCPointerType_Type.tp_base = &PyType_Type;
if (PyType_Ready(&PyCPointerType_Type) < 0)
return NULL;
PyCArrayType_Type.tp_base = &PyType_Type;
if (PyType_Ready(&PyCArrayType_Type) < 0)
return NULL;
PyCSimpleType_Type.tp_base = &PyType_Type;
if (PyType_Ready(&PyCSimpleType_Type) < 0)
return NULL;
PyCFuncPtrType_Type.tp_base = &PyType_Type;
if (PyType_Ready(&PyCFuncPtrType_Type) < 0)
return NULL;
/************************************************* /*************************************************
* *
* Classes using a custom metaclass * Classes using a custom metaclass
*/ */
if (PyType_Ready(&PyCData_Type) < 0) MOD_ADD_TYPE(&Struct_Type, &PyCStructType_Type, &PyCData_Type);
return NULL; MOD_ADD_TYPE(&Union_Type, &UnionType_Type, &PyCData_Type);
MOD_ADD_TYPE(&PyCPointer_Type, &PyCPointerType_Type, &PyCData_Type);
Py_SET_TYPE(&Struct_Type, &PyCStructType_Type); MOD_ADD_TYPE(&PyCArray_Type, &PyCArrayType_Type, &PyCData_Type);
Struct_Type.tp_base = &PyCData_Type; MOD_ADD_TYPE(&Simple_Type, &PyCSimpleType_Type, &PyCData_Type);
if (PyType_Ready(&Struct_Type) < 0) MOD_ADD_TYPE(&PyCFuncPtr_Type, &PyCFuncPtrType_Type, &PyCData_Type);
return NULL;
Py_INCREF(&Struct_Type);
PyModule_AddObject(m, "Structure", (PyObject *)&Struct_Type);
Py_SET_TYPE(&Union_Type, &UnionType_Type);
Union_Type.tp_base = &PyCData_Type;
if (PyType_Ready(&Union_Type) < 0)
return NULL;
Py_INCREF(&Union_Type);
PyModule_AddObject(m, "Union", (PyObject *)&Union_Type);
Py_SET_TYPE(&PyCPointer_Type, &PyCPointerType_Type);
PyCPointer_Type.tp_base = &PyCData_Type;
if (PyType_Ready(&PyCPointer_Type) < 0)
return NULL;
Py_INCREF(&PyCPointer_Type);
PyModule_AddObject(m, "_Pointer", (PyObject *)&PyCPointer_Type);
Py_SET_TYPE(&PyCArray_Type, &PyCArrayType_Type);
PyCArray_Type.tp_base = &PyCData_Type;
if (PyType_Ready(&PyCArray_Type) < 0)
return NULL;
Py_INCREF(&PyCArray_Type);
PyModule_AddObject(m, "Array", (PyObject *)&PyCArray_Type);
Py_SET_TYPE(&Simple_Type, &PyCSimpleType_Type);
Simple_Type.tp_base = &PyCData_Type;
if (PyType_Ready(&Simple_Type) < 0)
return NULL;
Py_INCREF(&Simple_Type);
PyModule_AddObject(m, "_SimpleCData", (PyObject *)&Simple_Type);
Py_SET_TYPE(&PyCFuncPtr_Type, &PyCFuncPtrType_Type);
PyCFuncPtr_Type.tp_base = &PyCData_Type;
if (PyType_Ready(&PyCFuncPtr_Type) < 0)
return NULL;
Py_INCREF(&PyCFuncPtr_Type);
PyModule_AddObject(m, "CFuncPtr", (PyObject *)&PyCFuncPtr_Type);
/************************************************* /*************************************************
* *
@ -5808,8 +5741,7 @@ PyInit__ctypes(void)
*/ */
/* PyCField_Type is derived from PyBaseObject_Type */ /* PyCField_Type is derived from PyBaseObject_Type */
if (PyType_Ready(&PyCField_Type) < 0) TYPE_READY(&PyCField_Type);
return NULL;
/************************************************* /*************************************************
* *
@ -5817,56 +5749,120 @@ PyInit__ctypes(void)
*/ */
DictRemover_Type.tp_new = PyType_GenericNew; DictRemover_Type.tp_new = PyType_GenericNew;
if (PyType_Ready(&DictRemover_Type) < 0) TYPE_READY(&DictRemover_Type);
return NULL; TYPE_READY(&StructParam_Type);
if (PyType_Ready(&StructParam_Type) < 0) {
return NULL;
}
#ifdef MS_WIN32 #ifdef MS_WIN32
if (create_comerror() < 0) TYPE_READY_BASE(&PyComError_Type, PyExc_Exception);
return NULL;
PyModule_AddObject(m, "COMError", ComError);
PyModule_AddObject(m, "FUNCFLAG_HRESULT", PyLong_FromLong(FUNCFLAG_HRESULT));
PyModule_AddObject(m, "FUNCFLAG_STDCALL", PyLong_FromLong(FUNCFLAG_STDCALL));
#endif #endif
PyModule_AddObject(m, "FUNCFLAG_CDECL", PyLong_FromLong(FUNCFLAG_CDECL));
PyModule_AddObject(m, "FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
PyModule_AddObject(m, "FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
PyModule_AddObject(m, "FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
PyModule_AddStringConstant(m, "__version__", "1.1.0");
PyModule_AddObject(m, "_memmove_addr", PyLong_FromVoidPtr(memmove)); #undef TYPE_READY
PyModule_AddObject(m, "_memset_addr", PyLong_FromVoidPtr(memset)); #undef TYPE_READY_BASE
PyModule_AddObject(m, "_string_at_addr", PyLong_FromVoidPtr(string_at)); #undef MOD_ADD_TYPE
PyModule_AddObject(m, "_cast_addr", PyLong_FromVoidPtr(cast)); return 0;
}
static int
_ctypes_add_objects(PyObject *mod)
{
#define MOD_ADD(name, expr) \
do { \
PyObject *obj = (expr); \
if (obj == NULL) { \
return -1; \
} \
if (PyModule_AddObjectRef(mod, name, obj) < 0) { \
Py_DECREF(obj); \
return -1; \
} \
Py_DECREF(obj); \
} while (0)
MOD_ADD("_pointer_type_cache", Py_NewRef(_ctypes_ptrtype_cache));
#ifdef MS_WIN32
MOD_ADD("COMError", Py_NewRef(ComError));
MOD_ADD("FUNCFLAG_HRESULT", PyLong_FromLong(FUNCFLAG_HRESULT));
MOD_ADD("FUNCFLAG_STDCALL", PyLong_FromLong(FUNCFLAG_STDCALL));
#endif
MOD_ADD("FUNCFLAG_CDECL", PyLong_FromLong(FUNCFLAG_CDECL));
MOD_ADD("FUNCFLAG_USE_ERRNO", PyLong_FromLong(FUNCFLAG_USE_ERRNO));
MOD_ADD("FUNCFLAG_USE_LASTERROR", PyLong_FromLong(FUNCFLAG_USE_LASTERROR));
MOD_ADD("FUNCFLAG_PYTHONAPI", PyLong_FromLong(FUNCFLAG_PYTHONAPI));
MOD_ADD("__version__", PyUnicode_FromString("1.1.0"));
MOD_ADD("_memmove_addr", PyLong_FromVoidPtr(memmove));
MOD_ADD("_memset_addr", PyLong_FromVoidPtr(memset));
MOD_ADD("_string_at_addr", PyLong_FromVoidPtr(string_at));
MOD_ADD("_cast_addr", PyLong_FromVoidPtr(cast));
#ifdef CTYPES_UNICODE #ifdef CTYPES_UNICODE
PyModule_AddObject(m, "_wstring_at_addr", PyLong_FromVoidPtr(wstring_at)); MOD_ADD("_wstring_at_addr", PyLong_FromVoidPtr(wstring_at));
#endif #endif
/* If RTLD_LOCAL is not defined (Windows!), set it to zero. */ /* If RTLD_LOCAL is not defined (Windows!), set it to zero. */
#if !HAVE_DECL_RTLD_LOCAL #if !HAVE_DECL_RTLD_LOCAL
#define RTLD_LOCAL 0 # define RTLD_LOCAL 0
#endif #endif
/* If RTLD_GLOBAL is not defined (cygwin), set it to the same value as /* If RTLD_GLOBAL is not defined (cygwin), set it to the same value as
RTLD_LOCAL. RTLD_LOCAL. */
*/
#if !HAVE_DECL_RTLD_GLOBAL #if !HAVE_DECL_RTLD_GLOBAL
#define RTLD_GLOBAL RTLD_LOCAL # define RTLD_GLOBAL RTLD_LOCAL
#endif #endif
MOD_ADD("RTLD_LOCAL", PyLong_FromLong(RTLD_LOCAL));
MOD_ADD("RTLD_GLOBAL", PyLong_FromLong(RTLD_GLOBAL));
MOD_ADD("ArgumentError", Py_NewRef(PyExc_ArgError));
return 0;
#undef MOD_ADD
}
PyModule_AddObject(m, "RTLD_LOCAL", PyLong_FromLong(RTLD_LOCAL));
PyModule_AddObject(m, "RTLD_GLOBAL", PyLong_FromLong(RTLD_GLOBAL)); static int
_ctypes_mod_exec(PyObject *mod)
{
_unpickle = PyObject_GetAttrString(mod, "_unpickle");
if (_unpickle == NULL) {
return -1;
}
_ctypes_ptrtype_cache = PyDict_New();
if (_ctypes_ptrtype_cache == NULL) {
return -1;
}
PyExc_ArgError = PyErr_NewException("ctypes.ArgumentError", NULL, NULL); PyExc_ArgError = PyErr_NewException("ctypes.ArgumentError", NULL, NULL);
if (PyExc_ArgError) { if (!PyExc_ArgError) {
Py_INCREF(PyExc_ArgError); return -1;
PyModule_AddObject(m, "ArgumentError", PyExc_ArgError);
} }
return m;
if (_ctypes_add_types(mod) < 0) {
return -1;
}
#ifdef MS_WIN32
ComError = (PyObject*)&PyComError_Type;
#endif
if (_ctypes_add_objects(mod) < 0) {
return -1;
}
return 0;
}
PyMODINIT_FUNC
PyInit__ctypes(void)
{
PyObject *mod = PyModule_Create(&_ctypesmodule);
if (!mod) {
return NULL;
}
if (_ctypes_mod_exec(mod) < 0) {
Py_DECREF(mod);
return NULL;
}
return mod;
} }
/* /*

View File

@ -254,8 +254,6 @@ set_last_error(PyObject *self, PyObject *args)
return set_error_internal(self, args, 1); return set_error_internal(self, args, 1);
} }
PyObject *ComError;
static WCHAR *FormatError(DWORD code) static WCHAR *FormatError(DWORD code)
{ {
WCHAR *lpMsgBuf; WCHAR *lpMsgBuf;