From b3e8b3371d93451b0186cd28506e3c8b96186c37 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 3 Sep 2026 04:32:16 +0300 Subject: [PATCH 1/4] gh-156867: struct.pack() check for overflows for Zf type --- Lib/test/test_struct.py | 10 ++++++++++ .../2026-09-03-04-32-06.gh-issue-156867.hq3efC.rst | 3 +++ Modules/_struct.c | 10 +++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-09-03-04-32-06.gh-issue-156867.hq3efC.rst diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 3809f2d26438955..530bd8f65d74613 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -424,6 +424,16 @@ def test_705836(self): self.assertRaises(OverflowError, struct.pack, "Zf", big_real) + self.assertRaises(OverflowError, struct.pack, "Zf", big_imag) + self.assertRaises(OverflowError, struct.pack, "StructError, "required argument is not a complex"); return -1; } - memcpy(p, &x, sizeof(x)); - return 0; + + int ret = PyFloat_Pack4(c.real, p, PY_LITTLE_ENDIAN); + + if (ret) { + return ret; + } + return PyFloat_Pack4(c.imag, p + sizeof(float), PY_LITTLE_ENDIAN); } static int From 9201d773c6ed7b49ae9ee3c81993e055e1557bf2 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 19 Sep 2026 02:48:07 +0300 Subject: [PATCH 2/4] Update Lib/test/test_struct.py Co-authored-by: Victor Stinner --- Lib/test/test_struct.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 530bd8f65d74613..e20594c3fc8211c 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -424,15 +424,14 @@ def test_705836(self): self.assertRaises(OverflowError, struct.pack, "Zf", big_real) - self.assertRaises(OverflowError, struct.pack, "Zf", big_imag) - self.assertRaises(OverflowError, struct.pack, "Zf", " Date: Sat, 19 Sep 2026 02:59:03 +0300 Subject: [PATCH 3/4] address review: ensure buffer unmodified on overflow --- Lib/test/test_struct.py | 11 +++++++++-- Modules/_struct.c | 10 ++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index e20594c3fc8211c..a2c34acb8d95aed 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -426,13 +426,20 @@ def test_705836(self): def test_float_complex_overflow(self): for value in ( - 1e300, # big real - 1e300j, # big imag + 1e300 + 0.5j, # big real + 1.5 + 1e300j, # big imag ): for format in (">Zf", " Date: Sat, 19 Sep 2026 04:35:13 +0300 Subject: [PATCH 4/4] + fix bp/lp_float_complex + indentation --- Lib/test/test_struct.py | 12 ++++++------ Modules/_struct.c | 36 ++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index a2c34acb8d95aed..c7ec6405aa92e15 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -433,12 +433,12 @@ def test_float_complex_overflow(self): with self.subTest(value=value, format=format): self.assertRaises(OverflowError, struct.pack, format, value) - ba = bytearray(8) - try: - struct.Struct(format).pack_into(ba, 0, value) - assert False - except OverflowError: - self.assertEqual(ba, bytearray(8)) + ba = bytearray(8) + try: + struct.Struct(format).pack_into(ba, 0, value) + assert False + except OverflowError: + self.assertEqual(ba, bytearray(8)) def test_1530559(self): for code, byteorder in iter_integer_formats(): diff --git a/Modules/_struct.c b/Modules/_struct.c index 66822f6bd72ac06..fb2f40ab9766c5b 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -790,25 +790,20 @@ np_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { Py_complex c = PyComplex_AsCComplex(v); + char tmp[8]; if (c.real == -1 && PyErr_Occurred()) { PyErr_SetString(state->StructError, "required argument is not a complex"); return -1; } - - char tmp[8]; - int ret = PyFloat_Pack4(c.real, tmp, PY_LITTLE_ENDIAN); - - if (ret) { - return ret; - } - ret = PyFloat_Pack4(c.imag, tmp + 4, PY_LITTLE_ENDIAN); - if (ret) { - return ret; + if (PyFloat_Pack4(c.real, tmp, PY_LITTLE_ENDIAN) + || PyFloat_Pack4(c.imag, tmp + 4, PY_LITTLE_ENDIAN)) + { + return -1; } memcpy(p, tmp, 8); - return ret; + return 0; } static int @@ -1142,15 +1137,20 @@ static int bp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { Py_complex x = PyComplex_AsCComplex(v); + char tmp[8]; + if (x.real == -1 && PyErr_Occurred()) { PyErr_SetString(state->StructError, "required argument is not a complex"); return -1; } - if (PyFloat_Pack4(x.real, p, 0)) { + if (PyFloat_Pack4(x.real, tmp, 0) + || PyFloat_Pack4(x.imag, tmp + 4, 0)) + { return -1; } - return PyFloat_Pack4(x.imag, p + 4, 0); + memcpy(p, tmp, 8); + return 0; } static int @@ -1468,16 +1468,20 @@ static int lp_float_complex(_structmodulestate *state, char *p, PyObject *v, const formatdef *f) { Py_complex x = PyComplex_AsCComplex(v); + char tmp[8]; + if (x.real == -1 && PyErr_Occurred()) { PyErr_SetString(state->StructError, "required argument is not a complex"); return -1; } - if (PyFloat_Pack4(x.real, p, 1)) { + if (PyFloat_Pack4(x.real, tmp, 1) + || PyFloat_Pack4(x.imag, tmp + 4, 1)) + { return -1; } - return PyFloat_Pack4(x.imag, p + 4, 1); - + memcpy(p, tmp, 8); + return 0; } static int