mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	Issue #9530: Fix undefined-behaviour-inducing overflow checks in bytes and bytearray implementations.
This commit is contained in:
		
							parent
							
								
									331ea92ade
								
							
						
					
					
						commit
						cf940c701f
					
				
					 3 changed files with 49 additions and 68 deletions
				
			
		|  | @ -220,8 +220,10 @@ def test_repeat(self): | ||||||
|             self.assertRaises(TypeError, lambda: b * 3.14) |             self.assertRaises(TypeError, lambda: b * 3.14) | ||||||
|             self.assertRaises(TypeError, lambda: 3.14 * b) |             self.assertRaises(TypeError, lambda: 3.14 * b) | ||||||
|             # XXX Shouldn't bytes and bytearray agree on what to raise? |             # XXX Shouldn't bytes and bytearray agree on what to raise? | ||||||
|             self.assertRaises((OverflowError, MemoryError), |             with self.assertRaises((OverflowError, MemoryError)): | ||||||
|                               lambda: b * sys.maxsize) |                 c = b * sys.maxsize | ||||||
|  |             with self.assertRaises((OverflowError, MemoryError)): | ||||||
|  |                 b *= sys.maxsize | ||||||
| 
 | 
 | ||||||
|     def test_repeat_1char(self): |     def test_repeat_1char(self): | ||||||
|         self.assertEqual(self.type2test(b'x')*100, self.type2test([ord('x')]*100)) |         self.assertEqual(self.type2test(b'x')*100, self.type2test([ord('x')]*100)) | ||||||
|  |  | ||||||
|  | @ -310,9 +310,9 @@ bytearray_repeat(PyByteArrayObject *self, Py_ssize_t count) | ||||||
|     if (count < 0) |     if (count < 0) | ||||||
|         count = 0; |         count = 0; | ||||||
|     mysize = Py_SIZE(self); |     mysize = Py_SIZE(self); | ||||||
|     size = mysize * count; |     if (count > 0 && mysize > PY_SSIZE_T_MAX / count) | ||||||
|     if (count != 0 && size / count != mysize) |  | ||||||
|         return PyErr_NoMemory(); |         return PyErr_NoMemory(); | ||||||
|  |     size = mysize * count; | ||||||
|     result = (PyByteArrayObject *)PyByteArray_FromStringAndSize(NULL, size); |     result = (PyByteArrayObject *)PyByteArray_FromStringAndSize(NULL, size); | ||||||
|     if (result != NULL && size != 0) { |     if (result != NULL && size != 0) { | ||||||
|         if (mysize == 1) |         if (mysize == 1) | ||||||
|  | @ -335,9 +335,9 @@ bytearray_irepeat(PyByteArrayObject *self, Py_ssize_t count) | ||||||
|     if (count < 0) |     if (count < 0) | ||||||
|         count = 0; |         count = 0; | ||||||
|     mysize = Py_SIZE(self); |     mysize = Py_SIZE(self); | ||||||
|     size = mysize * count; |     if (count > 0 && mysize > PY_SSIZE_T_MAX / count) | ||||||
|     if (count != 0 && size / count != mysize) |  | ||||||
|         return PyErr_NoMemory(); |         return PyErr_NoMemory(); | ||||||
|  |     size = mysize * count; | ||||||
|     if (size < self->ob_alloc) { |     if (size < self->ob_alloc) { | ||||||
|         Py_SIZE(self) = size; |         Py_SIZE(self) = size; | ||||||
|         self->ob_bytes[Py_SIZE(self)] = '\0'; /* Trailing null byte */ |         self->ob_bytes[Py_SIZE(self)] = '\0'; /* Trailing null byte */ | ||||||
|  | @ -1505,30 +1505,28 @@ replace_interleave(PyByteArrayObject *self, | ||||||
| { | { | ||||||
|     char *self_s, *result_s; |     char *self_s, *result_s; | ||||||
|     Py_ssize_t self_len, result_len; |     Py_ssize_t self_len, result_len; | ||||||
|     Py_ssize_t count, i, product; |     Py_ssize_t count, i; | ||||||
|     PyByteArrayObject *result; |     PyByteArrayObject *result; | ||||||
| 
 | 
 | ||||||
|     self_len = PyByteArray_GET_SIZE(self); |     self_len = PyByteArray_GET_SIZE(self); | ||||||
| 
 | 
 | ||||||
|     /* 1 at the end plus 1 after every character */ |     /* 1 at the end plus 1 after every character;
 | ||||||
|     count = self_len+1; |        count = min(maxcount, self_len + 1) */ | ||||||
|     if (maxcount < count) |     if (maxcount <= self_len) | ||||||
|         count = maxcount; |         count = maxcount; | ||||||
|  |     else | ||||||
|  |         /* Can't overflow: self_len + 1 <= maxcount <= PY_SSIZE_T_MAX. */ | ||||||
|  |         count = self_len + 1; | ||||||
| 
 | 
 | ||||||
|     /* Check for overflow */ |     /* Check for overflow */ | ||||||
|     /*   result_len = count * to_len + self_len; */ |     /*   result_len = count * to_len + self_len; */ | ||||||
|     product = count * to_len; |     assert(count > 0); | ||||||
|     if (product / to_len != count) { |     if (to_len > (PY_SSIZE_T_MAX - self_len) / count) { | ||||||
|         PyErr_SetString(PyExc_OverflowError, |  | ||||||
|                         "replace string is too long"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     result_len = product + self_len; |  | ||||||
|     if (result_len < 0) { |  | ||||||
|         PyErr_SetString(PyExc_OverflowError, |         PyErr_SetString(PyExc_OverflowError, | ||||||
|                         "replace string is too long"); |                         "replace string is too long"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     result_len = count * to_len + self_len; | ||||||
| 
 | 
 | ||||||
|     if (! (result = (PyByteArrayObject *) |     if (! (result = (PyByteArrayObject *) | ||||||
|                      PyByteArray_FromStringAndSize(NULL, result_len)) ) |                      PyByteArray_FromStringAndSize(NULL, result_len)) ) | ||||||
|  | @ -1758,7 +1756,7 @@ replace_single_character(PyByteArrayObject *self, | ||||||
|     char *self_s, *result_s; |     char *self_s, *result_s; | ||||||
|     char *start, *next, *end; |     char *start, *next, *end; | ||||||
|     Py_ssize_t self_len, result_len; |     Py_ssize_t self_len, result_len; | ||||||
|     Py_ssize_t count, product; |     Py_ssize_t count; | ||||||
|     PyByteArrayObject *result; |     PyByteArrayObject *result; | ||||||
| 
 | 
 | ||||||
|     self_s = PyByteArray_AS_STRING(self); |     self_s = PyByteArray_AS_STRING(self); | ||||||
|  | @ -1772,16 +1770,12 @@ replace_single_character(PyByteArrayObject *self, | ||||||
| 
 | 
 | ||||||
|     /* use the difference between current and new, hence the "-1" */ |     /* use the difference between current and new, hence the "-1" */ | ||||||
|     /*   result_len = self_len + count * (to_len-1)  */ |     /*   result_len = self_len + count * (to_len-1)  */ | ||||||
|     product = count * (to_len-1); |     assert(count > 0); | ||||||
|     if (product / (to_len-1) != count) { |     if (to_len - 1 > (PY_SSIZE_T_MAX - self_len) / count) { | ||||||
|         PyErr_SetString(PyExc_OverflowError, "replace bytes is too long"); |         PyErr_SetString(PyExc_OverflowError, "replace bytes is too long"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     result_len = self_len + product; |     result_len = self_len + count * (to_len - 1); | ||||||
|     if (result_len < 0) { |  | ||||||
|             PyErr_SetString(PyExc_OverflowError, "replace bytes is too long"); |  | ||||||
|             return NULL; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     if ( (result = (PyByteArrayObject *) |     if ( (result = (PyByteArrayObject *) | ||||||
|           PyByteArray_FromStringAndSize(NULL, result_len)) == NULL) |           PyByteArray_FromStringAndSize(NULL, result_len)) == NULL) | ||||||
|  | @ -1825,7 +1819,7 @@ replace_substring(PyByteArrayObject *self, | ||||||
|     char *self_s, *result_s; |     char *self_s, *result_s; | ||||||
|     char *start, *next, *end; |     char *start, *next, *end; | ||||||
|     Py_ssize_t self_len, result_len; |     Py_ssize_t self_len, result_len; | ||||||
|     Py_ssize_t count, offset, product; |     Py_ssize_t count, offset; | ||||||
|     PyByteArrayObject *result; |     PyByteArrayObject *result; | ||||||
| 
 | 
 | ||||||
|     self_s = PyByteArray_AS_STRING(self); |     self_s = PyByteArray_AS_STRING(self); | ||||||
|  | @ -1842,16 +1836,12 @@ replace_substring(PyByteArrayObject *self, | ||||||
| 
 | 
 | ||||||
|     /* Check for overflow */ |     /* Check for overflow */ | ||||||
|     /*    result_len = self_len + count * (to_len-from_len) */ |     /*    result_len = self_len + count * (to_len-from_len) */ | ||||||
|     product = count * (to_len-from_len); |     assert(count > 0); | ||||||
|     if (product / (to_len-from_len) != count) { |     if (to_len - from_len > (PY_SSIZE_T_MAX - self_len) / count) { | ||||||
|         PyErr_SetString(PyExc_OverflowError, "replace bytes is too long"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     result_len = self_len + product; |  | ||||||
|     if (result_len < 0) { |  | ||||||
|         PyErr_SetString(PyExc_OverflowError, "replace bytes is too long"); |         PyErr_SetString(PyExc_OverflowError, "replace bytes is too long"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     result_len = self_len + count * (to_len - from_len); | ||||||
| 
 | 
 | ||||||
|     if ( (result = (PyByteArrayObject *) |     if ( (result = (PyByteArrayObject *) | ||||||
|           PyByteArray_FromStringAndSize(NULL, result_len)) == NULL) |           PyByteArray_FromStringAndSize(NULL, result_len)) == NULL) | ||||||
|  |  | ||||||
|  | @ -579,13 +579,14 @@ PyBytes_Repr(PyObject *obj, int smartquotes) | ||||||
|     static const char *hexdigits = "0123456789abcdef"; |     static const char *hexdigits = "0123456789abcdef"; | ||||||
|     register PyBytesObject* op = (PyBytesObject*) obj; |     register PyBytesObject* op = (PyBytesObject*) obj; | ||||||
|     Py_ssize_t length = Py_SIZE(op); |     Py_ssize_t length = Py_SIZE(op); | ||||||
|     size_t newsize = 3 + 4 * length; |     size_t newsize; | ||||||
|     PyObject *v; |     PyObject *v; | ||||||
|     if (newsize > PY_SSIZE_T_MAX || (newsize-3) / 4 != length) { |     if (length > (PY_SSIZE_T_MAX - 3) / 4) { | ||||||
|         PyErr_SetString(PyExc_OverflowError, |         PyErr_SetString(PyExc_OverflowError, | ||||||
|             "bytes object is too large to make repr"); |             "bytes object is too large to make repr"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     newsize = 3 + 4 * length; | ||||||
|     v = PyUnicode_FromUnicode(NULL, newsize); |     v = PyUnicode_FromUnicode(NULL, newsize); | ||||||
|     if (v == NULL) { |     if (v == NULL) { | ||||||
|         return NULL; |         return NULL; | ||||||
|  | @ -732,12 +733,12 @@ bytes_repeat(register PyBytesObject *a, register Py_ssize_t n) | ||||||
|     /* watch out for overflows:  the size can overflow int,
 |     /* watch out for overflows:  the size can overflow int,
 | ||||||
|      * and the # of bytes needed can overflow size_t |      * and the # of bytes needed can overflow size_t | ||||||
|      */ |      */ | ||||||
|     size = Py_SIZE(a) * n; |     if (n > 0 && Py_SIZE(a) > PY_SSIZE_T_MAX / n) { | ||||||
|     if (n && size / n != Py_SIZE(a)) { |  | ||||||
|         PyErr_SetString(PyExc_OverflowError, |         PyErr_SetString(PyExc_OverflowError, | ||||||
|             "repeated bytes are too long"); |             "repeated bytes are too long"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     size = Py_SIZE(a) * n; | ||||||
|     if (size == Py_SIZE(a) && PyBytes_CheckExact(a)) { |     if (size == Py_SIZE(a) && PyBytes_CheckExact(a)) { | ||||||
|         Py_INCREF(a); |         Py_INCREF(a); | ||||||
|         return (PyObject *)a; |         return (PyObject *)a; | ||||||
|  | @ -1687,30 +1688,28 @@ replace_interleave(PyBytesObject *self, | ||||||
| { | { | ||||||
|     char *self_s, *result_s; |     char *self_s, *result_s; | ||||||
|     Py_ssize_t self_len, result_len; |     Py_ssize_t self_len, result_len; | ||||||
|     Py_ssize_t count, i, product; |     Py_ssize_t count, i; | ||||||
|     PyBytesObject *result; |     PyBytesObject *result; | ||||||
| 
 | 
 | ||||||
|     self_len = PyBytes_GET_SIZE(self); |     self_len = PyBytes_GET_SIZE(self); | ||||||
| 
 | 
 | ||||||
|     /* 1 at the end plus 1 after every character */ |     /* 1 at the end plus 1 after every character;
 | ||||||
|     count = self_len+1; |        count = min(maxcount, self_len + 1) */ | ||||||
|     if (maxcount < count) |     if (maxcount <= self_len) | ||||||
|         count = maxcount; |         count = maxcount; | ||||||
|  |     else | ||||||
|  |         /* Can't overflow: self_len + 1 <= maxcount <= PY_SSIZE_T_MAX. */ | ||||||
|  |         count = self_len + 1; | ||||||
| 
 | 
 | ||||||
|     /* Check for overflow */ |     /* Check for overflow */ | ||||||
|     /*   result_len = count * to_len + self_len; */ |     /*   result_len = count * to_len + self_len; */ | ||||||
|     product = count * to_len; |     assert(count > 0); | ||||||
|     if (product / to_len != count) { |     if (to_len > (PY_SSIZE_T_MAX - self_len) / count) { | ||||||
|         PyErr_SetString(PyExc_OverflowError, |  | ||||||
|                         "replacement bytes are too long"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     result_len = product + self_len; |  | ||||||
|     if (result_len < 0) { |  | ||||||
|         PyErr_SetString(PyExc_OverflowError, |         PyErr_SetString(PyExc_OverflowError, | ||||||
|                         "replacement bytes are too long"); |                         "replacement bytes are too long"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     result_len = count * to_len + self_len; | ||||||
| 
 | 
 | ||||||
|     if (! (result = (PyBytesObject *) |     if (! (result = (PyBytesObject *) | ||||||
|                      PyBytes_FromStringAndSize(NULL, result_len)) ) |                      PyBytes_FromStringAndSize(NULL, result_len)) ) | ||||||
|  | @ -1939,7 +1938,7 @@ replace_single_character(PyBytesObject *self, | ||||||
|     char *self_s, *result_s; |     char *self_s, *result_s; | ||||||
|     char *start, *next, *end; |     char *start, *next, *end; | ||||||
|     Py_ssize_t self_len, result_len; |     Py_ssize_t self_len, result_len; | ||||||
|     Py_ssize_t count, product; |     Py_ssize_t count; | ||||||
|     PyBytesObject *result; |     PyBytesObject *result; | ||||||
| 
 | 
 | ||||||
|     self_s = PyBytes_AS_STRING(self); |     self_s = PyBytes_AS_STRING(self); | ||||||
|  | @ -1953,18 +1952,13 @@ replace_single_character(PyBytesObject *self, | ||||||
| 
 | 
 | ||||||
|     /* use the difference between current and new, hence the "-1" */ |     /* use the difference between current and new, hence the "-1" */ | ||||||
|     /*   result_len = self_len + count * (to_len-1)  */ |     /*   result_len = self_len + count * (to_len-1)  */ | ||||||
|     product = count * (to_len-1); |     assert(count > 0); | ||||||
|     if (product / (to_len-1) != count) { |     if (to_len - 1 > (PY_SSIZE_T_MAX - self_len) / count) { | ||||||
|         PyErr_SetString(PyExc_OverflowError, |         PyErr_SetString(PyExc_OverflowError, | ||||||
|                         "replacement bytes are too long"); |                         "replacement bytes are too long"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|     result_len = self_len + product; |     result_len = self_len + count * (to_len - 1); | ||||||
|     if (result_len < 0) { |  | ||||||
|         PyErr_SetString(PyExc_OverflowError, |  | ||||||
|                         "replacment bytes are too long"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     if ( (result = (PyBytesObject *) |     if ( (result = (PyBytesObject *) | ||||||
|           PyBytes_FromStringAndSize(NULL, result_len)) == NULL) |           PyBytes_FromStringAndSize(NULL, result_len)) == NULL) | ||||||
|  | @ -2007,7 +2001,7 @@ replace_substring(PyBytesObject *self, | ||||||
|     char *self_s, *result_s; |     char *self_s, *result_s; | ||||||
|     char *start, *next, *end; |     char *start, *next, *end; | ||||||
|     Py_ssize_t self_len, result_len; |     Py_ssize_t self_len, result_len; | ||||||
|     Py_ssize_t count, offset, product; |     Py_ssize_t count, offset; | ||||||
|     PyBytesObject *result; |     PyBytesObject *result; | ||||||
| 
 | 
 | ||||||
|     self_s = PyBytes_AS_STRING(self); |     self_s = PyBytes_AS_STRING(self); | ||||||
|  | @ -2024,18 +2018,13 @@ replace_substring(PyBytesObject *self, | ||||||
| 
 | 
 | ||||||
|     /* Check for overflow */ |     /* Check for overflow */ | ||||||
|     /*    result_len = self_len + count * (to_len-from_len) */ |     /*    result_len = self_len + count * (to_len-from_len) */ | ||||||
|     product = count * (to_len-from_len); |     assert(count > 0); | ||||||
|     if (product / (to_len-from_len) != count) { |     if (to_len - from_len > (PY_SSIZE_T_MAX - self_len) / count) { | ||||||
|         PyErr_SetString(PyExc_OverflowError, |  | ||||||
|                         "replacement bytes are too long"); |  | ||||||
|         return NULL; |  | ||||||
|     } |  | ||||||
|     result_len = self_len + product; |  | ||||||
|     if (result_len < 0) { |  | ||||||
|         PyErr_SetString(PyExc_OverflowError, |         PyErr_SetString(PyExc_OverflowError, | ||||||
|                         "replacement bytes are too long"); |                         "replacement bytes are too long"); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     result_len = self_len + count * (to_len-from_len); | ||||||
| 
 | 
 | ||||||
|     if ( (result = (PyBytesObject *) |     if ( (result = (PyBytesObject *) | ||||||
|           PyBytes_FromStringAndSize(NULL, result_len)) == NULL) |           PyBytes_FromStringAndSize(NULL, result_len)) == NULL) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mark Dickinson
						Mark Dickinson