]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-145743: Fix inconsistency after calling Struct.__init__() with invalid...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 10 Mar 2026 16:52:27 +0000 (17:52 +0100)
committerGitHub <noreply@github.com>
Tue, 10 Mar 2026 16:52:27 +0000 (16:52 +0000)
Only set the format attribute after successful (re-)initialization.
(cherry picked from commit 3f33bf83e8496737b86333bc9ec55dc3ccb3faca)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Lib/test/test_struct.py
Modules/_struct.c

index a3f8fad7a9a0f91788013d2c6d234fa33a1d91dd..0105027403ae1972356dad8aa585c8484f83c5da 100644 (file)
@@ -579,8 +579,24 @@ class StructTest(unittest.TestCase):
         # Issue 9422: there was a memory leak when reinitializing a
         # Struct instance.  This test can be used to detect the leak
         # when running with regrtest -L.
-        s = struct.Struct('i')
-        s.__init__('ii')
+        s = struct.Struct('>h')
+        s.__init__('>hh')
+        self.assertEqual(s.format, '>hh')
+        packed = b'\x00\x01\x00\x02'
+        self.assertEqual(s.pack(1, 2), packed)
+        self.assertEqual(s.unpack(packed), (1, 2))
+
+        with self.assertRaises(UnicodeEncodeError):
+            s.__init__('\udc00')
+        self.assertEqual(s.format, '>hh')
+        self.assertEqual(s.pack(1, 2), packed)
+        self.assertEqual(s.unpack(packed), (1, 2))
+
+        with self.assertRaises(struct.error):
+            s.__init__('$')
+        self.assertEqual(s.format, '>hh')
+        self.assertEqual(s.pack(1, 2), packed)
+        self.assertEqual(s.unpack(packed), (1, 2))
 
     def check_sizeof(self, format_str, number_of_codes):
         # The size of 'PyStructObject'
index c3ca125e41ab6481cf873b6692308202d88c3a42..93b913955538b678a4c05c3fc3c86eb818c5c36a 100644 (file)
@@ -1420,11 +1420,11 @@ align(Py_ssize_t size, char c, const formatdef *e)
 /* calculate the size of a format string */
 
 static int
-prepare_s(PyStructObject *self)
+prepare_s(PyStructObject *self, PyObject *format)
 {
     const formatdef *f;
     const formatdef *e;
-    formatcode *codes;
+    formatcode *codes, *codes0;
 
     const char *s;
     const char *fmt;
@@ -1434,8 +1434,8 @@ prepare_s(PyStructObject *self)
 
     _structmodulestate *state = get_struct_state_structinst(self);
 
-    fmt = PyBytes_AS_STRING(self->s_format);
-    if (strlen(fmt) != (size_t)PyBytes_GET_SIZE(self->s_format)) {
+    fmt = PyBytes_AS_STRING(format);
+    if (strlen(fmt) != (size_t)PyBytes_GET_SIZE(format)) {
         PyErr_SetString(state->StructError,
                         "embedded null character");
         return -1;
@@ -1511,13 +1511,7 @@ prepare_s(PyStructObject *self)
         PyErr_NoMemory();
         return -1;
     }
-    /* Free any s_codes value left over from a previous initialization. */
-    if (self->s_codes != NULL)
-        PyMem_Free(self->s_codes);
-    self->s_codes = codes;
-    self->s_size = size;
-    self->s_len = len;
-
+    codes0 = codes;
     s = fmt;
     size = 0;
     while ((c = *s++) != '\0') {
@@ -1557,6 +1551,14 @@ prepare_s(PyStructObject *self)
     codes->size = 0;
     codes->repeat = 0;
 
+    /* Free any s_codes value left over from a previous initialization. */
+    if (self->s_codes != NULL)
+        PyMem_Free(self->s_codes);
+    self->s_codes = codes0;
+    self->s_size = size;
+    self->s_len = len;
+    Py_XSETREF(self->s_format, Py_NewRef(format));
+
     return 0;
 
   overflow:
@@ -1622,9 +1624,8 @@ Struct___init___impl(PyStructObject *self, PyObject *format)
         return -1;
     }
 
-    Py_SETREF(self->s_format, format);
-
-    ret = prepare_s(self);
+    ret = prepare_s(self, format);
+    Py_DECREF(format);
     return ret;
 }