summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@redhat.com>2019-08-30 12:30:33 (GMT)
committerGitHub <noreply@github.com>2019-08-30 12:30:33 (GMT)
commit96b4087ce784ee7434dffdf69c475f5b40543982 (patch)
treeae440df23a43a6bbfd6e6f00d6c353ae3c88dd52 /Modules
parent6a650aaf7735e30636db2721247f317064c2cfd4 (diff)
downloadcpython-96b4087ce784ee7434dffdf69c475f5b40543982.zip
cpython-96b4087ce784ee7434dffdf69c475f5b40543982.tar.gz
cpython-96b4087ce784ee7434dffdf69c475f5b40543982.tar.bz2
bpo-37140: Fix StructUnionType_paramfunc() (GH-15612)
Fix a ctypes regression of Python 3.8. When a ctypes.Structure is passed by copy to a function, ctypes internals created a temporary object which had the side effect of calling the structure finalizer (__del__) twice. The Python semantics requires a finalizer to be called exactly once. Fix ctypes internals to no longer call the finalizer twice. Create a new internal StructParam_Type which is only used by _ctypes_callproc() to call PyMem_Free(ptr) on Py_DECREF(argument). StructUnionType_paramfunc() creates such object.
Diffstat (limited to 'Modules')
-rw-r--r--Modules/_ctypes/_ctypes.c73
1 files changed, 59 insertions, 14 deletions
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index c1941c1..95bfe9d 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -392,6 +392,35 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape,
return result;
}
+/* StructParamObject and StructParam_Type are used in _ctypes_callproc()
+ for argument.keep to call PyMem_Free(ptr) on Py_DECREF(argument).
+
+ StructUnionType_paramfunc() creates such object when a ctypes Structure is
+ passed by copy to a C function. */
+typedef struct {
+ PyObject_HEAD
+ void *ptr;
+} StructParamObject;
+
+
+static void
+StructParam_dealloc(PyObject *myself)
+{
+ StructParamObject *self = (StructParamObject *)myself;
+ PyMem_Free(self->ptr);
+ Py_TYPE(self)->tp_free(myself);
+}
+
+
+static PyTypeObject StructParam_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "_ctypes.StructParam_Type",
+ .tp_basicsize = sizeof(StructParamObject),
+ .tp_dealloc = StructParam_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+
/*
PyCStructType_Type - a meta type/class. Creating a new class using this one as
__metaclass__ will call the constructor StructUnionType_new. It replaces the
@@ -403,35 +432,47 @@ static PyCArgObject *
StructUnionType_paramfunc(CDataObject *self)
{
PyCArgObject *parg;
- CDataObject *copied_self;
+ PyObject *obj;
StgDictObject *stgdict;
+ void *ptr;
if ((size_t)self->b_size > sizeof(void*)) {
- void *new_ptr = PyMem_Malloc(self->b_size);
- if (new_ptr == NULL)
+ ptr = PyMem_Malloc(self->b_size);
+ if (ptr == NULL) {
return NULL;
- memcpy(new_ptr, self->b_ptr, self->b_size);
- copied_self = (CDataObject *)PyCData_AtAddress(
- (PyObject *)Py_TYPE(self), new_ptr);
- copied_self->b_needsfree = 1;
+ }
+ memcpy(ptr, self->b_ptr, self->b_size);
+
+ /* Create a Python object which calls PyMem_Free(ptr) in
+ its deallocator. The object will be destroyed
+ at _ctypes_callproc() cleanup. */
+ obj = (&StructParam_Type)->tp_alloc(&StructParam_Type, 0);
+ if (obj == NULL) {
+ PyMem_Free(ptr);
+ return NULL;
+ }
+
+ StructParamObject *struct_param = (StructParamObject *)obj;
+ struct_param->ptr = ptr;
} else {
- copied_self = self;
- Py_INCREF(copied_self);
+ ptr = self->b_ptr;
+ obj = (PyObject *)self;
+ Py_INCREF(obj);
}
parg = PyCArgObject_new();
if (parg == NULL) {
- Py_DECREF(copied_self);
+ Py_DECREF(obj);
return NULL;
}
parg->tag = 'V';
- stgdict = PyObject_stgdict((PyObject *)copied_self);
+ stgdict = PyObject_stgdict((PyObject *)self);
assert(stgdict); /* Cannot be NULL for structure/union instances */
parg->pffi_type = &stgdict->ffi_type_pointer;
- parg->value.p = copied_self->b_ptr;
- parg->size = copied_self->b_size;
- parg->obj = (PyObject *)copied_self;
+ parg->value.p = ptr;
+ parg->size = self->b_size;
+ parg->obj = obj;
return parg;
}
@@ -5700,6 +5741,10 @@ PyInit__ctypes(void)
if (PyType_Ready(&DictRemover_Type) < 0)
return NULL;
+ if (PyType_Ready(&StructParam_Type) < 0) {
+ return NULL;
+ }
+
#ifdef MS_WIN32
if (create_comerror() < 0)
return NULL;