summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiro HronĨok <miro@hroncok.cz>2023-03-24 08:06:25 +0100
committerGitHub <noreply@github.com>2023-03-24 08:06:25 +0100
commit43ce5588fb97fba407e1af04195c01b4d710453c (patch)
tree09493ab3ea7e9e7076cb5e54139fa929b88be9e7
parent6af714dbd3338d8a77a4db9d88d8504fb67a188c (diff)
downloadcython-43ce5588fb97fba407e1af04195c01b4d710453c.tar.gz
Account for possible extension type struct padding when calculating the itemsize for the "size changed" check (GH-4894) (GH-5289)
Backported from https://github.com/cython/cython/pull/4894
-rw-r--r--Cython/Compiler/ModuleNode.py6
-rw-r--r--Cython/Utility/ImportExport.c40
2 files changed, 40 insertions, 6 deletions
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index 56845330d..e81b63d0a 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -3078,12 +3078,12 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if not condition:
code.putln("") # start in new line
code.putln("#if defined(PYPY_VERSION_NUM) && PYPY_VERSION_NUM < 0x050B0000")
- code.putln('sizeof(%s),' % objstruct)
+ code.putln('sizeof(%s), __PYX_GET_STRUCT_ALIGNMENT(%s),' % (objstruct, objstruct))
code.putln("#else")
- code.putln('sizeof(%s),' % sizeof_objstruct)
+ code.putln('sizeof(%s), __PYX_GET_STRUCT_ALIGNMENT(%s),' % (sizeof_objstruct, sizeof_objstruct))
code.putln("#endif")
else:
- code.put('sizeof(%s), ' % objstruct)
+ code.putln('sizeof(%s), __PYX_GET_STRUCT_ALIGNMENT(%s),' % (objstruct, objstruct))
# check_size
if type.check_size and type.check_size in ('error', 'warn', 'ignore'):
diff --git a/Cython/Utility/ImportExport.c b/Cython/Utility/ImportExport.c
index 1689c1138..e1458fc8f 100644
--- a/Cython/Utility/ImportExport.c
+++ b/Cython/Utility/ImportExport.c
@@ -301,13 +301,24 @@ set_path:
#ifndef __PYX_HAVE_RT_ImportType_proto
#define __PYX_HAVE_RT_ImportType_proto
+#if __STDC_VERSION__ >= 201112L
+#include <stdalign.h>
+#endif
+
+#if __STDC_VERSION__ >= 201112L || __cplusplus >= 201103L
+#define __PYX_GET_STRUCT_ALIGNMENT(s) alignof(s)
+#else
+// best guess at what the alignment could be since we can't measure it
+#define __PYX_GET_STRUCT_ALIGNMENT(s) sizeof(void*)
+#endif
+
enum __Pyx_ImportType_CheckSize {
__Pyx_ImportType_CheckSize_Error = 0,
__Pyx_ImportType_CheckSize_Warn = 1,
__Pyx_ImportType_CheckSize_Ignore = 2
};
-static PyTypeObject *__Pyx_ImportType(PyObject* module, const char *module_name, const char *class_name, size_t size, enum __Pyx_ImportType_CheckSize check_size); /*proto*/
+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*/
#endif
@@ -316,13 +327,15 @@ static PyTypeObject *__Pyx_ImportType(PyObject* module, const char *module_name,
#ifndef __PYX_HAVE_RT_ImportType
#define __PYX_HAVE_RT_ImportType
static PyTypeObject *__Pyx_ImportType(PyObject *module, const char *module_name, const char *class_name,
- size_t size, enum __Pyx_ImportType_CheckSize check_size)
+ size_t size, size_t alignment, enum __Pyx_ImportType_CheckSize check_size)
{
PyObject *result = 0;
char warning[200];
Py_ssize_t basicsize;
+ Py_ssize_t itemsize;
#ifdef Py_LIMITED_API
PyObject *py_basicsize;
+ PyObject *py_itemsize;
#endif
result = PyObject_GetAttrString(module, class_name);
@@ -336,6 +349,7 @@ static PyTypeObject *__Pyx_ImportType(PyObject *module, const char *module_name,
}
#ifndef Py_LIMITED_API
basicsize = ((PyTypeObject *)result)->tp_basicsize;
+ itemsize = ((PyTypeObject *)result)->tp_itemsize;
#else
py_basicsize = PyObject_GetAttrString(result, "__basicsize__");
if (!py_basicsize)
@@ -345,8 +359,28 @@ static PyTypeObject *__Pyx_ImportType(PyObject *module, const char *module_name,
py_basicsize = 0;
if (basicsize == (Py_ssize_t)-1 && PyErr_Occurred())
goto bad;
+ py_itemsize = PyObject_GetAttrString(result, "__itemsize__");
+ if (!py_itemsize)
+ goto bad;
+ itemsize = PyLong_AsSsize_t(py_itemsize);
+ Py_DECREF(py_itemsize);
+ py_itemsize = 0;
+ if (itemsize == (Py_ssize_t)-1 && PyErr_Occurred())
+ goto bad;
#endif
- if ((size_t)basicsize < size) {
+ if (itemsize) {
+ // If itemsize is smaller than the alignment the struct can end up with some extra
+ // padding at the end. In this case we need to work out the maximum size that
+ // the padding could be when calculating the range of valid struct sizes.
+ if (size % alignment) {
+ // if this is true we've probably calculated the alignment wrongly
+ // (most likely because alignof isn't available)
+ alignment = size % alignment;
+ }
+ if (itemsize < (Py_ssize_t)alignment)
+ itemsize = (Py_ssize_t)alignment;
+ }
+ if ((size_t)(basicsize + itemsize) < size) {
PyErr_Format(PyExc_ValueError,
"%.200s.%.200s size changed, may indicate binary incompatibility. "
"Expected %zd from C header, got %zd from PyObject",