-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
bpo-38368: Added fix for ctypes crash when handling arrays in structs… #16589
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
Changes from 2 commits
03934da
5c947c7
fd976d3
2b07ffe
4942f5c
b440691
8bc7ca9
f2f02a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -667,11 +667,37 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct | |||||
* Although the passing in registers is specific to 64-bit Linux, the | ||||||
* array-in-struct vs. pointer problem is general. But we restrict the | ||||||
* type transformation to small structs nonetheless. | ||||||
* | ||||||
* Note that although a union may be small in terms of memory usage, it | ||||||
* could contain many overlapping declarations of arrays, e.g. | ||||||
* | ||||||
* union { | ||||||
* unsigned int_8 foo [16]; | ||||||
* unsigned uint_8 bar [16]; | ||||||
* unsigned int_16 baz[8]; | ||||||
* unsigned uint_16 bozz[8]; | ||||||
* unsigned int_32 fizz[4]; | ||||||
* unsigned uint_32 buzz[4]; | ||||||
* } | ||||||
* | ||||||
* which is still only 16 bytes in size. We need to convert this into | ||||||
* the following equivalent for libffi: | ||||||
* | ||||||
* union { | ||||||
* struct { int_8 e1; int_8 e2; ... int_8 e_16; } f1; | ||||||
* struct { uint_8 e1; uint_8 e2; ... uint_8 e_16; } f2; | ||||||
* struct { int_16 e1; int_16 e2; ... int_16 e_8; } f3; | ||||||
* struct { uint_16 e1; uint_16 e2; ... uint_16 e_8; } f4; | ||||||
* struct { int_32 e1; int_32 e2; ... int_32 e_4; } f5; | ||||||
* struct { uint_32 e1; uint_32 e2; ... uint_32 e_4; } f6; | ||||||
* } | ||||||
* | ||||||
* So the struct/union needs setting up as follows: all non-array | ||||||
* elements copied across as is, and all array elements replaced with | ||||||
* an equivalent struct which has as many fields as the array has | ||||||
* elements, plus one NULL pointer. | ||||||
*/ | ||||||
ffi_type *actual_types[MAX_ELEMENTS + 1]; | ||||||
int actual_type_index = 0; | ||||||
|
||||||
memset(actual_types, 0, sizeof(actual_types)); | ||||||
for (i = 0; i < len; ++i) { | ||||||
PyObject *name, *desc; | ||||||
PyObject *pair = PySequence_GetItem(fields, i); | ||||||
|
@@ -695,14 +721,17 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct | |||||
i); | ||||||
return -1; | ||||||
} | ||||||
if (!PyCArrayTypeObject_Check(desc)) { | ||||||
/* Not an array. Just copy over the element ffi_type. */ | ||||||
actual_types[actual_type_index++] = &dict->ffi_type_pointer; | ||||||
assert(actual_type_index <= MAX_ELEMENTS); | ||||||
} | ||||||
else { | ||||||
/* | ||||||
* If it's an array, the corresponding entry in | ||||||
* stgdict->ffi_type_pointer.elements needs to be replaced with | ||||||
* a pointer to a dummy struct. | ||||||
*/ | ||||||
if (PyCArrayTypeObject_Check(desc)) { | ||||||
Py_ssize_t length = dict->length; | ||||||
StgDictObject *edict; | ||||||
ffi_type * dummy_struct; | ||||||
ffi_type ** dummy_fields; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Py_ssize_t dummy_index; | ||||||
|
||||||
edict = PyType_stgdict(dict->proto); | ||||||
if (edict == NULL) { | ||||||
|
@@ -712,37 +741,36 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct | |||||
i); | ||||||
return -1; | ||||||
} | ||||||
|
||||||
/* Do separate allocations for now. */ | ||||||
dummy_struct = PyMem_New(ffi_type, 1); | ||||||
if (dummy_struct == NULL) { | ||||||
PyErr_NoMemory(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there should be a |
||||||
return -1; | ||||||
} | ||||||
dummy_fields = PyMem_New(ffi_type *, length + 1); | ||||||
if (dummy_fields == NULL) { | ||||||
PyErr_NoMemory(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the problem is that we're doing these allocations in a loop, and unless we keep some sort of linked list or dynamically allocated structure to keep track of all our allocations (both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will have a think about an alternative allocation strategy which allows an easier recovery, but it will be potentially a little harder to follow the logic compared to what's there now. |
||||||
return -1; | ||||||
} | ||||||
memset(dummy_struct, 0, sizeof(ffi_type)); | ||||||
dummy_struct->size = length * edict->ffi_type_pointer.size; | ||||||
dummy_struct->alignment = edict->ffi_type_pointer.alignment; | ||||||
dummy_struct->type = FFI_TYPE_STRUCT; | ||||||
dummy_struct->elements = dummy_fields; | ||||||
|
||||||
stgdict->ffi_type_pointer.elements[ffi_ofs + i] = dummy_struct; | ||||||
|
||||||
/* Copy over the element's type, length times. */ | ||||||
dummy_index = 0; | ||||||
while (length > 0) { | ||||||
actual_types[actual_type_index++] = &edict->ffi_type_pointer; | ||||||
assert(actual_type_index <= MAX_ELEMENTS); | ||||||
dummy_fields[dummy_index++] = &edict->ffi_type_pointer; | ||||||
length--; | ||||||
} | ||||||
dummy_fields[dummy_index] = NULL; | ||||||
} | ||||||
Py_DECREF(pair); | ||||||
} | ||||||
|
||||||
actual_types[actual_type_index++] = NULL; | ||||||
/* | ||||||
* Replace the old elements with the new, taking into account | ||||||
* base class elements where necessary. | ||||||
*/ | ||||||
assert(stgdict->ffi_type_pointer.elements); | ||||||
PyMem_Free(stgdict->ffi_type_pointer.elements); | ||||||
stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *, | ||||||
ffi_ofs + actual_type_index); | ||||||
if (stgdict->ffi_type_pointer.elements == NULL) { | ||||||
PyErr_NoMemory(); | ||||||
return -1; 5D0A td> | ||||||
} | ||||||
if (ffi_ofs) { | ||||||
memcpy(stgdict->ffi_type_pointer.elements, | ||||||
basedict->ffi_type_pointer.elements, | ||||||
ffi_ofs * sizeof(ffi_type *)); | ||||||
|
||||||
} | ||||||
memcpy(&stgdict->ffi_type_pointer.elements[ffi_ofs], actual_types, | ||||||
actual_type_index * sizeof(ffi_type *)); | ||||||
} | ||||||
|
||||||
/* We did check that this flag was NOT set above, it must not | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.