Skip to content

Commit 3f33bf8

Browse files
gh-145743: Fix inconsistency after calling Struct.__init__() with invalid format (GH-145744)
Only set the format attribute after successful (re-)initialization.
1 parent bf4017b commit 3f33bf8

File tree

2 files changed

+33
-16
lines changed

2 files changed

+33
-16
lines changed

Lib/test/test_struct.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,8 +584,24 @@ def test_Struct_reinitialization(self):
584584
# Issue 9422: there was a memory leak when reinitializing a
585585
# Struct instance. This test can be used to detect the leak
586586
# when running with regrtest -L.
587-
s = struct.Struct('i')
588-
s.__init__('ii')
587+
s = struct.Struct('>h')
588+
s.__init__('>hh')
589+
self.assertEqual(s.format, '>hh')
590+
packed = b'\x00\x01\x00\x02'
591+
self.assertEqual(s.pack(1, 2), packed)
592+
self.assertEqual(s.unpack(packed), (1, 2))
593+
594+
with self.assertRaises(UnicodeEncodeError):
595+
s.__init__('\udc00')
596+
self.assertEqual(s.format, '>hh')
597+
self.assertEqual(s.pack(1, 2), packed)
598+
self.assertEqual(s.unpack(packed), (1, 2))
599+
600+
with self.assertRaises(struct.error):
601+
s.__init__('$')
602+
self.assertEqual(s.format, '>hh')
603+
self.assertEqual(s.pack(1, 2), packed)
604+
self.assertEqual(s.unpack(packed), (1, 2))
589605

590606
def check_sizeof(self, format_str, number_of_codes):
591607
# The size of 'PyStructObject'

Modules/_struct.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,11 +1620,11 @@ align(Py_ssize_t size, char c, const formatdef *e)
16201620
/* calculate the size of a format string */
16211621

16221622
static int
1623-
prepare_s(PyStructObject *self)
1623+
prepare_s(PyStructObject *self, PyObject *format)
16241624
{
16251625
const formatdef *f;
16261626
const formatdef *e;
1627-
formatcode *codes;
1627+
formatcode *codes, *codes0;
16281628

16291629
const char *s;
16301630
const char *fmt;
@@ -1634,8 +1634,8 @@ prepare_s(PyStructObject *self)
16341634

16351635
_structmodulestate *state = get_struct_state_structinst(self);
16361636

1637-
fmt = PyBytes_AS_STRING(self->s_format);
1638-
if (strlen(fmt) != (size_t)PyBytes_GET_SIZE(self->s_format)) {
1637+
fmt = PyBytes_AS_STRING(format);
1638+
if (strlen(fmt) != (size_t)PyBytes_GET_SIZE(format)) {
16391639
PyErr_SetString(state->StructError,
16401640
"embedded null character");
16411641
return -1;
@@ -1711,13 +1711,7 @@ prepare_s(PyStructObject *self)
17111711
PyErr_NoMemory();
17121712
return -1;
17131713
}
1714-
/* Free any s_codes value left over from a previous initialization. */
1715-
if (self->s_codes != NULL)
1716-
PyMem_Free(self->s_codes);
1717-
self->s_codes = codes;
1718-
self->s_size = size;
1719-
self->s_len = len;
1720-
1714+
codes0 = codes;
17211715
s = fmt;
17221716
size = 0;
17231717
while ((c = *s++) != '\0') {
@@ -1757,6 +1751,14 @@ prepare_s(PyStructObject *self)
17571751
codes->size = 0;
17581752
codes->repeat = 0;
17591753

1754+
/* Free any s_codes value left over from a previous initialization. */
1755+
if (self->s_codes != NULL)
1756+
PyMem_Free(self->s_codes);
1757+
self->s_codes = codes0;
1758+
self->s_size = size;
1759+
self->s_len = len;
1760+
Py_XSETREF(self->s_format, Py_NewRef(format));
1761+
17601762
return 0;
17611763

17621764
overflow:
@@ -1820,9 +1822,8 @@ Struct___init___impl(PyStructObject *self, PyObject *format)
18201822
return -1;
18211823
}
18221824

1823-
Py_SETREF(self->s_format, format);
1824-
1825-
ret = prepare_s(self);
1825+
ret = prepare_s(self, format);
1826+
Py_DECREF(format);
18261827
return ret;
18271828
}
18281829

0 commit comments

Comments
 (0)