# HG changeset patch # Parent 9498736fbd8fc06299671aa952a6b9b6b75c089a Issue #1621: Avoid signed overflow in ctypes arrays and bitfields diff -r 9498736fbd8f Lib/ctypes/test/test_arrays.py --- a/Lib/ctypes/test/test_arrays.py Sat Jul 23 07:32:14 2016 +0300 +++ b/Lib/ctypes/test/test_arrays.py Sun Jul 24 08:58:13 2016 +0000 @@ -1,3 +1,4 @@ +import sys import unittest from ctypes import * @@ -161,8 +162,6 @@ self.assertEqual(Y()._length_, 187) def test_bad_subclass(self): - import sys - with self.assertRaises(AttributeError): class T(Array): pass @@ -181,5 +180,12 @@ _type_ = c_int _length_ = 1.87 + def test_size_overflow(self): + under_half = (c_char * (sys.maxsize // 2)) + under_half * 2 # No overflow + over_half = (c_char * (sys.maxsize // 2 + 1)) + with self.assertRaises(OverflowError): + over_half * 2 + if __name__ == '__main__': unittest.main() diff -r 9498736fbd8f Lib/ctypes/test/test_bitfields.py --- a/Lib/ctypes/test/test_bitfields.py Sat Jul 23 07:32:14 2016 +0300 +++ b/Lib/ctypes/test/test_bitfields.py Sun Jul 24 08:58:13 2016 +0000 @@ -258,6 +258,16 @@ x.a = 0xFEDCBA9876543211 self.assertEqual(x.a, 0xFEDCBA9876543211) + @need_symbol('c_int64') + def test_int64(self): + class X(Structure): + _fields_ = [("a", c_int64, 64)] + x = X() + x.a = -10 + self.assertEqual(x.a, -10) + x.a = 0x7EDCBA9876543211 + self.assertEqual(x.a, 0x7EDCBA9876543211) + @need_symbol('c_uint32') def test_uint32_swap_little_endian(self): # Issue #23319 diff -r 9498736fbd8f Misc/NEWS --- a/Misc/NEWS Sat Jul 23 07:32:14 2016 +0300 +++ b/Misc/NEWS Sun Jul 24 08:58:13 2016 +0000 @@ -38,7 +38,8 @@ - Issue #27567: Expose the EPOLLRDHUP and POLLRDHUP constants in the select module. -- Issue #1621: Avoid signed int negation overflow in the "audioop" module. +- Issue #1621: Avoid signed integer overflow in the "audioop" and "ctypes" + modules. - Issue #27533: Release GIL in nt._isdir diff -r 9498736fbd8f Modules/_ctypes/_ctypes.c --- a/Modules/_ctypes/_ctypes.c Sat Jul 23 07:32:14 2016 +0300 +++ b/Modules/_ctypes/_ctypes.c Sun Jul 24 08:58:13 2016 +0000 @@ -1385,7 +1385,7 @@ sizeof(Py_ssize_t) * (stgdict->ndim - 1)); itemsize = itemdict->size; - if (length * itemsize < 0) { + if (length > PY_SSIZE_T_MAX / itemsize) { PyErr_SetString(PyExc_OverflowError, "array too large"); goto error; diff -r 9498736fbd8f Modules/_ctypes/cfield.c --- a/Modules/_ctypes/cfield.c Sat Jul 23 07:32:14 2016 +0300 +++ b/Modules/_ctypes/cfield.c Sun Jul 24 08:58:13 2016 +0000 @@ -427,8 +427,16 @@ #define LOW_BIT(x) ((x) & 0xFFFF) #define NUM_BITS(x) ((x) >> 16) +#ifdef HAVE_LONG_LONG +#define LONGEST_INT PY_LONG_LONG +#else +#define LONGEST_INT long +#endif + /* Doesn't work if NUM_BITS(size) == 0, but it never happens in SET() call. */ -#define BIT_MASK(type, size) (((((type)1 << (NUM_BITS(size) - 1)) - 1) << 1) + 1) +#define BIT_MASK(type, size) ((type)( \ + ((unsigned LONGEST_INT)1 << (NUM_BITS(size) - 1) << 1) \ + - 1)) /* This macro CHANGES the first parameter IN PLACE. For proper sign handling, we must first shift left, then right. @@ -439,7 +447,7 @@ v >>= (sizeof(v)*8 - NUM_BITS(size)); \ } -/* This macro RETURNS the first parameter with the bit field CHANGED. */ +/* This macro RETURNS x with the bit field CHANGED. */ #define SET(type, x, v, size) \ (NUM_BITS(size) ? \ ( ( (type)x & ~(BIT_MASK(type, size) << LOW_BIT(size)) ) | ( ((type)v & BIT_MASK(type, size)) << LOW_BIT(size) ) ) \ @@ -944,7 +952,7 @@ if (get_ulonglong(value, &val) < 0) return NULL; memcpy(&x, ptr, sizeof(x)); - x = SET(PY_LONG_LONG, x, val, size); + x = SET(unsigned PY_LONG_LONG, x, val, size); memcpy(ptr, &x, sizeof(x)); _RET(value); }