summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_ctypes/test_structures.py63
-rw-r--r--Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst2
-rw-r--r--Modules/_ctypes/_ctypes.c10
-rw-r--r--Modules/_ctypes/stgdict.c2
4 files changed, 70 insertions, 7 deletions
diff --git a/Lib/test/test_ctypes/test_structures.py b/Lib/test/test_ctypes/test_structures.py
index 21039f0..3eafc77 100644
--- a/Lib/test/test_ctypes/test_structures.py
+++ b/Lib/test/test_ctypes/test_structures.py
@@ -1,5 +1,5 @@
import _ctypes_test
-import platform
+from platform import architecture as _architecture
import struct
import sys
import unittest
@@ -8,6 +8,7 @@ from ctypes import (CDLL, Array, Structure, Union, POINTER, sizeof, byref, align
c_uint8, c_uint16, c_uint32,
c_short, c_ushort, c_int, c_uint,
c_long, c_ulong, c_longlong, c_ulonglong, c_float, c_double)
+from ctypes.util import find_library
from struct import calcsize
from collections import namedtuple
from test import support
@@ -472,6 +473,66 @@ class StructureTestCase(unittest.TestCase):
self.assertEqual(s.first, got.first)
self.assertEqual(s.second, got.second)
+ def _test_issue18060(self, Vector):
+ # The call to atan2() should succeed if the
+ # class fields were correctly cloned in the
+ # subclasses. Otherwise, it will segfault.
+ if sys.platform == 'win32':
+ libm = CDLL(find_library('msvcrt.dll'))
+ else:
+ libm = CDLL(find_library('m'))
+
+ libm.atan2.argtypes = [Vector]
+ libm.atan2.restype = c_double
+
+ arg = Vector(y=0.0, x=-1.0)
+ self.assertAlmostEqual(libm.atan2(arg), 3.141592653589793)
+
+ @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build")
+ @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
+ def test_issue18060_a(self):
+ # This test case calls
+ # PyCStructUnionType_update_stgdict() for each
+ # _fields_ assignment, and PyCStgDict_clone()
+ # for the Mid and Vector class definitions.
+ class Base(Structure):
+ _fields_ = [('y', c_double),
+ ('x', c_double)]
+ class Mid(Base):
+ pass
+ Mid._fields_ = []
+ class Vector(Mid): pass
+ self._test_issue18060(Vector)
+
+ @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build")
+ @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
+ def test_issue18060_b(self):
+ # This test case calls
+ # PyCStructUnionType_update_stgdict() for each
+ # _fields_ assignment.
+ class Base(Structure):
+ _fields_ = [('y', c_double),
+ ('x', c_double)]
+ class Mid(Base):
+ _fields_ = []
+ class Vector(Mid):
+ _fields_ = []
+ self._test_issue18060(Vector)
+
+ @unittest.skipIf(_architecture() == ('64bit', 'WindowsPE'), "can't test Windows x64 build")
+ @unittest.skipUnless(sys.byteorder == 'little', "can't test on this platform")
+ def test_issue18060_c(self):
+ # This test case calls
+ # PyCStructUnionType_update_stgdict() for each
+ # _fields_ assignment.
+ class Base(Structure):
+ _fields_ = [('y', c_double)]
+ class Mid(Base):
+ _fields_ = []
+ class Vector(Mid):
+ _fields_ = [('x', c_double)]
+ self._test_issue18060(Vector)
+
def test_array_in_struct(self):
# See bpo-22273
diff --git a/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst b/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst
new file mode 100644
index 0000000..3fefbc3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-05-17-07-22-33.bpo-18060.5mqTQM.rst
@@ -0,0 +1,2 @@
+Fixed a class inheritance issue that can cause segfaults when deriving two or more levels of subclasses from a base class of Structure or Union.
+
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index f909a94..fc16b91 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -4354,10 +4354,10 @@ _init_pos_args(PyObject *self, PyTypeObject *type,
return index;
}
- for (i = 0;
- i < dict->length && (i+index) < PyTuple_GET_SIZE(args);
+ for (i = index;
+ i < dict->length && i < PyTuple_GET_SIZE(args);
++i) {
- PyObject *pair = PySequence_GetItem(fields, i);
+ PyObject *pair = PySequence_GetItem(fields, i - index);
PyObject *name, *val;
int res;
if (!pair)
@@ -4367,7 +4367,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type,
Py_DECREF(pair);
return -1;
}
- val = PyTuple_GET_ITEM(args, i + index);
+ val = PyTuple_GET_ITEM(args, i);
if (kwds) {
res = PyDict_Contains(kwds, name);
if (res != 0) {
@@ -4388,7 +4388,7 @@ _init_pos_args(PyObject *self, PyTypeObject *type,
if (res == -1)
return -1;
}
- return index + dict->length;
+ return dict->length;
}
static int
diff --git a/Modules/_ctypes/stgdict.c b/Modules/_ctypes/stgdict.c
index dfdb96b..fb3e20e 100644
--- a/Modules/_ctypes/stgdict.c
+++ b/Modules/_ctypes/stgdict.c
@@ -695,7 +695,7 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
stgdict->size = aligned_size;
stgdict->align = total_align;
- stgdict->length = len; /* ADD ffi_ofs? */
+ stgdict->length = ffi_ofs + len;
/*
* The value of MAX_STRUCT_SIZE depends on the platform Python is running on.