Skip to content

Commit 43ce558

Browse files
authored
Account for possible extension type struct padding when calculating the itemsize for the "size changed" check (GH-4894) (GH-5289)
Backported from #4894
1 parent 6af714d commit 43ce558

2 files changed

Lines changed: 40 additions & 6 deletions

File tree

Cython/Compiler/ModuleNode.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,12 +3078,12 @@ def generate_type_import_call(self, type, code, import_generator, error_code=Non
30783078
if not condition:
30793079
code.putln("") # start in new line
30803080
code.putln("#if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x050B0000")
3081-
code.putln('sizeof(%s),' % objstruct)
3081+
code.putln('sizeof(%s), __PYX_GET_STRUCT_ALIGNMENT(%s),' % (objstruct, objstruct))
30823082
code.putln("#else")
3083-
code.putln('sizeof(%s),' % sizeof_objstruct)
3083+
code.putln('sizeof(%s), __PYX_GET_STRUCT_ALIGNMENT(%s),' % (sizeof_objstruct, sizeof_objstruct))
30843084
code.putln("#endif")
30853085
else:
3086-
code.put('sizeof(%s), ' % objstruct)
3086+
code.putln('sizeof(%s), __PYX_GET_STRUCT_ALIGNMENT(%s),' % (objstruct, objstruct))
30873087

30883088
# check_size
30893089
if type.check_size and type.check_size in ('error', 'warn', 'ignore'):

Cython/Utility/ImportExport.c

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -301,13 +301,24 @@ static int __Pyx_SetPackagePathFromImportLib(PyObject *module_name) {
301301
#ifndef __PYX_HAVE_RT_ImportType_proto
302302
#define __PYX_HAVE_RT_ImportType_proto
303303

304+
#if __STDC_VERSION__ >= 201112L
305+
#include <stdalign.h>
306+
#endif
307+
308+
#if __STDC_VERSION__ >= 201112L || __cplusplus >= 201103L
309+
#define __PYX_GET_STRUCT_ALIGNMENT(s) alignof(s)
310+
#else
311+
// best guess at what the alignment could be since we can't measure it
312+
#define __PYX_GET_STRUCT_ALIGNMENT(s) sizeof(void*)
313+
#endif
314+
304315
enum __Pyx_ImportType_CheckSize {
305316
__Pyx_ImportType_CheckSize_Error = 0,
306317
__Pyx_ImportType_CheckSize_Warn = 1,
307318
__Pyx_ImportType_CheckSize_Ignore = 2
308319
};
309320

310-
static PyTypeObject *__Pyx_ImportType(PyObject* module, const char *module_name, const char *class_name, size_t size, enum __Pyx_ImportType_CheckSize check_size); /*proto*/
321+
static PyTypeObject *__Pyx_ImportType(PyObject* module, const char *module_name, const char *class_name, size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize check_size); /*proto*/
311322

312323
#endif
313324

@@ -316,13 +327,15 @@ static PyTypeObject *__Pyx_ImportType(PyObject* module, const char *module_name,
316327
#ifndef __PYX_HAVE_RT_ImportType
317328
#define __PYX_HAVE_RT_ImportType
318329
static PyTypeObject *__Pyx_ImportType(PyObject *module, const char *module_name, const char *class_name,
319-
size_t size, enum __Pyx_ImportType_CheckSize check_size)
330+
size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize check_size)
320331
{
321332
PyObject *result = 0;
322333
char warning[200];
323334
Py_ssize_t basicsize;
335+
Py_ssize_t itemsize;
324336
#ifdef Py_LIMITED_API
325337
PyObject *py_basicsize;
338+
PyObject *py_itemsize;
326339
#endif
327340

328341
result = PyObject_GetAttrString(module, class_name);
@@ -336,6 +349,7 @@ static PyTypeObject *__Pyx_ImportType(PyObject *module, const char *module_name,
336349
}
337350
#ifndef Py_LIMITED_API
338351
basicsize = ((PyTypeObject *)result)->tp_basicsize;
352+
itemsize = ((PyTypeObject *)result)->tp_itemsize;
339353
#else
340354
py_basicsize = PyObject_GetAttrString(result, "__basicsize__");
341355
if (!py_basicsize)
@@ -345,8 +359,28 @@ static PyTypeObject *__Pyx_ImportType(PyObject *module, const char *module_name,
345359
py_basicsize = 0;
346360
if (basicsize == (Py_ssize_t)-1 && PyErr_Occurred())
347361
goto bad;
362+
py_itemsize = PyObject_GetAttrString(result, "__itemsize__");
363+
if (!py_itemsize)
364+
goto bad;
365+
itemsize = PyLong_AsSsize_t(py_itemsize);
366+
Py_DECREF(py_itemsize);
367+
py_itemsize = 0;
368+
if (itemsize == (Py_ssize_t)-1 && PyErr_Occurred())
369+
goto bad;
348370
#endif
349-
if ((size_t)basicsize < size) {
371+
if (itemsize) {
372+
// If itemsize is smaller than the alignment the struct can end up with some extra
373+
// padding at the end. In this case we need to work out the maximum size that
374+
// the padding could be when calculating the range of valid struct sizes.
375+
if (size % alignment) {
376+
// if this is true we've probably calculated the alignment wrongly
377+
// (most likely because alignof isn't available)
378+
alignment = size % alignment;
379+
}
380+
if (itemsize < (Py_ssize_t)alignment)
381+
itemsize = (Py_ssize_t)alignment;
382+
}
383+
if ((size_t)(basicsize + itemsize) < size) {
350384
PyErr_Format(PyExc_ValueError,
351385
"%.200s.%.200s size changed, may indicate binary incompatibility. "
352386
"Expected %zd from C header, got %zd from PyObject",

0 commit comments

Comments
 (0)