]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-132673: Fix a crash with zero-alignment in `ctypes.Structure` (#132695)
authorPeter Bierma <zintensitydev@gmail.com>
Sat, 19 Apr 2025 14:29:05 +0000 (10:29 -0400)
committerGitHub <noreply@github.com>
Sat, 19 Apr 2025 14:29:05 +0000 (14:29 +0000)
Lib/test/test_ctypes/test_aligned_structures.py
Misc/NEWS.d/next/Library/2025-04-18-12-45-18.gh-issue-132673.P7Z3F1.rst [new file with mode: 0644]
Modules/_ctypes/stgdict.c

index a208fb9a00966ad97837fefd0d1260dba644bffb..8e8ac429900374768c8cdea96ab52a29125e8255 100644 (file)
@@ -1,7 +1,7 @@
 from ctypes import (
     c_char, c_uint32, c_uint16, c_ubyte, c_byte, alignment, sizeof,
     BigEndianStructure, LittleEndianStructure,
-    BigEndianUnion, LittleEndianUnion,
+    BigEndianUnion, LittleEndianUnion, Structure
 )
 import struct
 import unittest
@@ -281,6 +281,41 @@ class TestAlignedStructures(unittest.TestCase):
             self.assertEqual(main.b.y, 3)
             self.assertEqual(main.c, 4)
 
+    def test_negative_align(self):
+        for base in (Structure, LittleEndianStructure, BigEndianStructure):
+            with (
+                self.subTest(base=base),
+                self.assertRaisesRegex(
+                    ValueError,
+                    '_align_ must be a non-negative integer',
+                )
+            ):
+                class MyStructure(base):
+                    _align_ = -1
+                    _fields_ = []
+
+    def test_zero_align_no_fields(self):
+        for base in (Structure, LittleEndianStructure, BigEndianStructure):
+            with self.subTest(base=base):
+                class MyStructure(base):
+                    _align_ = 0
+                    _fields_ = []
+
+                self.assertEqual(alignment(MyStructure), 1)
+                self.assertEqual(alignment(MyStructure()), 1)
+
+    def test_zero_align_with_fields(self):
+        for base in (Structure, LittleEndianStructure, BigEndianStructure):
+            with self.subTest(base=base):
+                class MyStructure(base):
+                    _align_ = 0
+                    _fields_ = [
+                        ("x", c_ubyte),
+                    ]
+
+                self.assertEqual(alignment(MyStructure), 1)
+                self.assertEqual(alignment(MyStructure()), 1)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2025-04-18-12-45-18.gh-issue-132673.P7Z3F1.rst b/Misc/NEWS.d/next/Library/2025-04-18-12-45-18.gh-issue-132673.P7Z3F1.rst
new file mode 100644 (file)
index 0000000..db74ef6
--- /dev/null
@@ -0,0 +1,2 @@
+Fix a crash when using ``_align_ = 0`` and ``_fields_ = []`` in a
+:class:`ctypes.Structure`.
index ad82e4891c519abf6a533574f73435102cee43c4..5f5e79e5a645fa1a531530f336f465d0c2e0c2e5 100644 (file)
@@ -383,7 +383,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct
         size = 0;
         align = 0;
         union_size = 0;
-        total_align = forced_alignment;
+        total_align = forced_alignment == 0 ? 1 : forced_alignment;
         stginfo->ffi_type_pointer.type = FFI_TYPE_STRUCT;
         stginfo->ffi_type_pointer.elements = PyMem_New(ffi_type *, len + 1);
         if (stginfo->ffi_type_pointer.elements == NULL) {
@@ -570,6 +570,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct
     }
 
     /* Adjust the size according to the alignment requirements */
+    assert(total_align != 0);
     aligned_size = ((size + total_align - 1) / total_align) * total_align;
 
     if (isStruct) {