bpo-22273: Update ctypes to correctly handle arrays in small structur… · python/cpython@16c0f6d (original) (raw)
`@@ -344,6 +344,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
`
344
344
`int pack = 0;
`
345
345
`Py_ssize_t ffi_ofs;
`
346
346
`int big_endian;
`
``
347
`+
#if defined(X86_64)
`
``
348
`+
int arrays_seen = 0;
`
``
349
`+
#endif
`
347
350
``
348
351
`/* HACK Alert: I cannot be bothered to fix ctypes.com, so there has to
`
349
352
` be a way to use the old, broken sematics: fields are not extended
`
`@@ -468,6 +471,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
`
468
471
`Py_XDECREF(pair);
`
469
472
`return -1;
`
470
473
` }
`
``
474
`+
#if defined(X86_64)
`
``
475
`+
if (PyCArrayTypeObject_Check(desc))
`
``
476
`+
arrays_seen = 1;
`
``
477
`+
#endif
`
471
478
`dict = PyType_stgdict(desc);
`
472
479
`if (dict == NULL) {
`
473
480
`Py_DECREF(pair);
`
`@@ -608,6 +615,106 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
`
608
615
`stgdict->align = total_align;
`
609
616
`stgdict->length = len; /* ADD ffi_ofs? */
`
610
617
``
``
618
`+
#if defined(X86_64)
`
``
619
+
``
620
`+
#define MAX_ELEMENTS 16
`
``
621
+
``
622
`+
if (arrays_seen && (size <= 16)) {
`
``
623
`+
/*
`
``
624
`+
- See bpo-22273. Arrays are normally treated as pointers, which is
`
``
625
`+
- fine when an array name is being passed as parameter, but not when
`
``
626
`+
- passing structures by value that contain arrays. On 64-bit Linux,
`
``
627
`+
- small structures passed by value are passed in registers, and in
`
``
628
`+
- order to do this, libffi needs to know the true type of the array
`
``
629
`+
- members of structs. Treating them as pointers breaks things.
`
``
630
`+
`
``
631
`+
- By small structures, we mean ones that are 16 bytes or less. In that
`
``
632
`+
- case, there can't be more than 16 elements after unrolling arrays,
`
``
633
`+
- as we (will) disallow bitfields. So we can collect the true ffi_type
`
``
634
`+
- values in a fixed-size local array on the stack and, if any arrays
`
``
635
`+
- were seen, replace the ffi_type_pointer.elements with a more
`
``
636
`+
- accurate set, to allow libffi to marshal them into registers
`
``
637
`+
- correctly. It means one more loop over the fields, but if we got
`
``
638
`+
- here, the structure is small, so there aren't too many of those.
`
``
639
`+
*/
`
``
640
`+
ffi_type *actual_types[MAX_ELEMENTS + 1];
`
``
641
`+
int actual_type_index = 0;
`
``
642
+
``
643
`+
memset(actual_types, 0, sizeof(actual_types));
`
``
644
`+
for (i = 0; i < len; ++i) {
`
``
645
`+
PyObject *name, *desc;
`
``
646
`+
PyObject *pair = PySequence_GetItem(fields, i);
`
``
647
`+
StgDictObject *dict;
`
``
648
`+
int bitsize = 0;
`
``
649
+
``
650
`+
if (pair == NULL) {
`
``
651
`+
return -1;
`
``
652
`+
}
`
``
653
`+
if (!PyArg_ParseTuple(pair, "UO|i", &name, &desc, &bitsize)) {
`
``
654
`+
PyErr_SetString(PyExc_TypeError,
`
``
655
`+
"'fields' must be a sequence of (name, C type) pairs");
`
``
656
`+
Py_XDECREF(pair);
`
``
657
`+
return -1;
`
``
658
`+
}
`
``
659
`+
dict = PyType_stgdict(desc);
`
``
660
`+
if (dict == NULL) {
`
``
661
`+
Py_DECREF(pair);
`
``
662
`+
PyErr_Format(PyExc_TypeError,
`
``
663
`+
"second item in fields tuple (index %zd) must be a C type",
`
``
664
`+
i);
`
``
665
`+
return -1;
`
``
666
`+
}
`
``
667
`+
if (!PyCArrayTypeObject_Check(desc)) {
`
``
668
`+
/* Not an array. Just copy over the element ffi_type. */
`
``
669
`+
actual_types[actual_type_index++] = &dict->ffi_type_pointer;
`
``
670
`+
assert(actual_type_index <= MAX_ELEMENTS);
`
``
671
`+
}
`
``
672
`+
else {
`
``
673
`+
int length = dict->length;
`
``
674
`+
StgDictObject *edict;
`
``
675
+
``
676
`+
edict = PyType_stgdict(dict->proto);
`
``
677
`+
if (edict == NULL) {
`
``
678
`+
Py_DECREF(pair);
`
``
679
`+
PyErr_Format(PyExc_TypeError,
`
``
680
`+
"second item in fields tuple (index %zd) must be a C type",
`
``
681
`+
i);
`
``
682
`+
return -1;
`
``
683
`+
}
`
``
684
`+
/* Copy over the element's type, length times. */
`
``
685
`+
while (length > 0) {
`
``
686
`+
actual_types[actual_type_index++] = &edict->ffi_type_pointer;
`
``
687
`+
assert(actual_type_index <= MAX_ELEMENTS);
`
``
688
`+
length--;
`
``
689
`+
}
`
``
690
`+
}
`
``
691
`+
Py_DECREF(pair);
`
``
692
`+
}
`
``
693
+
``
694
`+
actual_types[actual_type_index++] = NULL;
`
``
695
`+
/*
`
``
696
`+
- Replace the old elements with the new, taking into account
`
``
697
`+
- base class elements where necessary.
`
``
698
`+
*/
`
``
699
`+
assert(stgdict->ffi_type_pointer.elements);
`
``
700
`+
PyMem_Free(stgdict->ffi_type_pointer.elements);
`
``
701
`+
stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *,
`
``
702
`+
ffi_ofs + actual_type_index);
`
``
703
`+
if (stgdict->ffi_type_pointer.elements == NULL) {
`
``
704
`+
PyErr_NoMemory();
`
``
705
`+
return -1;
`
``
706
`+
}
`
``
707
`+
if (ffi_ofs) {
`
``
708
`+
memcpy(stgdict->ffi_type_pointer.elements,
`
``
709
`+
basedict->ffi_type_pointer.elements,
`
``
710
`+
ffi_ofs * sizeof(ffi_type *));
`
``
711
+
``
712
`+
}
`
``
713
`+
memcpy(&stgdict->ffi_type_pointer.elements[ffi_ofs], actual_types,
`
``
714
`+
actual_type_index * sizeof(ffi_type *));
`
``
715
`+
}
`
``
716
`+
#endif
`
``
717
+
611
718
`/* We did check that this flag was NOT set above, it must not
`
612
719
` have been set until now. */
`
613
720
`if (stgdict->flags & DICTFLAG_FINAL) {
`