Skip to content

Commit cf6c1f3

Browse files
committed
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.
1 parent 89eee1f commit cf6c1f3

3 files changed

Lines changed: 40 additions & 15 deletions

File tree

‎Lib/test/test_array.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,13 +1612,26 @@ def test_overflows(self):
16121612
self.assertRaises(OverflowError, array.array, self.typecode, [123456])
16131613
# Overflows also float type:
16141614
self.assertRaises(OverflowError, array.array, self.typecode, [1e300])
1615+
# A failed append, insert or extend leaves the array unchanged:
1616+
for x in 123456, 1e300:
1617+
a = array.array(self.typecode, [1])
1618+
self.assertRaises(OverflowError, a.append, x)
1619+
self.assertRaises(OverflowError, a.insert, 0, x)
1620+
self.assertRaises(OverflowError, a.extend, [x])
1621+
self.assertEqual(a, array.array(self.typecode, [1]))
16151622

16161623
class FloatTest(FPTest, unittest.TestCase):
16171624
typecode = 'f'
16181625
minitemsize = 4
16191626

16201627
def test_overflows(self):
16211628
self.assertRaises(OverflowError, array.array, self.typecode, [1e300])
1629+
# A failed append, insert or extend leaves the array unchanged:
1630+
a = array.array(self.typecode, [1])
1631+
self.assertRaises(OverflowError, a.append, 1e300)
1632+
self.assertRaises(OverflowError, a.insert, 0, 1e300)
1633+
self.assertRaises(OverflowError, a.extend, [1e300])
1634+
self.assertEqual(a, array.array(self.typecode, [1]))
16221635

16231636
class DoubleTest(FPTest, unittest.TestCase):
16241637
typecode = 'd'
@@ -1649,6 +1662,13 @@ class ComplexFloatTest(CFPTest, unittest.TestCase):
16491662
def test_overflows(self):
16501663
self.assertRaises(OverflowError, array.array, self.typecode, [1e300])
16511664
self.assertRaises(OverflowError, array.array, self.typecode, [1e300j])
1665+
# A failed append, insert or extend leaves the array unchanged:
1666+
for x in 1e300, 1e300j:
1667+
a = array.array(self.typecode, [1])
1668+
self.assertRaises(OverflowError, a.append, x)
1669+
self.assertRaises(OverflowError, a.insert, 0, x)
1670+
self.assertRaises(OverflowError, a.extend, [x])
1671+
self.assertEqual(a, array.array(self.typecode, [1]))
16521672

16531673
class ComplexDoubleTest(CFPTest, unittest.TestCase):
16541674
typecode = 'Zd'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :meth:`array.array.append`, :meth:`~array.array.insert` and
2+
:meth:`~array.array.extend` leaving an extra item in the array when they
3+
raise :exc:`OverflowError` for the ``'e'``, ``'f'`` or ``'Zf'`` type code.

‎Modules/arraymodule.c‎

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -585,15 +585,18 @@ static int
585585
e_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
586586
{
587587
double x;
588+
char buf[sizeof(short)];
588589
if (!PyArg_Parse(v, "d;array item must be float", &x)) {
589590
return -1;
590591
}
592+
if (PyFloat_Pack2(x, buf, PY_LITTLE_ENDIAN) < 0) {
593+
return -1;
594+
}
591595

592596
CHECK_ARRAY_BOUNDS(ap, i);
593597

594598
if (i >= 0) {
595-
return PyFloat_Pack2(x, ap->ob_item + sizeof(short)*i,
596-
PY_LITTLE_ENDIAN);
599+
memcpy(ap->ob_item + sizeof(short)*i, buf, sizeof(buf));
597600
}
598601
return 0;
599602
}
@@ -608,14 +611,17 @@ static int
608611
f_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
609612
{
610613
double x;
614+
char buf[sizeof(float)];
611615
if (!PyArg_Parse(v, "d;array item must be float", &x))
612616
return -1;
617+
if (PyFloat_Pack4(x, buf, PY_LITTLE_ENDIAN) < 0) {
618+
return -1;
619+
}
613620

614621
CHECK_ARRAY_BOUNDS(ap, i);
615622

616623
if (i >= 0) {
617-
return PyFloat_Pack4(x, ap->ob_item + sizeof(float)*i,
618-
PY_LITTLE_ENDIAN);
624+
memcpy(ap->ob_item + sizeof(float)*i, buf, sizeof(buf));
619625
}
620626
return 0;
621627
}
@@ -653,25 +659,21 @@ static int
653659
cf_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)
654660
{
655661
Py_complex x;
662+
char f[8];
656663

657664
if (!PyArg_Parse(v, "D;array item must be complex", &x)) {
658665
return -1;
659666
}
667+
if (PyFloat_Pack4(x.real, f, PY_LITTLE_ENDIAN) < 0
668+
|| PyFloat_Pack4(x.imag, f + sizeof(float), PY_LITTLE_ENDIAN) < 0)
669+
{
670+
return -1;
671+
}
660672

661673
CHECK_ARRAY_BOUNDS(ap, i);
662674

663675
if (i >= 0) {
664-
char f[8];
665-
int ret = PyFloat_Pack4(x.real, f, PY_LITTLE_ENDIAN);
666-
667-
if (ret) {
668-
return ret;
669-
}
670-
ret = PyFloat_Pack4(x.imag, f + sizeof(float), PY_LITTLE_ENDIAN);
671-
if (!ret) {
672-
memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f));
673-
}
674-
return ret;
676+
memcpy(ap->ob_item + i*sizeof(f), &f, sizeof(f));
675677
}
676678
return 0;
677679
}

0 commit comments

Comments
 (0)