Skip to content

Commit b5082d8

Browse files
[3.13] gh-145743: Fix inconsistency after calling Struct.__init__() with invalid format (GH-145744) (GH-145764)
Only set the format attribute after successful (re-)initialization. (cherry picked from commit 3f33bf8) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 26326a0 commit b5082d8

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
@@ -579,8 +579,24 @@ def test_Struct_reinitialization(self):
579579
# Issue 9422: there was a memory leak when reinitializing a
580580
# Struct instance. This test can be used to detect the leak
581581
# when running with regrtest -L.
582-
s = struct.Struct('i')
583-
s.__init__('ii')
582+
s = struct.Struct('>h')
583+
s.__init__('>hh')
584+
self.assertEqual(s.format, '>hh')
585+
packed = b'\x00\x01\x00\x02'
586+
self.assertEqual(s.pack(1, 2), packed)
587+
self.assertEqual(s.unpack(packed), (1, 2))
588+
589+
with self.assertRaises(UnicodeEncodeError):
590+
s.__init__('\udc00')
591+
self.assertEqual(s.format, '>hh')
592+
self.assertEqual(s.pack(1, 2), packed)
593+
self.assertEqual(s.unpack(packed), (1, 2))
594+
595+
with self.assertRaises(struct.error):
596+
s.__init__('$')
597+
self.assertEqual(s.format, '>hh')
598+
self.assertEqual(s.pack(1, 2), packed)
599+
self.assertEqual(s.unpack(packed), (1, 2))
584600

585601
def check_sizeof(self, format_str, number_of_codes):
586602
# The size of 'PyStructObject'

Modules/_struct.c

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

14221422
static int
1423-
prepare_s(PyStructObject *self)
1423+
prepare_s(PyStructObject *self, PyObject *format)
14241424
{
14251425
const formatdef *f;
14261426
const formatdef *e;
1427-
formatcode *codes;
1427+
formatcode *codes, *codes0;
14281428

14291429
const char *s;
14301430
const char *fmt;
@@ -1434,8 +1434,8 @@ prepare_s(PyStructObject *self)
14341434

14351435
_structmodulestate *state = get_struct_state_structinst(self);
14361436

1437-
fmt = PyBytes_AS_STRING(self->s_format);
1438-
if (strlen(fmt) != (size_t)PyBytes_GET_SIZE(self->s_format)) {
1437+
fmt = PyBytes_AS_STRING(format);
1438+
if (strlen(fmt) != (size_t)PyBytes_GET_SIZE(format)) {
14391439
PyErr_SetString(state->StructError,
14401440
"embedded null character");
14411441
return -1;
@@ -1511,13 +1511,7 @@ prepare_s(PyStructObject *self)
15111511
PyErr_NoMemory();
15121512
return -1;
15131513
}
1514-
/* Free any s_codes value left over from a previous initialization. */
1515-
if (self->s_codes != NULL)
1516-
PyMem_Free(self->s_codes);
1517-
self->s_codes = codes;
1518-
self->s_size = size;
1519-
self->s_len = len;
1520-
1514+
codes0 = codes;
15211515
s = fmt;
15221516
size = 0;
15231517
while ((c = *s++) != '\0') {
@@ -1557,6 +1551,14 @@ prepare_s(PyStructObject *self)
15571551
codes->size = 0;
15581552
codes->repeat = 0;
15591553

1554+
/* Free any s_codes value left over from a previous initialization. */
1555+
if (self->s_codes != NULL)
1556+
PyMem_Free(self->s_codes);
1557+
self->s_codes = codes0;
1558+
self->s_size = size;
1559+
self->s_len = len;
1560+
Py_XSETREF(self->s_format, Py_NewRef(format));
1561+
15601562
return 0;
15611563

15621564
overflow:
@@ -1622,9 +1624,8 @@ Struct___init___impl(PyStructObject *self, PyObject *format)
16221624
return -1;
16231625
}
16241626

1625-
Py_SETREF(self->s_format, format);
1626-
1627-
ret = prepare_s(self);
1627+
ret = prepare_s(self, format);
1628+
Py_DECREF(format);
16281629
return ret;
16291630
}
16301631

0 commit comments

Comments
 (0)