Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Lib/test/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -1612,13 +1612,26 @@ 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'
minitemsize = 4

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'
Expand Down Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
32 changes: 17 additions & 15 deletions Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Comment on lines +592 to +594

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should move this to the if (i >= 0) branch below, ditto for other functions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the review. Inside if (i >= 0) the pack doesn't run for the setitem(self, -1, v) probe in ins1(), so the overflow is only seen after the resize. I tried it and the new tests fail, with append(1e300) on an 'f' array leaving array('f', [1.0, -431602080.0]) again. I can move it below CHECK_ARRAY_BOUNDS, where b_setitem does its range check, if you prefer.


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;
}
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
Loading