diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index 05b3e3e03fc..b417bea2150 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1228,6 +1228,20 @@ class LongTest(unittest.TestCase): self.assertRaises(TypeError, myint.from_bytes, 0, 'big') self.assertRaises(TypeError, int.from_bytes, 0, 'big', True) + def test_access_to_nonexistent_digit_0(self): + # http://bugs.python.org/issue14630: A bug in _PyLong_Copy meant that + # ob_digit[0] was being incorrectly accessed for instances of a + # subclass of int, with value 0. + class Integer(int): + def __new__(cls, value=0): + self = int.__new__(cls, value) + self.foo = 'foo' + return self + + integers = [Integer(0) for i in range(1000)] + for n in map(int, integers): + self.assertEqual(n, 0) + def test_main(): support.run_unittest(LongTest) diff --git a/Misc/NEWS b/Misc/NEWS index 6d3410f177a..08a1dd3e4ba 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,12 @@ What's New in Python 3.3.0 Alpha 3? Core and Builtins ----------------- +- Issue #14630: Fix a memory access bug for instances of a subclass of int + with value 0. + +- Issue #14339: Speed improvements to bin, oct and hex functions. Patch by + Serhiy Storchaka. + - Issue #14098: New functions PyErr_GetExcInfo and PyErr_SetExcInfo. Patch by Stefan Behnel. diff --git a/Objects/longobject.c b/Objects/longobject.c index ab49f28f532..3630ae4e197 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -156,9 +156,7 @@ _PyLong_Copy(PyLongObject *src) if (i < 0) i = -(i); if (i < 2) { - sdigit ival = src->ob_digit[0]; - if (Py_SIZE(src) < 0) - ival = -ival; + sdigit ival = MEDIUM_VALUE(src); CHECK_SMALL_INT(ival); } result = _PyLong_New(i); @@ -1672,11 +1670,10 @@ _PyLong_Format(PyObject *aa, int base) { register PyLongObject *a = (PyLongObject *)aa; PyObject *v; - Py_ssize_t i, sz; + Py_ssize_t sz; Py_ssize_t size_a; - char *p; - char sign = '\0'; - char *buffer; + Py_UCS1 *p; + int negative; int bits; assert(base == 2 || base == 8 || base == 10 || base == 16); @@ -1688,6 +1685,7 @@ _PyLong_Format(PyObject *aa, int base) return NULL; } size_a = ABS(Py_SIZE(a)); + negative = Py_SIZE(a) < 0; /* Compute a rough upper bound for the length of the string */ switch (base) { @@ -1704,33 +1702,40 @@ _PyLong_Format(PyObject *aa, int base) assert(0); /* shouldn't ever get here */ bits = 0; /* to silence gcc warning */ } - /* compute length of output string: allow 2 characters for prefix and - 1 for possible '-' sign. */ - if (size_a > (PY_SSIZE_T_MAX - 3) / PyLong_SHIFT / sizeof(Py_UCS4)) { - PyErr_SetString(PyExc_OverflowError, - "int is too large to format"); - return NULL; - } - /* now size_a * PyLong_SHIFT + 3 <= PY_SSIZE_T_MAX, so the RHS below - is safe from overflow */ - sz = 3 + (size_a * PyLong_SHIFT + (bits - 1)) / bits; - assert(sz >= 0); - buffer = PyMem_Malloc(sz); - if (buffer == NULL) { - PyErr_NoMemory(); - return NULL; - } - p = &buffer[sz]; - if (Py_SIZE(a) < 0) - sign = '-'; - if (Py_SIZE(a) == 0) { + /* Compute exact length 'sz' of output string. */ + if (size_a == 0) { + sz = 3; + } + else { + Py_ssize_t size_a_in_bits; + /* Ensure overflow doesn't occur during computation of sz. */ + if (size_a > (PY_SSIZE_T_MAX - 3) / PyLong_SHIFT) { + PyErr_SetString(PyExc_OverflowError, + "int is too large to format"); + return NULL; + } + size_a_in_bits = (size_a - 1) * PyLong_SHIFT + + bits_in_digit(a->ob_digit[size_a - 1]); + /* Allow 2 characters for prefix and 1 for a '-' sign. */ + sz = 2 + negative + (size_a_in_bits + (bits - 1)) / bits; + } + + v = PyUnicode_New(sz, 'x'); + if (v == NULL) { + return NULL; + } + assert(PyUnicode_KIND(v) == PyUnicode_1BYTE_KIND); + + p = PyUnicode_1BYTE_DATA(v) + sz; + if (size_a == 0) { *--p = '0'; } else { /* JRH: special case for power-of-2 bases */ twodigits accum = 0; int accumbits = 0; /* # of bits in accum */ + Py_ssize_t i; for (i = 0; i < size_a; ++i) { accum |= (twodigits)a->ob_digit[i] << accumbits; accumbits += PyLong_SHIFT; @@ -1739,7 +1744,6 @@ _PyLong_Format(PyObject *aa, int base) char cdigit; cdigit = (char)(accum & (base - 1)); cdigit += (cdigit < 10) ? '0' : 'a'-10; - assert(p > buffer); *--p = cdigit; accumbits -= bits; accum >>= bits; @@ -1754,10 +1758,9 @@ _PyLong_Format(PyObject *aa, int base) else /* (base == 2) */ *--p = 'b'; *--p = '0'; - if (sign) - *--p = sign; - v = PyUnicode_DecodeASCII(p, &buffer[sz] - p, NULL); - PyMem_Free(buffer); + if (negative) + *--p = '-'; + assert(p == PyUnicode_1BYTE_DATA(v)); return v; }