Skip to content

Commit 8f85adc

Browse files
[3.14] gh-78724: Raise RuntimeError's when calling methods on non-ready Struct()'s (GH-143643) (GH-143695)
(cherry picked from commit 515ae40) Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
1 parent 46594d4 commit 8f85adc

File tree

3 files changed

+31
-8
lines changed

3 files changed

+31
-8
lines changed

Lib/test/test_struct.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,18 @@ def test_endian_table_init_subinterpreters(self):
816816
results = executor.map(exec, [code] * 5)
817817
self.assertListEqual(list(results), [None] * 5)
818818

819+
def test_operations_on_half_initialized_Struct(self):
820+
S = struct.Struct.__new__(struct.Struct)
821+
822+
spam = array.array('b', b' ')
823+
self.assertRaises(RuntimeError, S.iter_unpack, spam)
824+
self.assertRaises(RuntimeError, S.pack, 1)
825+
self.assertRaises(RuntimeError, S.pack_into, spam, 1)
826+
self.assertRaises(RuntimeError, S.unpack, spam)
827+
self.assertRaises(RuntimeError, S.unpack_from, spam)
828+
self.assertRaises(RuntimeError, getattr, S, 'format')
829+
self.assertEqual(S.size, -1)
830+
819831

820832
class UnpackIteratorTest(unittest.TestCase):
821833
"""
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
@@ -1698,8 +1698,6 @@ prepare_s(PyStructObject *self)
16981698
return -1;
16991699
}
17001700

1701-
self->s_size = size;
1702-
self->s_len = len;
17031701
codes = PyMem_Malloc((ncodes + 1) * sizeof(formatcode));
17041702
if (codes == NULL) {
17051703
PyErr_NoMemory();
@@ -1709,6 +1707,8 @@ prepare_s(PyStructObject *self)
17091707
if (self->s_codes != NULL)
17101708
PyMem_Free(self->s_codes);
17111709
self->s_codes = codes;
1710+
self->s_size = size;
1711+
self->s_len = len;
17121712

17131713
s = fmt;
17141714
size = 0;
@@ -1897,6 +1897,14 @@ s_unpack_internal(PyStructObject *soself, const char *startfrom,
18971897
return NULL;
18981898
}
18991899

1900+
#define ENSURE_STRUCT_IS_READY(self) \
1901+
do { \
1902+
if (!(self)->s_codes) { \
1903+
PyErr_SetString(PyExc_RuntimeError, \
1904+
"Struct object is not initialized"); \
1905+
return NULL; \
1906+
} \
1907+
} while (0);
19001908

19011909
/*[clinic input]
19021910
Struct.unpack
@@ -1917,7 +1925,7 @@ Struct_unpack_impl(PyStructObject *self, Py_buffer *buffer)
19171925
/*[clinic end generated code: output=873a24faf02e848a input=3113f8e7038b2f6c]*/
19181926
{
19191927
_structmodulestate *state = get_struct_state_structinst(self);
1920-
assert(self->s_codes != NULL);
1928+
ENSURE_STRUCT_IS_READY(self);
19211929
if (buffer->len != self->s_size) {
19221930
PyErr_Format(state->StructError,
19231931
"unpack requires a buffer of %zd bytes",
@@ -1949,7 +1957,7 @@ Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer,
19491957
/*[clinic end generated code: output=57fac875e0977316 input=cafd4851d473c894]*/
19501958
{
19511959
_structmodulestate *state = get_struct_state_structinst(self);
1952-
assert(self->s_codes != NULL);
1960+
ENSURE_STRUCT_IS_READY(self);
19531961

19541962
if (offset < 0) {
19551963
if (offset + self->s_size > 0) {
@@ -2101,8 +2109,7 @@ Struct_iter_unpack_impl(PyStructObject *self, PyObject *buffer)
21012109
{
21022110
_structmodulestate *state = get_struct_state_structinst(self);
21032111
unpackiterobject *iter;
2104-
2105-
assert(self->s_codes != NULL);
2112+
ENSURE_STRUCT_IS_READY(self);
21062113

21072114
if (self->s_size == 0) {
21082115
PyErr_Format(state->StructError,
@@ -2243,8 +2250,8 @@ s_pack(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
22432250

22442251
/* Validate arguments. */
22452252
soself = PyStructObject_CAST(self);
2253+
ENSURE_STRUCT_IS_READY(soself);
22462254
assert(PyStruct_Check(self, state));
2247-
assert(soself->s_codes != NULL);
22482255
if (nargs != soself->s_len)
22492256
{
22502257
PyErr_Format(state->StructError,
@@ -2288,8 +2295,8 @@ s_pack_into(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
22882295

22892296
/* Validate arguments. +1 is for the first arg as buffer. */
22902297
soself = PyStructObject_CAST(self);
2298+
ENSURE_STRUCT_IS_READY(soself);
22912299
assert(PyStruct_Check(self, state));
2292-
assert(soself->s_codes != NULL);
22932300
if (nargs != (soself->s_len + 2))
22942301
{
22952302
if (nargs == 0) {
@@ -2376,6 +2383,7 @@ static PyObject *
23762383
s_get_format(PyObject *op, void *Py_UNUSED(closure))
23772384
{
23782385
PyStructObject *self = PyStructObject_CAST(op);
2386+
ENSURE_STRUCT_IS_READY(self);
23792387
return PyUnicode_FromStringAndSize(PyBytes_AS_STRING(self->s_format),
23802388
PyBytes_GET_SIZE(self->s_format));
23812389
}

0 commit comments

Comments
 (0)