From cf6c1f3dd4d14a1808afd7a71a188fa317df8df2 Mon Sep 17 00:00:00 2001 From: Vyron Vasileiadis Date: Mon, 28 Sep 2026 16:52:54 +0300 Subject: [PATCH] gh-158345: Don't grow an array when append, insert or extend overflows ins1() checks the item with setitem(self, -1, v) before growing the array, but since gh-156865 the 'e', 'f' and 'Zf' setters detect an overflow only when they pack into the array, which that call skips. The array then grew by one item before the real store failed. Pack into a local buffer before the bounds check, so the check call fails too, and copy the result into the array only for a real index. --- Lib/test/test_array.py | 20 ++++++++++++ ...-09-28-13-52-36.gh-issue-158345.GNlDCF.rst | 3 ++ Modules/arraymodule.c | 32 ++++++++++--------- 3 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-09-28-13-52-36.gh-issue-158345.GNlDCF.rst diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index ba9c25c835bc39..943420f4c3f4e8 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -1612,6 +1612,13 @@ def test_overflows(self): self.assertRaises(OverflowError, array.array, self.typecode, [123456]) # Overflows also float type: self.assertRaises(OverflowError, array.array, self.typecode, [1e300]) + # A failed append, insert or extend leaves the array unchanged: + for x in 123456, 1e300: + a = array.array(self.typecode, [1]) + self.assertRaises(OverflowError, a.append, x) + self.assertRaises(OverflowError, a.insert, 0, x) + self.assertRaises(OverflowError, a.extend, [x]) + self.assertEqual(a, array.array(self.typecode, [1])) class FloatTest(FPTest, unittest.TestCase): typecode = 'f' @@ -1619,6 +1626,12 @@ class FloatTest(FPTest, unittest.TestCase): def test_overflows(self): self.assertRaises(OverflowError, array.array, self.typecode, [1e300]) + # A failed append, insert or extend leaves the array unchanged: + a = array.array(self.typecode, [1]) + self.assertRaises(OverflowError, a.append, 1e300) + self.assertRaises(OverflowError, a.insert, 0, 1e300) + self.assertRaises(OverflowError, a.extend, [1e300]) + self.assertEqual(a, array.array(self.typecode, [1])) class DoubleTest(FPTest, unittest.TestCase): typecode = 'd' @@ -1649,6 +1662,13 @@ class ComplexFloatTest(CFPTest, unittest.TestCase): def test_overflows(self): self.assertRaises(OverflowError, array.array, self.typecode, [1e300]) self.assertRaises(OverflowError, array.array, self.typecode, [1e300j]) + # A failed append, insert or extend leaves the array unchanged: + for x in 1e300, 1e300j: + a = array.array(self.typecode, [1]) + self.assertRaises(OverflowError, a.append, x) + self.assertRaises(OverflowError, a.insert, 0, x) + self.assertRaises(OverflowError, a.extend, [x]) + self.assertEqual(a, array.array(self.typecode, [1])) class ComplexDoubleTest(CFPTest, unittest.TestCase): typecode = 'Zd' diff --git a/Misc/NEWS.d/next/Library/2026-09-28-13-52-36.gh-issue-158345.GNlDCF.rst b/Misc/NEWS.d/next/Library/2026-09-28-13-52-36.gh-issue-158345.GNlDCF.rst new file mode 100644 index 00000000000000..208b3e8055e54a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-28-13-52-36.gh-issue-158345.GNlDCF.rst @@ -0,0 +1,3 @@ +Fix :meth:`array.array.append`, :meth:`~array.array.insert` and +:meth:`~array.array.extend` leaving an extra item in the array when they +raise :exc:`OverflowError` for the ``'e'``, ``'f'`` or ``'Zf'`` type code. diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index ef492d143d5644..9b5afe09a5e751 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -585,15 +585,18 @@ static int e_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) { double x; + char buf[sizeof(short)]; if (!PyArg_Parse(v, "d;array item must be float", &x)) { return -1; } + if (PyFloat_Pack2(x, buf, PY_LITTLE_ENDIAN) < 0) { + return -1; + } CHECK_ARRAY_BOUNDS(ap, i); if (i >= 0) { - return PyFloat_Pack2(x, ap->ob_item + sizeof(short)*i, - PY_LITTLE_ENDIAN); + memcpy(ap->ob_item + sizeof(short)*i, buf, sizeof(buf)); } return 0; } @@ -608,14 +611,17 @@ static int f_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) { double x; + char buf[sizeof(float)]; if (!PyArg_Parse(v, "d;array item must be float", &x)) return -1; + if (PyFloat_Pack4(x, buf, PY_LITTLE_ENDIAN) < 0) { + return -1; + } CHECK_ARRAY_BOUNDS(ap, i); if (i >= 0) { - return PyFloat_Pack4(x, ap->ob_item + sizeof(float)*i, - PY_LITTLE_ENDIAN); + memcpy(ap->ob_item + sizeof(float)*i, buf, sizeof(buf)); } return 0; } @@ -653,25 +659,21 @@ static int cf_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v) { Py_complex x; + char f[8]; if (!PyArg_Parse(v, "D;array item must be complex", &x)) { return -1; } + if (PyFloat_Pack4(x.real, f, PY_LITTLE_ENDIAN) < 0 + || PyFloat_Pack4(x.imag, f + sizeof(float), PY_LITTLE_ENDIAN) < 0) + { + return -1; + } CHECK_ARRAY_BOUNDS(ap, i); if (i >= 0) { - char f[8]; - int ret = PyFloat_Pack4(x.real, f, PY_LITTLE_ENDIAN); - - if (ret) { - return ret; - } - ret = PyFloat_Pack4(x.imag, f + sizeof(float), PY_LITTLE_ENDIAN); - if (!ret) { - memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f)); - } - return ret; + memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f)); } return 0; }