From: Sergey B Kirpichev Date: Sun, 11 Jan 2026 15:52:01 +0000 (+0300) Subject: gh-78724: Raise RuntimeError's when calling methods on non-ready Struct()'s (GH-143643) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=515ae4078dffa0b74e5e5431462c2f4fe4563ffa;p=thirdparty%2FPython%2Fcpython.git gh-78724: Raise RuntimeError's when calling methods on non-ready Struct()'s (GH-143643) --- diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index bbfe19a4e0ba..88662fec60fe 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -816,6 +816,18 @@ class StructTest(ComplexesAreIdenticalMixin, unittest.TestCase): results = executor.map(exec, [code] * 5) self.assertListEqual(list(results), [None] * 5) + def test_operations_on_half_initialized_Struct(self): + S = struct.Struct.__new__(struct.Struct) + + spam = array.array('b', b' ') + self.assertRaises(RuntimeError, S.iter_unpack, spam) + self.assertRaises(RuntimeError, S.pack, 1) + self.assertRaises(RuntimeError, S.pack_into, spam, 1) + self.assertRaises(RuntimeError, S.unpack, spam) + self.assertRaises(RuntimeError, S.unpack_from, spam) + self.assertRaises(RuntimeError, getattr, S, 'format') + self.assertEqual(S.size, -1) + class UnpackIteratorTest(unittest.TestCase): """ diff --git a/Misc/NEWS.d/next/Library/2026-01-10-10-04-08.gh-issue-78724.xkXfxX.rst b/Misc/NEWS.d/next/Library/2026-01-10-10-04-08.gh-issue-78724.xkXfxX.rst new file mode 100644 index 000000000000..8a4bec4e1653 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-10-10-04-08.gh-issue-78724.xkXfxX.rst @@ -0,0 +1,3 @@ +Raise :exc:`RuntimeError`'s when user attempts to call methods on +half-initialized :class:`~struct.Struct` objects, For example, created by +``Struct.__new__(Struct)``. Patch by Sergey B Kirpichev. diff --git a/Modules/_struct.c b/Modules/_struct.c index 2acb3df3a303..a8e9021f0a30 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -1698,8 +1698,6 @@ prepare_s(PyStructObject *self) return -1; } - self->s_size = size; - self->s_len = len; codes = PyMem_Malloc((ncodes + 1) * sizeof(formatcode)); if (codes == NULL) { PyErr_NoMemory(); @@ -1709,6 +1707,8 @@ prepare_s(PyStructObject *self) if (self->s_codes != NULL) PyMem_Free(self->s_codes); self->s_codes = codes; + self->s_size = size; + self->s_len = len; s = fmt; size = 0; @@ -1897,6 +1897,14 @@ fail: return NULL; } +#define ENSURE_STRUCT_IS_READY(self) \ + do { \ + if (!(self)->s_codes) { \ + PyErr_SetString(PyExc_RuntimeError, \ + "Struct object is not initialized"); \ + return NULL; \ + } \ + } while (0); /*[clinic input] Struct.unpack @@ -1917,7 +1925,7 @@ Struct_unpack_impl(PyStructObject *self, Py_buffer *buffer) /*[clinic end generated code: output=873a24faf02e848a input=3113f8e7038b2f6c]*/ { _structmodulestate *state = get_struct_state_structinst(self); - assert(self->s_codes != NULL); + ENSURE_STRUCT_IS_READY(self); if (buffer->len != self->s_size) { PyErr_Format(state->StructError, "unpack requires a buffer of %zd bytes", @@ -1949,7 +1957,7 @@ Struct_unpack_from_impl(PyStructObject *self, Py_buffer *buffer, /*[clinic end generated code: output=57fac875e0977316 input=cafd4851d473c894]*/ { _structmodulestate *state = get_struct_state_structinst(self); - assert(self->s_codes != NULL); + ENSURE_STRUCT_IS_READY(self); if (offset < 0) { if (offset + self->s_size > 0) { @@ -2101,8 +2109,7 @@ Struct_iter_unpack_impl(PyStructObject *self, PyObject *buffer) { _structmodulestate *state = get_struct_state_structinst(self); unpackiterobject *iter; - - assert(self->s_codes != NULL); + ENSURE_STRUCT_IS_READY(self); if (self->s_size == 0) { PyErr_Format(state->StructError, @@ -2242,8 +2249,8 @@ s_pack(PyObject *self, PyObject *const *args, Py_ssize_t nargs) /* Validate arguments. */ soself = PyStructObject_CAST(self); + ENSURE_STRUCT_IS_READY(soself); assert(PyStruct_Check(self, state)); - assert(soself->s_codes != NULL); if (nargs != soself->s_len) { PyErr_Format(state->StructError, @@ -2285,8 +2292,8 @@ s_pack_into(PyObject *self, PyObject *const *args, Py_ssize_t nargs) /* Validate arguments. +1 is for the first arg as buffer. */ soself = PyStructObject_CAST(self); + ENSURE_STRUCT_IS_READY(soself); assert(PyStruct_Check(self, state)); - assert(soself->s_codes != NULL); if (nargs != (soself->s_len + 2)) { if (nargs == 0) { @@ -2373,6 +2380,7 @@ static PyObject * s_get_format(PyObject *op, void *Py_UNUSED(closure)) { PyStructObject *self = PyStructObject_CAST(op); + ENSURE_STRUCT_IS_READY(self); return PyUnicode_FromStringAndSize(PyBytes_AS_STRING(self->s_format), PyBytes_GET_SIZE(self->s_format)); }