Skip to content

Commit 8fd4435

Browse files
authored
[3.13] gh-78724: Raise RuntimeError's when calling methods on non-ready Struct()'s (GH-143643) (GH-143714)
(cherry picked from commit 515ae40)
1 parent 1822f59 commit 8fd4435

File tree

3 files changed

+32
-8
lines changed

3 files changed

+32
-8
lines changed

Lib/test/test_struct.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,19 @@ def test_repr(self):
786786
s = struct.Struct('=i2H')
787787
self.assertEqual(repr(s), f'Struct({s.format!r})')
788788

789+
def test_operations_on_half_initialized_Struct(self):
790+
S = struct.Struct.__new__(struct.Struct)
791+
792+
spam = array.array('b', b' ')
793+
self.assertRaises(RuntimeError, S.iter_unpack, spam)
794+
self.assertRaises(RuntimeError, S.pack, 1)
795+
self.assertRaises(RuntimeError, S.pack_into, spam, 1)
796+
self.assertRaises(RuntimeError, S.unpack, spam)
797+
self.assertRaises(RuntimeError, S.unpack_from, spam)
798+
self.assertRaises(RuntimeError, getattr, S, 'format')
799+
self.assertEqual(S.size, -1)
800+
801+
789802
class UnpackIteratorTest(unittest.TestCase):
790803
"""
791804
Tests for iterative unpacking (struct.Struct.iter_unpack).
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Raise :exc:`RuntimeError`'s when user attempts to call methods on
2+
half-initialized :class:`~struct.Struct` objects, For example, created by
3+
``Struct.__new__(Struct)``. Patch by Sergey B Kirpichev.

Modules/_struct.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,8 +1498,6 @@ prepare_s(PyStructObject *self)
14981498
return -1;
14991499
}
15001500

1501-
self->s_size = size;
1502-
self->s_len = len;
15031501
codes = PyMem_Malloc((ncodes + 1) * sizeof(formatcode));
15041502
if (codes == NULL) {
15051503
PyErr_NoMemory();
@@ -1509,6 +1507,8 @@ prepare_s(PyStructObject *self)
15091507
if (self->s_codes != NULL)
15101508
PyMem_Free(self->s_codes);
15111509
self->s_codes = codes;
1510+
self->s_size = size;
1511+
self->s_len = len;
15121512

15131513
s = fmt;
15141514
size = 0;
@@ -1695,6 +1695,14 @@ s_unpack_internal(PyStructObject *soself, const char *startfrom,
16951695
return NULL;
16961696
}
16971697

1698+
#define ENSURE_STRUCT_IS_READY(self) \
1699+
do { \
1700+
if (!(self)->s_codes) { \
1701+
PyErr_SetString(PyExc_RuntimeError, \
1702+
"Struct object is not initialized"); \
1703+
return NULL; \
1704+
} \
1705+
} while (0);
16981706

16991707
/*[clinic input]
17001708
Struct.unpack
@@ -1715,7 +1723,7 @@ Struct_unpack_impl(PyStructObject *self, Py_buffer *buffer)
17151723
/*[clinic end generated code: output=873a24faf02e848a input=3113f8e7038b2f6c]*/
17161724
{
17171725
_structmodulestate *state = get_struct_state_structinst(self);
1718-
assert(self->s_codes != NULL);
1726+
ENSURE_STRUCT_IS_READY(self);
17191727
if (buffer->len != self->s_size) {
17201728
PyErr_Format(state->StructError,
17211729
"unpack requires a buffer of %zd bytes",
@@ -1747,7 +1755,7 @@ Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer,
17471755
/*[clinic end generated code: output=57fac875e0977316 input=cafd4851d473c894]*/
17481756
{
17491757
_structmodulestate *state = get_struct_state_structinst(self);
1750-
assert(self->s_codes != NULL);
1758+
ENSURE_STRUCT_IS_READY(self);
17511759

17521760
if (offset < 0) {
17531761
if (offset + self->s_size > 0) {
@@ -1890,8 +1898,7 @@ Struct_iter_unpack(PyStructObject *self, PyObject *buffer)
18901898
{
18911899
_structmodulestate *state = get_struct_state_structinst(self);
18921900
unpackiterobject *iter;
1893-
1894-
assert(self->s_codes != NULL);
1901+
ENSURE_STRUCT_IS_READY(self);
18951902

18961903
if (self->s_size == 0) {
18971904
PyErr_Format(state->StructError,
@@ -2032,8 +2039,8 @@ s_pack(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
20322039

20332040
/* Validate arguments. */
20342041
soself = (PyStructObject *)self;
2042+
ENSURE_STRUCT_IS_READY(soself);
20352043
assert(PyStruct_Check(self, state));
2036-
assert(soself->s_codes != NULL);
20372044
if (nargs != soself->s_len)
20382045
{
20392046
PyErr_Format(state->StructError,
@@ -2077,8 +2084,8 @@ s_pack_into(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
20772084

20782085
/* Validate arguments. +1 is for the first arg as buffer. */
20792086
soself = (PyStructObject *)self;
2087+
ENSURE_STRUCT_IS_READY(soself);
20802088
assert(PyStruct_Check(self, state));
2081-
assert(soself->s_codes != NULL);
20822089
if (nargs != (soself->s_len + 2))
20832090
{
20842091
if (nargs == 0) {
@@ -2164,6 +2171,7 @@ s_pack_into(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
21642171
static PyObject *
21652172
s_get_format(PyStructObject *self, void *unused)
21662173
{
2174+
ENSURE_STRUCT_IS_READY(self);
21672175
return PyUnicode_FromStringAndSize(PyBytes_AS_STRING(self->s_format),
21682176
PyBytes_GET_SIZE(self->s_format));
21692177
}

0 commit comments

Comments
 (0)