Python 2.4 and 2.5 are vulnerable to a bunch of overflows. I attached the Apple patches, it would be nice if you could add them to the package ASAP. Yes, I know, 8 patches is a lot… -- Jonathan
Index: Python-2.4.4/Objects/unicodeobject.c =================================================================== --- Python-2.4.4.orig/Objects/unicodeobject.c +++ Python-2.4.4/Objects/unicodeobject.c @@ -186,6 +186,11 @@ PyUnicodeObject *_PyUnicode_New(int leng return unicode_empty; } + /* Ensure we won't overflow the size. */ + if (length > ((INT_MAX / sizeof(Py_UNICODE)) - 1)) { + return (PyUnicodeObject *)PyErr_NoMemory(); + } + /* Unicode freelist & memory allocation */ if (unicode_freelist) { unicode = unicode_freelist; @@ -1040,6 +1045,9 @@ PyObject *PyUnicode_EncodeUTF7(const Py_ char * out; char * start; + if (cbAllocated / 5 != size) + return PyErr_NoMemory(); + if (size == 0) return PyString_FromStringAndSize(NULL, 0); @@ -1638,6 +1646,7 @@ PyUnicode_EncodeUTF16(const Py_UNICODE * { PyObject *v; unsigned char *p; + int nsize, bytesize; #ifdef Py_UNICODE_WIDE int i, pairs; #else @@ -1662,8 +1671,15 @@ PyUnicode_EncodeUTF16(const Py_UNICODE * if (s[i] >= 0x10000) pairs++; #endif - v = PyString_FromStringAndSize(NULL, - 2 * (size + pairs + (byteorder == 0))); + /* 2 * (size + pairs + (byteorder == 0)) */ + if (size > INT_MAX || + size > INT_MAX - pairs - (byteorder == 0)) + return PyErr_NoMemory(); + nsize = (size + pairs + (byteorder == 0)); + bytesize = nsize * 2; + if (bytesize / 2 != nsize) + return PyErr_NoMemory(); + v = PyString_FromStringAndSize(NULL, bytesize); if (v == NULL) return NULL; @@ -1977,6 +1993,11 @@ PyObject *unicodeescape_string(const Py_ char *p; static const char *hexdigit = "0123456789abcdef"; +#ifdef Py_UNICODE_WIDE + const int expandsize = 10; +#else + const int expandsize = 6; +#endif /* Initial allocation is based on the longest-possible unichr escape. @@ -1992,13 +2013,12 @@ PyObject *unicodeescape_string(const Py_ escape. */ + if (size > (INT_MAX - 2 - 1) / expandsize) + return PyErr_NoMemory(); + repr = PyString_FromStringAndSize(NULL, 2 -#ifdef Py_UNICODE_WIDE - + 10*size -#else - + 6*size -#endif + + expandsize*size + 1); if (repr == NULL) return NULL; @@ -2239,12 +2259,16 @@ PyObject *PyUnicode_EncodeRawUnicodeEsca char *q; static const char *hexdigit = "0123456789abcdef"; - #ifdef Py_UNICODE_WIDE - repr = PyString_FromStringAndSize(NULL, 10 * size); + const int expandsize = 10; #else - repr = PyString_FromStringAndSize(NULL, 6 * size); + const int expandsize = 6; #endif + + if (size > INT_MAX / expandsize) + return PyErr_NoMemory(); + + repr = PyString_FromStringAndSize(NULL, expandsize * size); if (repr == NULL) return NULL; if (size == 0) @@ -4289,6 +4313,11 @@ PyUnicodeObject *pad(PyUnicodeObject *se return self; } + if (left > INT_MAX - self->length || + right > INT_MAX - (left + self->length)) { + PyErr_SetString(PyExc_OverflowError, "padded string is too long"); + return NULL; + } u = _PyUnicode_New(left + self->length + right); if (u) { if (left) Index: Python-2.4.4/Objects/tupleobject.c =================================================================== --- Python-2.4.4.orig/Objects/tupleobject.c +++ Python-2.4.4/Objects/tupleobject.c @@ -60,11 +60,12 @@ PyTuple_New(register int size) int nbytes = size * sizeof(PyObject *); /* Check for overflow */ if (nbytes / sizeof(PyObject *) != (size_t)size || - (nbytes += sizeof(PyTupleObject) - sizeof(PyObject *)) - <= 0) + (nbytes > INT_MAX - sizeof(PyTupleObject) - sizeof(PyObject *))) { return PyErr_NoMemory(); } + nbytes += sizeof(PyTupleObject) - sizeof(PyObject *); + op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size); if (op == NULL) return NULL; Index: Python-2.4.4/Objects/bufferobject.c =================================================================== --- Python-2.4.4.orig/Objects/bufferobject.c +++ Python-2.4.4/Objects/bufferobject.c @@ -384,6 +384,10 @@ buffer_repeat(PyBufferObject *self, int count = 0; if (!get_buf(self, &ptr, &size)) return NULL; + if (count > INT_MAX / size) { + PyErr_SetString(PyExc_MemoryError, "result too large"); + return NULL; + } ob = PyString_FromStringAndSize(NULL, size * count); if ( ob == NULL ) return NULL; Index: Python-2.4.4/Objects/stringobject.c =================================================================== --- Python-2.4.4.orig/Objects/stringobject.c +++ Python-2.4.4/Objects/stringobject.c @@ -69,6 +69,11 @@ PyString_FromStringAndSize(const char *s return (PyObject *)op; } + if (size > INT_MAX - sizeof(PyStringObject)) { + PyErr_SetString(PyExc_OverflowError, "string is too large"); + return NULL; + } + /* Inline PyObject_NewVar */ op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); if (op == NULL) @@ -104,7 +109,7 @@ PyString_FromString(const char *str) assert(str != NULL); size = strlen(str); - if (size > INT_MAX) { + if (size > INT_MAX - sizeof(PyStringObject)) { PyErr_SetString(PyExc_OverflowError, "string is too long for a Python string"); return NULL; @@ -907,7 +912,18 @@ string_concat(register PyStringObject *a Py_INCREF(a); return (PyObject *)a; } + /* Check that string sizes are not negative, to prevent an + overflow in cases where we are passed incorrectly-created + strings with negative lengths (due to a bug in other code). + */ size = a->ob_size + b->ob_size; + if (a->ob_size < 0 || b->ob_size < 0 || + a->ob_size > INT_MAX - b->ob_size) { + PyErr_SetString(PyExc_OverflowError, + "strings are too large to concat"); + return NULL; + } + /* Inline PyObject_NewVar */ op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); if (op == NULL) Index: Python-2.4.4/Misc/NEWS =================================================================== --- Python-2.4.4.orig/Misc/NEWS +++ Python-2.4.4/Misc/NEWS @@ -23,6 +23,8 @@ What's New in Python 2.4.4c1? Core and builtins ----------------- +- Apply security patches from Apple. + - Issue #2620: Overflow checking when allocating or reallocating memory was not always being done properly in some python types and extension modules. PyMem_MALLOC, PyMem_REALLOC, PyMem_NEW and PyMem_RESIZE have Index: Python-2.4.4/Modules/mmapmodule.c =================================================================== --- Python-2.4.4.orig/Modules/mmapmodule.c +++ Python-2.4.4/Modules/mmapmodule.c @@ -223,7 +223,7 @@ mmap_read_method(mmap_object *self, return(NULL); /* silently 'adjust' out-of-range requests */ - if ((self->pos + num_bytes) > self->size) { + if (num_bytes > self->size - self->pos) { num_bytes -= (self->pos+num_bytes) - self->size; } result = Py_BuildValue("s#", self->data+self->pos, num_bytes); Index: Python-2.4.4/Modules/stropmodule.c =================================================================== --- Python-2.4.4.orig/Modules/stropmodule.c +++ Python-2.4.4/Modules/stropmodule.c @@ -214,6 +214,13 @@ strop_joinfields(PyObject *self, PyObjec return NULL; } slen = PyString_GET_SIZE(item); + if (slen > INT_MAX - reslen || + seplen > INT_MAX - reslen - seplen) { + PyErr_SetString(PyExc_OverflowError, + "input too long"); + Py_DECREF(res); + return NULL; + } while (reslen + slen + seplen >= sz) { if (_PyString_Resize(&res, sz * 2) < 0) return NULL; @@ -251,6 +258,14 @@ strop_joinfields(PyObject *self, PyObjec return NULL; } slen = PyString_GET_SIZE(item); + if (slen > INT_MAX - reslen || + seplen > INT_MAX - reslen - seplen) { + PyErr_SetString(PyExc_OverflowError, + "input too long"); + Py_DECREF(res); + Py_XDECREF(item); + return NULL; + } while (reslen + slen + seplen >= sz) { if (_PyString_Resize(&res, sz * 2) < 0) { Py_DECREF(item); Index: Python-2.4.4/Modules/gcmodule.c =================================================================== --- Python-2.4.4.orig/Modules/gcmodule.c +++ Python-2.4.4/Modules/gcmodule.c @@ -1249,7 +1249,10 @@ PyObject * _PyObject_GC_Malloc(size_t basicsize) { PyObject *op; - PyGC_Head *g = PyObject_MALLOC(sizeof(PyGC_Head) + basicsize); + PyGC_Head *g; + if (basicsize > INT_MAX - sizeof(PyGC_Head)) + return PyErr_NoMemory(); + g = PyObject_MALLOC(sizeof(PyGC_Head) + basicsize); if (g == NULL) return PyErr_NoMemory(); g->gc.gc_refs = GC_UNTRACKED; @@ -1291,6 +1294,8 @@ _PyObject_GC_Resize(PyVarObject *op, int { const size_t basicsize = _PyObject_VAR_SIZE(op->ob_type, nitems); PyGC_Head *g = AS_GC(op); + if (basicsize > INT_MAX - sizeof(PyGC_Head)) + return (PyVarObject *)PyErr_NoMemory(); g = PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize); if (g == NULL) return (PyVarObject *)PyErr_NoMemory();
r65262 | neal.norwitz | 2008-07-28 07:22:45 +0200 (Mon, 28 Jul 2008) | 11 lines Backport r65182. This change modified from using the unsigned max value to the signed max value similar to 2.5 and trunk. Issue #2620: Overflow checking when allocating or reallocating memory was not always being done properly in some python types and extension modules. PyMem_MALLOC, PyMem_REALLOC, PyMem_NEW and PyMem_RESIZE have all been updated to perform better checks and places in the code that would previously leak memory on the error path when such an allocation failed have been fixed. Index: Python-2.4.4/Include/pymem.h =================================================================== --- Python-2.4.4.orig/Include/pymem.h +++ Python-2.4.4/Include/pymem.h @@ -66,8 +66,12 @@ PyAPI_FUNC(void) PyMem_Free(void *); for malloc(0), which would be treated as an error. Some platforms would return a pointer with no memory behind it, which would break pymalloc. To solve these problems, allocate an extra byte. */ -#define PyMem_MALLOC(n) malloc((n) ? (n) : 1) -#define PyMem_REALLOC(p, n) realloc((p), (n) ? (n) : 1) +/* Returns NULL to indicate error if a negative size or size larger than + Py_ssize_t can represent is supplied. Helps prevents security holes. */ +#define PyMem_MALLOC(n) (((n) < 0 || (n) > INT_MAX) ? NULL \ + : malloc((n) ? (n) : 1)) +#define PyMem_REALLOC(p, n) (((n) < 0 || (n) > INT_MAX) ? NULL \ + : realloc((p), (n) ? (n) : 1)) #endif /* PYMALLOC_DEBUG */ @@ -80,24 +84,31 @@ PyAPI_FUNC(void) PyMem_Free(void *); * Type-oriented memory interface * ============================== * - * These are carried along for historical reasons. There's rarely a good - * reason to use them anymore (you can just as easily do the multiply and - * cast yourself). + * Allocate memory for n objects of the given type. Returns a new pointer + * or NULL if the request was too large or memory allocation failed. Use + * these macros rather than doing the multiplication yourself so that proper + * overflow checking is always done. */ #define PyMem_New(type, n) \ - ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ + ( ((n) > INT_MAX / sizeof(type)) ? NULL : \ ( (type *) PyMem_Malloc((n) * sizeof(type)) ) ) #define PyMem_NEW(type, n) \ - ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ + ( ((n) > INT_MAX / sizeof(type)) ? NULL : \ ( (type *) PyMem_MALLOC((n) * sizeof(type)) ) ) +/* + * The value of (p) is always clobbered by this macro regardless of success. + * The caller MUST check if (p) is NULL afterwards and deal with the memory + * error if so. This means the original value of (p) MUST be saved for the + * caller's memory error handler to not lose track of it. + */ #define PyMem_Resize(p, type, n) \ - ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ - ( (p) = (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) ) + ( (p) = ((n) > INT_MAX / sizeof(type)) ? NULL : \ + (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) #define PyMem_RESIZE(p, type, n) \ - ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ - ( (p) = (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) ) + ( (p) = ((n) > INT_MAX / sizeof(type)) ? NULL : \ + (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) /* In order to avoid breaking old code mixing PyObject_{New, NEW} with PyMem_{Del, DEL} and PyMem_{Free, FREE}, the PyMem "release memory" Index: Python-2.4.4/Objects/obmalloc.c =================================================================== --- Python-2.4.4.orig/Objects/obmalloc.c +++ Python-2.4.4/Objects/obmalloc.c @@ -585,6 +585,15 @@ PyObject_Malloc(size_t nbytes) uint size; /* + * Limit ourselves to INT_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for nbytes < 0 is not required. + */ + if (nbytes > INT_MAX) + return NULL; + + /* * This implicitly redirects malloc(0). */ if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { @@ -814,6 +823,15 @@ PyObject_Realloc(void *p, size_t nbytes) if (p == NULL) return PyObject_Malloc(nbytes); + /* + * Limit ourselves to INT_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for nbytes < 0 is not required. + */ + if (nbytes > INT_MAX) + return NULL; + pool = POOL_ADDR(p); if (Py_ADDRESS_IN_RANGE(p, pool)) { /* We're in charge of this block */ Index: Python-2.4.4/Misc/NEWS =================================================================== --- Python-2.4.4.orig/Misc/NEWS +++ Python-2.4.4/Misc/NEWS @@ -23,6 +23,13 @@ What's New in Python 2.4.4c1? Core and builtins ----------------- +- Issue #2620: Overflow checking when allocating or reallocating memory + was not always being done properly in some python types and extension + modules. PyMem_MALLOC, PyMem_REALLOC, PyMem_NEW and PyMem_RESIZE have + all been updated to perform better checks and places in the code that + would previously leak memory on the error path when such an allocation + failed have been fixed. + - Added checks for integer overflows, contributed by Google. Some are only available if asserts are left in the code, in cases where they can't be triggered from Python code. Index: Python-2.4.4/Modules/almodule.c =================================================================== --- Python-2.4.4.orig/Modules/almodule.c +++ Python-2.4.4/Modules/almodule.c @@ -1633,9 +1633,11 @@ al_QueryValues(PyObject *self, PyObject if (nvals < 0) goto cleanup; if (nvals > setsize) { + ALvalue *old_return_set = return_set; setsize = nvals; PyMem_RESIZE(return_set, ALvalue, setsize); if (return_set == NULL) { + return_set = old_return_set; PyErr_NoMemory(); goto cleanup; } Index: Python-2.4.4/Modules/arraymodule.c =================================================================== --- Python-2.4.4.orig/Modules/arraymodule.c +++ Python-2.4.4/Modules/arraymodule.c @@ -814,6 +814,7 @@ static int array_do_extend(arrayobject *self, PyObject *bb) { int size; + char *old_item; if (!array_Check(bb)) return array_iter_extend(self, bb); @@ -829,10 +830,11 @@ array_do_extend(arrayobject *self, PyObj return -1; } size = self->ob_size + b->ob_size; + old_item = self->ob_item; PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize); if (self->ob_item == NULL) { - PyObject_Del(self); - PyErr_NoMemory(); + self->ob_item = old_item; + PyErr_NoMemory(); return -1; } memcpy(self->ob_item + self->ob_size*self->ob_descr->itemsize, @@ -884,7 +886,7 @@ array_inplace_repeat(arrayobject *self, if (size > INT_MAX / n) { return PyErr_NoMemory(); } - PyMem_Resize(items, char, n * size); + PyMem_RESIZE(items, char, n * size); if (items == NULL) return PyErr_NoMemory(); p = items; Index: Python-2.4.4/Modules/selectmodule.c =================================================================== --- Python-2.4.4.orig/Modules/selectmodule.c +++ Python-2.4.4/Modules/selectmodule.c @@ -342,10 +342,12 @@ update_ufd_array(pollObject *self) { int i, pos; PyObject *key, *value; + struct pollfd *old_ufds = self->ufds; self->ufd_len = PyDict_Size(self->dict); - PyMem_Resize(self->ufds, struct pollfd, self->ufd_len); + PyMem_RESIZE(self->ufds, struct pollfd, self->ufd_len); if (self->ufds == NULL) { + self->ufds = old_ufds; PyErr_NoMemory(); return 0; }
r61180 | martin.v.loewis | 2008-03-02 20:20:32 +0100 (Sun, 02 Mar 2008) | 5 lines Backport of r60793: Added checks for integer overflows, contributed by Google. Some are only available if asserts are left in the code, in cases where they can't be triggered from Python code. Index: Python-2.4.4/Python/bltinmodule.c =================================================================== --- Python-2.4.4.orig/Python/bltinmodule.c +++ Python-2.4.4/Python/bltinmodule.c @@ -2376,11 +2376,43 @@ filterstring(PyObject *func, PyObject *s PyString_AS_STRING(item)[0]; } else { /* do we need more space? */ - int need = j + reslen + len-i-1; + int need = j; + + /* calculate space requirements while checking for overflow */ + if (need > INT_MAX - reslen) { + Py_DECREF(item); + goto Fail_1; + } + + need += reslen; + + if (need > INT_MAX - len) { + Py_DECREF(item); + goto Fail_1; + } + + need += len; + + if (need <= i) { + Py_DECREF(item); + goto Fail_1; + } + + need = need - i - 1; + + assert(need >= 0); + assert(outlen >= 0); + if (need > outlen) { /* overallocate, to avoid reallocations */ - if (need<2*outlen) + if (outlen > INT_MAX / 2) { + Py_DECREF(item); + return NULL; + } + + if (need<2*outlen) { need = 2*outlen; + } if (_PyString_Resize(&result, need)) { Py_DECREF(item); return NULL; @@ -2472,11 +2504,31 @@ filterunicode(PyObject *func, PyObject * else { /* do we need more space? */ int need = j + reslen + len - i - 1; + + /* check that didnt overflow */ + if ((j > INT_MAX - reslen) || + ((j + reslen) > INT_MAX - len) || + ((j + reslen + len) < i) || + ((j + reslen + len - i) <= 0)) { + Py_DECREF(item); + return NULL; + } + + assert(need >= 0); + assert(outlen >= 0); + if (need > outlen) { /* overallocate, to avoid reallocations */ - if (need < 2 * outlen) - need = 2 * outlen; + if (need < 2 * outlen) { + if (outlen > INT_MAX / 2) { + Py_DECREF(item); + return NULL; + } else { + need = 2 * outlen; + } + } + if (PyUnicode_Resize( &result, need) < 0) { Py_DECREF(item); Index: Python-2.4.4/Include/pyport.h =================================================================== --- Python-2.4.4.orig/Include/pyport.h +++ Python-2.4.4/Include/pyport.h @@ -616,6 +616,17 @@ typedef struct fd_set { #error "LONG_BIT definition appears wrong for platform (bad gcc/glibc config?)." #endif +/* Largest possible value of size_t. + SIZE_MAX is part of C99, so it might be defined on some + platforms. If it is not defined, (size_t)-1 is a portable + definition for C89, due to the way signed->unsigned + conversion is defined. */ +#ifdef SIZE_MAX +#define PY_SIZE_MAX SIZE_MAX +#else +#define PY_SIZE_MAX ((size_t)-1) +#endif + #ifdef __cplusplus } #endif Index: Python-2.4.4/Include/pymem.h =================================================================== --- Python-2.4.4.orig/Include/pymem.h +++ Python-2.4.4/Include/pymem.h @@ -86,14 +86,18 @@ PyAPI_FUNC(void) PyMem_Free(void *); */ #define PyMem_New(type, n) \ - ( (type *) PyMem_Malloc((n) * sizeof(type)) ) + ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ + ( (type *) PyMem_Malloc((n) * sizeof(type)) ) ) #define PyMem_NEW(type, n) \ - ( (type *) PyMem_MALLOC((n) * sizeof(type)) ) + ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ + ( (type *) PyMem_MALLOC((n) * sizeof(type)) ) ) #define PyMem_Resize(p, type, n) \ - ( (p) = (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) + ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ + ( (p) = (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) ) #define PyMem_RESIZE(p, type, n) \ - ( (p) = (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) + ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ + ( (p) = (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) ) /* In order to avoid breaking old code mixing PyObject_{New, NEW} with PyMem_{Del, DEL} and PyMem_{Free, FREE}, the PyMem "release memory" Index: Python-2.4.4/Objects/bufferobject.c =================================================================== --- Python-2.4.4.orig/Objects/bufferobject.c +++ Python-2.4.4/Objects/bufferobject.c @@ -167,6 +167,10 @@ PyBuffer_New(int size) "size must be zero or positive"); return NULL; } + if (sizeof(*b) > INT_MAX - size) { + /* unlikely */ + return PyErr_NoMemory(); + } /* Inline PyObject_New */ o = PyObject_MALLOC(sizeof(*b) + size); if ( o == NULL ) @@ -355,6 +359,8 @@ buffer_concat(PyBufferObject *self, PyOb if ( (count = (*pb->bf_getreadbuffer)(other, 0, &ptr2)) < 0 ) return NULL; + assert(count <= PY_SIZE_MAX - size); + ob = PyString_FromStringAndSize(NULL, size + count); p = PyString_AS_STRING(ob); memcpy(p, ptr1, size); Index: Python-2.4.4/Objects/listobject.c =================================================================== --- Python-2.4.4.orig/Objects/listobject.c +++ Python-2.4.4/Objects/listobject.c @@ -45,7 +45,16 @@ list_resize(PyListObject *self, int news * system realloc(). * The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... */ - new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6) + newsize; + new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6); + + /* check for integer overflow */ + if (new_allocated > PY_SIZE_MAX - newsize) { + PyErr_NoMemory(); + return -1; + } else { + new_allocated += newsize; + } + if (newsize == 0) new_allocated = 0; items = self->ob_item; @@ -92,8 +101,9 @@ PyList_New(int size) return NULL; } nbytes = size * sizeof(PyObject *); - /* Check for overflow */ - if (nbytes / sizeof(PyObject *) != (size_t)size) + /* Check for overflow without an actual overflow, + * which can cause compiler to optimise out */ + if (size > PY_SIZE_MAX / sizeof(PyObject *)) return PyErr_NoMemory(); if (num_free_lists) { num_free_lists--; @@ -1372,6 +1382,10 @@ merge_getmem(MergeState *ms, int need) * we don't care what's in the block. */ merge_freemem(ms); + if (need > INT_MAX / sizeof(PyObject*)) { + PyErr_NoMemory(); + return -1; + } ms->a = (PyObject **)PyMem_Malloc(need * sizeof(PyObject*)); if (ms->a) { ms->alloced = need; @@ -2550,6 +2564,8 @@ list_ass_subscript(PyListObject* self, P step = -step; } + assert(slicelength <= PY_SIZE_MAX / sizeof(PyObject*)); + garbage = (PyObject**) PyMem_MALLOC(slicelength*sizeof(PyObject*)); if (!garbage) { Index: Python-2.4.4/Misc/NEWS =================================================================== --- Python-2.4.4.orig/Misc/NEWS +++ Python-2.4.4/Misc/NEWS @@ -23,6 +23,10 @@ What's New in Python 2.4.4c1? Core and builtins ----------------- +- Added checks for integer overflows, contributed by Google. Some are + only available if asserts are left in the code, in cases where they + can't be triggered from Python code. + - Bug #1456209: In some obscure cases it was possible for a class with a custom ``__eq__()`` method to confuse dict internals when class instances were used as a dict's keys and the ``__eq__()`` method mutated the dict. Index: Python-2.4.4/Parser/node.c =================================================================== --- Python-2.4.4.orig/Parser/node.c +++ Python-2.4.4/Parser/node.c @@ -91,6 +91,9 @@ PyNode_AddChild(register node *n1, int t if (current_capacity < 0 || required_capacity < 0) return E_OVERFLOW; if (current_capacity < required_capacity) { + if (required_capacity > PY_SIZE_MAX / sizeof(node)) { + return E_NOMEM; + } n = n1->n_child; n = (node *) PyObject_REALLOC(n, required_capacity * sizeof(node)); Index: Python-2.4.4/Modules/rgbimgmodule.c =================================================================== --- Python-2.4.4.orig/Modules/rgbimgmodule.c +++ Python-2.4.4/Modules/rgbimgmodule.c @@ -269,7 +269,7 @@ longimagedata(PyObject *self, PyObject * Py_Int32 *starttab = NULL, *lengthtab = NULL; FILE *inf = NULL; IMAGE image; - int y, z, tablen; + int y, z, tablen, new_size; int xsize, ysize, zsize; int bpp, rle, cur, badorder; int rlebuflen; @@ -306,9 +306,15 @@ longimagedata(PyObject *self, PyObject * } if (rle) { tablen = ysize * zsize * sizeof(Py_Int32); + rlebuflen = (int) (1.05 * xsize +10); + if ((tablen / sizeof(Py_Int32)) != (ysize * zsize) || + rlebuflen < 0) { + PyErr_NoMemory(); + goto finally; + } + starttab = (Py_Int32 *)malloc(tablen); lengthtab = (Py_Int32 *)malloc(tablen); - rlebuflen = (int) (1.05 * xsize +10); rledat = (unsigned char *)malloc(rlebuflen); if (!starttab || !lengthtab || !rledat) { PyErr_NoMemory(); @@ -336,8 +342,14 @@ longimagedata(PyObject *self, PyObject * fseek(inf, 512 + 2 * tablen, SEEK_SET); cur = 512 + 2 * tablen; + new_size = xsize * ysize + TAGLEN; + if (new_size < 0 || (new_size * sizeof(Py_Int32)) < 0) { + PyErr_NoMemory(); + goto finally; + } + rv = PyString_FromStringAndSize((char *)NULL, - (xsize * ysize + TAGLEN) * sizeof(Py_Int32)); + new_size * sizeof(Py_Int32)); if (rv == NULL) goto finally; @@ -405,8 +417,14 @@ longimagedata(PyObject *self, PyObject * copybw((Py_Int32 *) base, xsize * ysize); } else { + new_size = xsize * ysize + TAGLEN; + if (new_size < 0 || (new_size * sizeof(Py_Int32)) < 0) { + PyErr_NoMemory(); + goto finally; + } + rv = PyString_FromStringAndSize((char *) 0, - (xsize*ysize+TAGLEN)*sizeof(Py_Int32)); + new_size*sizeof(Py_Int32)); if (rv == NULL) goto finally; @@ -595,10 +613,16 @@ longstoimage(PyObject *self, PyObject *a return NULL; } tablen = ysize * zsize * sizeof(Py_Int32); + rlebuflen = (int) (1.05 * xsize + 10); + + if ((tablen / sizeof(Py_Int32)) != (ysize * zsize) || + rlebuflen < 0 || (xsize * sizeof(Py_Int32)) < 0) { + PyErr_NoMemory(); + goto finally; + } starttab = (Py_Int32 *)malloc(tablen); lengthtab = (Py_Int32 *)malloc(tablen); - rlebuflen = (int) (1.05 * xsize + 10); rlebuf = (unsigned char *)malloc(rlebuflen); lumbuf = (unsigned char *)malloc(xsize * sizeof(Py_Int32)); if (!starttab || !lengthtab || !rlebuf || !lumbuf) { Index: Python-2.4.4/Modules/datetimemodule.c =================================================================== --- Python-2.4.4.orig/Modules/datetimemodule.c +++ Python-2.4.4/Modules/datetimemodule.c @@ -1111,6 +1111,8 @@ format_utcoffset(char *buf, size_t bufle char sign; int none; + assert(buflen >= 1); + offset = call_utcoffset(tzinfo, tzinfoarg, &none); if (offset == -1 && PyErr_Occurred()) return -1; @@ -1188,6 +1190,11 @@ wrap_strftime(PyObject *object, PyObject * a new format. Since computing the replacements for those codes * is expensive, don't unless they're actually used. */ + if (PyString_Size(format) > INT_MAX - 1) { + PyErr_NoMemory(); + goto Done; + } + totalnew = PyString_Size(format) + 1; /* realistic if no %z/%Z */ newfmt = PyString_FromStringAndSize(NULL, totalnew); if (newfmt == NULL) goto Done; Index: Python-2.4.4/Modules/cjkcodecs/multibytecodec.c =================================================================== --- Python-2.4.4.orig/Modules/cjkcodecs/multibytecodec.c +++ Python-2.4.4/Modules/cjkcodecs/multibytecodec.c @@ -100,12 +100,16 @@ get_errorcallback(const char *errors) static int expand_encodebuffer(MultibyteEncodeBuffer *buf, int esize) { - int orgpos, orgsize; + int orgpos, orgsize, incsize; orgpos = (int)((char*)buf->outbuf - PyString_AS_STRING(buf->outobj)); orgsize = PyString_GET_SIZE(buf->outobj); - if (_PyString_Resize(&buf->outobj, orgsize + ( - esize < (orgsize >> 1) ? (orgsize >> 1) | 1 : esize)) == -1) + incsize = (esize < (orgsize >> 1) ? (orgsize >> 1) | 1 : esize); + + if (orgsize > INT_MAX - incsize) + return -1; + + if (_PyString_Resize(&buf->outobj, orgsize + incsize) == -1) return -1; buf->outbuf = (unsigned char *)PyString_AS_STRING(buf->outobj) +orgpos; @@ -416,6 +420,12 @@ multibytecodec_encode(MultibyteCodec *co buf.excobj = NULL; buf.inbuf = buf.inbuf_top = *data; buf.inbuf_end = buf.inbuf_top + datalen; + + if (datalen > (INT_MAX - 16) / 2) { + PyErr_NoMemory(); + goto errorexit; + } + buf.outobj = PyString_FromStringAndSize(NULL, datalen * 2 + 16); if (buf.outobj == NULL) goto errorexit; @@ -725,6 +735,10 @@ mbstreamreader_iread(MultibyteStreamRead PyObject *ctr; char *ctrdata; + if (PyString_GET_SIZE(cres) > INT_MAX - self->pendingsize) { + PyErr_NoMemory(); + goto errorexit; + } rsize = PyString_GET_SIZE(cres) + self->pendingsize; ctr = PyString_FromStringAndSize(NULL, rsize); if (ctr == NULL) Index: Python-2.4.4/Modules/arraymodule.c =================================================================== --- Python-2.4.4.orig/Modules/arraymodule.c +++ Python-2.4.4/Modules/arraymodule.c @@ -651,6 +651,9 @@ array_concat(arrayobject *a, PyObject *b PyErr_BadArgument(); return NULL; } + if (a->ob_size > INT_MAX - b->ob_size) { + return PyErr_NoMemory(); + } size = a->ob_size + b->ob_size; np = (arrayobject *) newarrayobject(&Arraytype, size, a->ob_descr); if (np == NULL) { @@ -673,6 +676,9 @@ array_repeat(arrayobject *a, int n) int nbytes; if (n < 0) n = 0; + if ((a->ob_size != 0) && (n > INT_MAX / a->ob_size)) { + return PyErr_NoMemory(); + } size = a->ob_size * n; np = (arrayobject *) newarrayobject(&Arraytype, size, a->ob_descr); if (np == NULL) @@ -817,6 +823,11 @@ array_do_extend(arrayobject *self, PyObj "can only extend with array of same kind"); return -1; } + if ((self->ob_size > INT_MAX - b->ob_size) || + ((self->ob_size + b->ob_size) > INT_MAX / self->ob_descr->itemsize)) { + PyErr_NoMemory(); + return -1; + } size = self->ob_size + b->ob_size; PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize); if (self->ob_item == NULL) { @@ -858,6 +869,10 @@ array_inplace_repeat(arrayobject *self, if (n < 0) n = 0; items = self->ob_item; + if ((self->ob_descr->itemsize != 0) && + (self->ob_size > INT_MAX / self->ob_descr->itemsize)) { + return PyErr_NoMemory(); + } size = self->ob_size * self->ob_descr->itemsize; if (n == 0) { PyMem_FREE(items); @@ -866,6 +881,9 @@ array_inplace_repeat(arrayobject *self, self->allocated = 0; } else { + if (size > INT_MAX / n) { + return PyErr_NoMemory(); + } PyMem_Resize(items, char, n * size); if (items == NULL) return PyErr_NoMemory(); @@ -1278,6 +1296,9 @@ array_fromlist(arrayobject *self, PyObje if ((*self->ob_descr->setitem)(self, self->ob_size - n + i, v) != 0) { self->ob_size -= n; + if (itemsize && (self->ob_size > INT_MAX / itemsize)) { + return PyErr_NoMemory(); + } PyMem_RESIZE(item, char, self->ob_size * itemsize); self->ob_item = item; @@ -1337,6 +1358,10 @@ array_fromstring(arrayobject *self, PyOb n = n / itemsize; if (n > 0) { char *item = self->ob_item; + if ((n > INT_MAX - self->ob_size) || + ((self->ob_size + n) > INT_MAX / itemsize)) { + return PyErr_NoMemory(); + } PyMem_RESIZE(item, char, (self->ob_size + n) * itemsize); if (item == NULL) { PyErr_NoMemory(); @@ -1362,8 +1387,12 @@ values,as if it had been read from a fil static PyObject * array_tostring(arrayobject *self, PyObject *unused) { - return PyString_FromStringAndSize(self->ob_item, + if (self->ob_size <= INT_MAX / self->ob_descr->itemsize) { + return PyString_FromStringAndSize(self->ob_item, self->ob_size * self->ob_descr->itemsize); + } else { + return PyErr_NoMemory(); + } } PyDoc_STRVAR(tostring_doc, @@ -1391,6 +1420,9 @@ array_fromunicode(arrayobject *self, PyO } if (n > 0) { Py_UNICODE *item = (Py_UNICODE *) self->ob_item; + if (self->ob_size > INT_MAX - n) { + return PyErr_NoMemory(); + } PyMem_RESIZE(item, Py_UNICODE, self->ob_size + n); if (item == NULL) { PyErr_NoMemory(); Index: Python-2.4.4/Modules/cStringIO.c =================================================================== --- Python-2.4.4.orig/Modules/cStringIO.c +++ Python-2.4.4/Modules/cStringIO.c @@ -121,6 +121,7 @@ PyDoc_STRVAR(IO_getval__doc__, static PyObject * IO_cgetval(PyObject *self) { UNLESS (IO__opencheck(IOOOBJECT(self))) return NULL; + assert(IOOOBJECT(self)->pos >= 0); return PyString_FromStringAndSize(((IOobject*)self)->buf, ((IOobject*)self)->pos); } @@ -139,6 +140,7 @@ IO_getval(IOobject *self, PyObject *args } else s=self->string_size; + assert(self->pos >= 0); return PyString_FromStringAndSize(self->buf, s); } @@ -158,6 +160,8 @@ IO_cread(PyObject *self, char **output, int l; UNLESS (IO__opencheck(IOOOBJECT(self))) return -1; + assert(IOOOBJECT(self)->pos >= 0); + assert(IOOOBJECT(self)->string_size >= 0); l = ((IOobject*)self)->string_size - ((IOobject*)self)->pos; if (n < 0 || n > l) { n = l; @@ -197,6 +201,11 @@ IO_creadline(PyObject *self, char **outp *output=((IOobject*)self)->buf + ((IOobject*)self)->pos; l = n - ((IOobject*)self)->buf - ((IOobject*)self)->pos; + + assert(IOOOBJECT(self)->pos <= INT_MAX - l); + assert(IOOOBJECT(self)->pos >= 0); + assert(IOOOBJECT(self)->string_size >= 0); + ((IOobject*)self)->pos += l; return l; } @@ -215,6 +224,7 @@ IO_readline(IOobject *self, PyObject *ar n -= m; self->pos -= m; } + assert(IOOOBJECT(self)->pos >= 0); return PyString_FromStringAndSize(output, n); } @@ -277,6 +287,7 @@ IO_tell(IOobject *self, PyObject *unused UNLESS (IO__opencheck(self)) return NULL; + assert(self->pos >= 0); return PyInt_FromLong(self->pos); } Index: Python-2.4.4/Modules/stropmodule.c =================================================================== --- Python-2.4.4.orig/Modules/stropmodule.c +++ Python-2.4.4/Modules/stropmodule.c @@ -576,7 +576,7 @@ strop_expandtabs(PyObject *self, PyObjec char* e; char* p; char* q; - int i, j; + int i, j, old_j; PyObject* out; char* string; int stringlen; @@ -593,12 +593,18 @@ strop_expandtabs(PyObject *self, PyObjec } /* First pass: determine size of output string */ - i = j = 0; /* j: current column; i: total of previous lines */ + i = j = old_j = 0; /* j: current column; i: total of previous lines */ e = string + stringlen; for (p = string; p < e; p++) { - if (*p == '\t') + if (*p == '\t') { j += tabsize - (j%tabsize); - else { + if (old_j > j) { + PyErr_SetString(PyExc_OverflowError, + "new string is too long"); + return NULL; + } + old_j = j; + } else { j++; if (*p == '\n') { i += j; @@ -607,6 +613,11 @@ strop_expandtabs(PyObject *self, PyObjec } } + if ((i + j) < 0) { + PyErr_SetString(PyExc_OverflowError, "new string is too long"); + return NULL; + } + /* Second pass: create output string and fill it */ out = PyString_FromStringAndSize(NULL, i+j); if (out == NULL) Index: Python-2.4.4/Modules/binascii.c =================================================================== --- Python-2.4.4.orig/Modules/binascii.c +++ Python-2.4.4/Modules/binascii.c @@ -194,6 +194,8 @@ binascii_a2b_uu(PyObject *self, PyObject if ( !PyArg_ParseTuple(args, "t#:a2b_uu", &ascii_data, &ascii_len) ) return NULL; + assert(ascii_len >= 0); + /* First byte: binary data length (in bytes) */ bin_len = (*ascii_data++ - ' ') & 077; ascii_len--; @@ -347,6 +349,11 @@ binascii_a2b_base64(PyObject *self, PyOb if ( !PyArg_ParseTuple(args, "t#:a2b_base64", &ascii_data, &ascii_len) ) return NULL; + assert(ascii_len >= 0); + + if (ascii_len > INT_MAX - 3) + return PyErr_NoMemory(); + bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */ /* Allocate the buffer */ @@ -436,6 +443,9 @@ binascii_b2a_base64(PyObject *self, PyOb if ( !PyArg_ParseTuple(args, "s#:b2a_base64", &bin_data, &bin_len) ) return NULL; + + assert(bin_len >= 0); + if ( bin_len > BASE64_MAXBIN ) { PyErr_SetString(Error, "Too much data for base64 line"); return NULL; @@ -491,6 +501,11 @@ binascii_a2b_hqx(PyObject *self, PyObjec if ( !PyArg_ParseTuple(args, "t#:a2b_hqx", &ascii_data, &len) ) return NULL; + assert(len >= 0); + + if (len > INT_MAX - 2) + return PyErr_NoMemory(); + /* Allocate a string that is too big (fixed later) Add two to the initial length to prevent interning which would preclude subsequent resizing. */ @@ -554,6 +569,11 @@ binascii_rlecode_hqx(PyObject *self, PyO if ( !PyArg_ParseTuple(args, "s#:rlecode_hqx", &in_data, &len) ) return NULL; + assert(len >= 0); + + if (len > INT_MAX / 2 - 2) + return PyErr_NoMemory(); + /* Worst case: output is twice as big as input (fixed later) */ if ( (rv=PyString_FromStringAndSize(NULL, len*2+2)) == NULL ) return NULL; @@ -603,6 +623,11 @@ binascii_b2a_hqx(PyObject *self, PyObjec if ( !PyArg_ParseTuple(args, "s#:b2a_hqx", &bin_data, &len) ) return NULL; + assert(len >= 0); + + if (len > INT_MAX / 2 - 2) + return PyErr_NoMemory(); + /* Allocate a buffer that is at least large enough */ if ( (rv=PyString_FromStringAndSize(NULL, len*2+2)) == NULL ) return NULL; @@ -641,9 +666,13 @@ binascii_rledecode_hqx(PyObject *self, P if ( !PyArg_ParseTuple(args, "s#:rledecode_hqx", &in_data, &in_len) ) return NULL; + assert(in_len >= 0); + /* Empty string is a special case */ if ( in_len == 0 ) return Py_BuildValue("s", ""); + else if (in_len > INT_MAX / 2) + return PyErr_NoMemory(); /* Allocate a buffer of reasonable size. Resized when needed */ out_len = in_len*2; @@ -669,6 +698,7 @@ binascii_rledecode_hqx(PyObject *self, P #define OUTBYTE(b) \ do { \ if ( --out_len_left < 0 ) { \ + if ( out_len > INT_MAX / 2) return PyErr_NoMemory(); \ _PyString_Resize(&rv, 2*out_len); \ if ( rv == NULL ) return NULL; \ out_data = (unsigned char *)PyString_AsString(rv) \ @@ -737,7 +767,7 @@ binascii_crc_hqx(PyObject *self, PyObjec if ( !PyArg_ParseTuple(args, "s#i:crc_hqx", &bin_data, &len, &crc) ) return NULL; - while(len--) { + while(len-- > 0) { crc=((crc<<8)&0xff00)^crctab_hqx[((crc>>8)&0xff)^*bin_data++]; } @@ -881,7 +911,7 @@ binascii_crc32(PyObject *self, PyObject /* only want the trailing 32 bits */ crc &= 0xFFFFFFFFUL; #endif - while (len--) + while (len-- > 0) crc = crc_32_tab[(crc ^ *bin_data++) & 0xffUL] ^ (crc >> 8); /* Note: (crc >> 8) MUST zero fill on left */ @@ -911,6 +941,10 @@ binascii_hexlify(PyObject *self, PyObjec if (!PyArg_ParseTuple(args, "s#:b2a_hex", &argbuf, &arglen)) return NULL; + assert(arglen >= 0); + if (arglen > INT_MAX / 2) + return PyErr_NoMemory(); + retval = PyString_FromStringAndSize(NULL, arglen*2); if (!retval) return NULL; @@ -968,6 +1002,8 @@ binascii_unhexlify(PyObject *self, PyObj if (!PyArg_ParseTuple(args, "s#:a2b_hex", &argbuf, &arglen)) return NULL; + assert(arglen >= 0); + /* XXX What should we do about strings with an odd length? Should * we add an implicit leading zero, or a trailing zero? For now, * raise an exception. Index: Python-2.4.4/Modules/audioop.c =================================================================== --- Python-2.4.4.orig/Modules/audioop.c +++ Python-2.4.4/Modules/audioop.c @@ -674,7 +674,7 @@ static PyObject * audioop_tostereo(PyObject *self, PyObject *args) { signed char *cp, *ncp; - int len, size, val1, val2, val = 0; + int len, new_len, size, val1, val2, val = 0; double fac1, fac2, fval, maxval; PyObject *rv; int i; @@ -690,7 +690,14 @@ audioop_tostereo(PyObject *self, PyObjec return 0; } - rv = PyString_FromStringAndSize(NULL, len*2); + new_len = len*2; + if (new_len < 0) { + PyErr_SetString(PyExc_MemoryError, + "not enough memory for output buffer"); + return 0; + } + + rv = PyString_FromStringAndSize(NULL, new_len); if ( rv == 0 ) return 0; ncp = (signed char *)PyString_AsString(rv); @@ -853,7 +860,7 @@ audioop_lin2lin(PyObject *self, PyObject { signed char *cp; unsigned char *ncp; - int len, size, size2, val = 0; + int len, new_len, size, size2, val = 0; PyObject *rv; int i, j; @@ -867,7 +874,13 @@ audioop_lin2lin(PyObject *self, PyObject return 0; } - rv = PyString_FromStringAndSize(NULL, (len/size)*size2); + new_len = (len/size)*size2; + if (new_len < 0) { + PyErr_SetString(PyExc_MemoryError, + "not enough memory for output buffer"); + return 0; + } + rv = PyString_FromStringAndSize(NULL, new_len); if ( rv == 0 ) return 0; ncp = (unsigned char *)PyString_AsString(rv); @@ -903,6 +916,7 @@ audioop_ratecv(PyObject *self, PyObject int chan, d, *prev_i, *cur_i, cur_o; PyObject *state, *samps, *str, *rv = NULL; int bytes_per_frame; + size_t alloc_size; weightA = 1; weightB = 0; @@ -944,8 +958,14 @@ audioop_ratecv(PyObject *self, PyObject inrate /= d; outrate /= d; - prev_i = (int *) malloc(nchannels * sizeof(int)); - cur_i = (int *) malloc(nchannels * sizeof(int)); + alloc_size = sizeof(int) * (unsigned)nchannels; + if (alloc_size < nchannels) { + PyErr_SetString(PyExc_MemoryError, + "not enough memory for output buffer"); + return 0; + } + prev_i = (int *) malloc(alloc_size); + cur_i = (int *) malloc(alloc_size); if (prev_i == NULL || cur_i == NULL) { (void) PyErr_NoMemory(); goto exit; @@ -1116,7 +1136,7 @@ audioop_ulaw2lin(PyObject *self, PyObjec unsigned char *cp; unsigned char cval; signed char *ncp; - int len, size, val; + int len, new_len, size, val; PyObject *rv; int i; @@ -1129,12 +1149,18 @@ audioop_ulaw2lin(PyObject *self, PyObjec return 0; } - rv = PyString_FromStringAndSize(NULL, len*size); + new_len = len*size; + if (new_len < 0) { + PyErr_SetString(PyExc_MemoryError, + "not enough memory for output buffer"); + return 0; + } + rv = PyString_FromStringAndSize(NULL, new_len); if ( rv == 0 ) return 0; ncp = (signed char *)PyString_AsString(rv); - for ( i=0; i < len*size; i += size ) { + for ( i=0; i < new_len; i += size ) { cval = *cp++; val = st_ulaw_to_linear(cval); @@ -1259,7 +1285,7 @@ audioop_adpcm2lin(PyObject *self, PyObje { signed char *cp; signed char *ncp; - int len, size, valpred, step, delta, index, sign, vpdiff; + int len, new_len, size, valpred, step, delta, index, sign, vpdiff; PyObject *rv, *str, *state; int i, inputbuffer = 0, bufferstep; @@ -1281,7 +1307,13 @@ audioop_adpcm2lin(PyObject *self, PyObje } else if ( !PyArg_Parse(state, "(ii)", &valpred, &index) ) return 0; - str = PyString_FromStringAndSize(NULL, len*size*2); + new_len = len*size*2; + if (new_len < 0) { + PyErr_SetString(PyExc_MemoryError, + "not enough memory for output buffer"); + return 0; + } + str = PyString_FromStringAndSize(NULL, new_len); if ( str == 0 ) return 0; ncp = (signed char *)PyString_AsString(str); @@ -1289,7 +1321,7 @@ audioop_adpcm2lin(PyObject *self, PyObje step = stepsizeTable[index]; bufferstep = 0; - for ( i=0; i < len*size*2; i += size ) { + for ( i=0; i < new_len; i += size ) { /* Step 1 - get the delta value and compute next index */ if ( bufferstep ) { delta = inputbuffer & 0xf; Index: Python-2.4.4/Modules/cPickle.c =================================================================== --- Python-2.4.4.orig/Modules/cPickle.c +++ Python-2.4.4/Modules/cPickle.c @@ -3419,6 +3419,14 @@ load_binstring(Unpicklerobject *self) if (self->read_func(self, &s, 4) < 0) return -1; l = calc_binint(s, 4); + if (l < 0) { + /* Corrupt or hostile pickle -- we never write one like + * this. + */ + PyErr_SetString(UnpicklingError, + "BINSTRING pickle has negative byte count"); + return -1; + } if (self->read_func(self, &s, l) < 0) return -1; @@ -3486,6 +3494,14 @@ load_binunicode(Unpicklerobject *self) if (self->read_func(self, &s, 4) < 0) return -1; l = calc_binint(s, 4); + if (l < 0) { + /* Corrupt or hostile pickle -- we never write one like + * this. + */ + PyErr_SetString(UnpicklingError, + "BINUNICODE pickle has negative byte count"); + return -1; + } if (self->read_func(self, &s, l) < 0) return -1; Index: Python-2.4.4/Modules/_csv.c =================================================================== --- Python-2.4.4.orig/Modules/_csv.c +++ Python-2.4.4/Modules/_csv.c @@ -470,6 +470,10 @@ parse_grow_buff(ReaderObj *self) self->field = PyMem_Malloc(self->field_size); } else { + if (self->field_size > INT_MAX / 2) { + PyErr_NoMemory(); + return 0; + } self->field_size *= 2; self->field = PyMem_Realloc(self->field, self->field_size); } @@ -1003,6 +1007,12 @@ join_append_data(WriterObj *self, char * static int join_check_rec_size(WriterObj *self, int rec_len) { + + if (rec_len < 0 || rec_len > INT_MAX - MEM_INCR) { + PyErr_NoMemory(); + return 0; + } + if (rec_len > self->rec_size) { if (self->rec_size == 0) { self->rec_size = (rec_len / MEM_INCR + 1) * MEM_INCR;
r63883 | gregory.p.smith | 2008-06-02 02:07:25 +0200 (Mon, 02 Jun 2008) | 5 lines - Issue #2588, #2589: Fix potential integer underflow and overflow conditions in the PyOS_vsnprintf C API function. This is a backport of r63728 and r63734 from trunk. Index: Python-2.4.4/Python/mysnprintf.c =================================================================== --- Python-2.4.4.orig/Python/mysnprintf.c +++ Python-2.4.4/Python/mysnprintf.c @@ -54,18 +54,28 @@ int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) { int len; /* # bytes written, excluding \0 */ -#ifndef HAVE_SNPRINTF +#ifdef HAVE_SNPRINTF +#define _PyOS_vsnprintf_EXTRA_SPACE 1 +#else +#define _PyOS_vsnprintf_EXTRA_SPACE 512 char *buffer; #endif assert(str != NULL); assert(size > 0); assert(format != NULL); + /* We take a size_t as input but return an int. Sanity check + * our input so that it won't cause an overflow in the + * vsnprintf return value or the buffer malloc size. */ + if (size > INT_MAX - _PyOS_vsnprintf_EXTRA_SPACE) { + len = -666; + goto Done; + } #ifdef HAVE_SNPRINTF len = vsnprintf(str, size, format, va); #else /* Emulate it. */ - buffer = PyMem_MALLOC(size + 512); + buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE); if (buffer == NULL) { len = -666; goto Done; @@ -75,7 +85,7 @@ PyOS_vsnprintf(char *str, size_t size, c if (len < 0) /* ignore the error */; - else if ((size_t)len >= size + 512) + else if ((size_t)len >= size + _PyOS_vsnprintf_EXTRA_SPACE) Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf"); else { @@ -86,8 +96,10 @@ PyOS_vsnprintf(char *str, size_t size, c str[to_copy] = '\0'; } PyMem_FREE(buffer); -Done: #endif - str[size-1] = '\0'; +Done: + if (size > 0) + str[size-1] = '\0'; return len; +#undef _PyOS_vsnprintf_EXTRA_SPACE }
Index: Python-2.5.2/Objects/unicodeobject.c =================================================================== --- Python-2.5.2.orig/Objects/unicodeobject.c +++ Python-2.5.2/Objects/unicodeobject.c @@ -240,6 +240,11 @@ PyUnicodeObject *_PyUnicode_New(Py_ssize return unicode_empty; } + /* Ensure we won't overflow the size. */ + if (length > ((PY_SSIZE_T_MAX / sizeof(Py_UNICODE)) - 1)) { + return (PyUnicodeObject *)PyErr_NoMemory(); + } + /* Unicode freelist & memory allocation */ if (unicode_freelist) { unicode = unicode_freelist; @@ -1102,6 +1107,9 @@ PyObject *PyUnicode_EncodeUTF7(const Py_ char * out; char * start; + if (cbAllocated / 5 != size) + return PyErr_NoMemory(); + if (size == 0) return PyString_FromStringAndSize(NULL, 0); @@ -1700,8 +1708,9 @@ PyUnicode_EncodeUTF16(const Py_UNICODE * { PyObject *v; unsigned char *p; + Py_ssize_t nsize, bytesize; #ifdef Py_UNICODE_WIDE - int i, pairs; + Py_ssize_t i, pairs; #else const int pairs = 0; #endif @@ -1724,8 +1733,15 @@ PyUnicode_EncodeUTF16(const Py_UNICODE * if (s[i] >= 0x10000) pairs++; #endif - v = PyString_FromStringAndSize(NULL, - 2 * (size + pairs + (byteorder == 0))); + /* 2 * (size + pairs + (byteorder == 0)) */ + if (size > PY_SSIZE_T_MAX || + size > PY_SSIZE_T_MAX - pairs - (byteorder == 0)) + return PyErr_NoMemory(); + nsize = (size + pairs + (byteorder == 0)); + bytesize = nsize * 2; + if (bytesize / 2 != nsize) + return PyErr_NoMemory(); + v = PyString_FromStringAndSize(NULL, bytesize); if (v == NULL) return NULL; @@ -2053,6 +2069,11 @@ PyObject *unicodeescape_string(const Py_ char *p; static const char *hexdigit = "0123456789abcdef"; +#ifdef Py_UNICODE_WIDE + const Py_ssize_t expandsize = 10; +#else + const Py_ssize_t expandsize = 6; +#endif /* Initial allocation is based on the longest-possible unichr escape. @@ -2068,13 +2089,12 @@ PyObject *unicodeescape_string(const Py_ escape. */ + if (size > (PY_SSIZE_T_MAX - 2 - 1) / expandsize) + return PyErr_NoMemory(); + repr = PyString_FromStringAndSize(NULL, 2 -#ifdef Py_UNICODE_WIDE - + 10*size -#else - + 6*size -#endif + + expandsize*size + 1); if (repr == NULL) return NULL; @@ -2315,12 +2335,16 @@ PyObject *PyUnicode_EncodeRawUnicodeEsca char *q; static const char *hexdigit = "0123456789abcdef"; - #ifdef Py_UNICODE_WIDE - repr = PyString_FromStringAndSize(NULL, 10 * size); + const Py_ssize_t expandsize = 10; #else - repr = PyString_FromStringAndSize(NULL, 6 * size); + const Py_ssize_t expandsize = 6; #endif + + if (size > PY_SSIZE_T_MAX / expandsize) + return PyErr_NoMemory(); + + repr = PyString_FromStringAndSize(NULL, expandsize * size); if (repr == NULL) return NULL; if (size == 0) @@ -4730,6 +4754,11 @@ PyUnicodeObject *pad(PyUnicodeObject *se return self; } + if (left > PY_SSIZE_T_MAX - self->length || + right > PY_SSIZE_T_MAX - (left + self->length)) { + PyErr_SetString(PyExc_OverflowError, "padded string is too long"); + return NULL; + } u = _PyUnicode_New(left + self->length + right); if (u) { if (left) Index: Python-2.5.2/Objects/tupleobject.c =================================================================== --- Python-2.5.2.orig/Objects/tupleobject.c +++ Python-2.5.2/Objects/tupleobject.c @@ -60,11 +60,12 @@ PyTuple_New(register Py_ssize_t size) Py_ssize_t nbytes = size * sizeof(PyObject *); /* Check for overflow */ if (nbytes / sizeof(PyObject *) != (size_t)size || - (nbytes += sizeof(PyTupleObject) - sizeof(PyObject *)) - <= 0) + (nbytes > PY_SSIZE_T_MAX - sizeof(PyTupleObject) - sizeof(PyObject *))) { return PyErr_NoMemory(); } + nbytes += sizeof(PyTupleObject) - sizeof(PyObject *); + op = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size); if (op == NULL) return NULL; Index: Python-2.5.2/Objects/bufferobject.c =================================================================== --- Python-2.5.2.orig/Objects/bufferobject.c +++ Python-2.5.2/Objects/bufferobject.c @@ -427,6 +427,10 @@ buffer_repeat(PyBufferObject *self, Py_s count = 0; if (!get_buf(self, &ptr, &size, ANY_BUFFER)) return NULL; + if (count > PY_SSIZE_T_MAX / size) { + PyErr_SetString(PyExc_MemoryError, "result too large"); + return NULL; + } ob = PyString_FromStringAndSize(NULL, size * count); if ( ob == NULL ) return NULL; Index: Python-2.5.2/Objects/longobject.c =================================================================== --- Python-2.5.2.orig/Objects/longobject.c +++ Python-2.5.2/Objects/longobject.c @@ -70,6 +70,8 @@ _PyLong_New(Py_ssize_t size) PyErr_NoMemory(); return NULL; } + /* XXX(nnorwitz): This can overflow -- + PyObject_NEW_VAR / _PyObject_VAR_SIZE need to detect overflow */ return PyObject_NEW_VAR(PyLongObject, &PyLong_Type, size); } Index: Python-2.5.2/Objects/stringobject.c =================================================================== --- Python-2.5.2.orig/Objects/stringobject.c +++ Python-2.5.2/Objects/stringobject.c @@ -76,6 +76,11 @@ PyString_FromStringAndSize(const char *s return (PyObject *)op; } + if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { + PyErr_SetString(PyExc_OverflowError, "string is too large"); + return NULL; + } + /* Inline PyObject_NewVar */ op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); if (op == NULL) @@ -111,7 +116,7 @@ PyString_FromString(const char *str) assert(str != NULL); size = strlen(str); - if (size > PY_SSIZE_T_MAX) { + if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { PyErr_SetString(PyExc_OverflowError, "string is too long for a Python string"); return NULL; @@ -972,14 +977,24 @@ string_concat(register PyStringObject *a Py_INCREF(a); return (PyObject *)a; } + /* Check that string sizes are not negative, to prevent an + overflow in cases where we are passed incorrectly-created + strings with negative lengths (due to a bug in other code). + */ size = a->ob_size + b->ob_size; - if (size < 0) { + if (a->ob_size < 0 || b->ob_size < 0 || + a->ob_size > PY_SSIZE_T_MAX - b->ob_size) { PyErr_SetString(PyExc_OverflowError, "strings are too large to concat"); return NULL; } /* Inline PyObject_NewVar */ + if (size > PY_SSIZE_T_MAX - sizeof(PyStringObject)) { + PyErr_SetString(PyExc_OverflowError, + "strings are too large to concat"); + return NULL; + } op = (PyStringObject *)PyObject_MALLOC(sizeof(PyStringObject) + size); if (op == NULL) return PyErr_NoMemory(); Index: Python-2.5.2/Misc/NEWS =================================================================== --- Python-2.5.2.orig/Misc/NEWS +++ Python-2.5.2/Misc/NEWS @@ -32,6 +32,8 @@ What's New in Python 2.5.2c1? Core and builtins ----------------- +- Apply security patches from Apple. + - Issue #2620: Overflow checking when allocating or reallocating memory was not always being done properly in some python types and extension modules. PyMem_MALLOC, PyMem_REALLOC, PyMem_NEW and PyMem_RESIZE have Index: Python-2.5.2/Lib/test/seq_tests.py =================================================================== --- Python-2.5.2.orig/Lib/test/seq_tests.py +++ Python-2.5.2/Lib/test/seq_tests.py @@ -307,11 +307,13 @@ class CommonTest(unittest.TestCase): self.assertEqual(id(s), id(s*1)) def test_bigrepeat(self): - x = self.type2test([0]) - x *= 2**16 - self.assertRaises(MemoryError, x.__mul__, 2**16) - if hasattr(x, '__imul__'): - self.assertRaises(MemoryError, x.__imul__, 2**16) + import sys + if sys.maxint <= 2147483647: + x = self.type2test([0]) + x *= 2**16 + self.assertRaises(MemoryError, x.__mul__, 2**16) + if hasattr(x, '__imul__'): + self.assertRaises(MemoryError, x.__imul__, 2**16) def test_subscript(self): a = self.type2test([10, 11]) Index: Python-2.5.2/Lib/test/test_strop.py =================================================================== --- Python-2.5.2.orig/Lib/test/test_strop.py +++ Python-2.5.2/Lib/test/test_strop.py @@ -115,6 +115,25 @@ class StropFunctionTestCase(unittest.Tes strop.uppercase strop.whitespace + @test_support.precisionbigmemtest(size=test_support._2G - 1, memuse=5) + def test_stropjoin_huge_list(self, size): + a = "A" * size + try: + r = strop.join([a, a], a) + except OverflowError: + pass + else: + self.assertEquals(len(r), len(a) * 3) + + @test_support.precisionbigmemtest(size=test_support._2G - 1, memuse=1) + def test_stropjoin_huge_tup(self, size): + a = "A" * size + try: + r = strop.join((a, a), a) + except OverflowError: + pass # acceptable on 32-bit + else: + self.assertEquals(len(r), len(a) * 3) transtable = '\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037 !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`xyzdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377' Index: Python-2.5.2/Lib/test/test_bigmem.py =================================================================== --- Python-2.5.2.orig/Lib/test/test_bigmem.py +++ Python-2.5.2/Lib/test/test_bigmem.py @@ -1,5 +1,5 @@ from test import test_support -from test.test_support import bigmemtest, _1G, _2G +from test.test_support import bigmemtest, _1G, _2G, _4G, precisionbigmemtest import unittest import operator @@ -54,6 +54,22 @@ class StrTest(unittest.TestCase): self.assertEquals(s[lpadsize:-rpadsize], SUBSTR) self.assertEquals(s.strip(), SUBSTR.strip()) + @precisionbigmemtest(size=_2G - 1, memuse=1) + def test_center_unicode(self, size): + SUBSTR = u' abc def ghi' + try: + s = SUBSTR.center(size) + except OverflowError: + pass # acceptable on 32-bit + else: + self.assertEquals(len(s), size) + lpadsize = rpadsize = (len(s) - len(SUBSTR)) // 2 + if len(s) % 2: + lpadsize += 1 + self.assertEquals(s[lpadsize:-rpadsize], SUBSTR) + self.assertEquals(s.strip(), SUBSTR.strip()) + del s + @bigmemtest(minsize=_2G, memuse=2) def test_count(self, size): SUBSTR = ' abc def ghi' @@ -70,10 +86,44 @@ class StrTest(unittest.TestCase): s = '.' * size self.assertEquals(len(s.decode('utf-8')), size) + def basic_encode_test(self, size, enc, c=u'.', expectedsize=None): + if expectedsize is None: + expectedsize = size + + s = c * size + self.assertEquals(len(s.encode(enc)), expectedsize) + @bigmemtest(minsize=_2G + 2, memuse=3) def test_encode(self, size): - s = u'.' * size - self.assertEquals(len(s.encode('utf-8')), size) + return self.basic_encode_test(size, 'utf-8') + + @precisionbigmemtest(size=_4G / 6 + 2, memuse=2) + def test_encode_raw_unicode_escape(self, size): + try: + return self.basic_encode_test(size, 'raw_unicode_escape') + except MemoryError: + pass # acceptable on 32-bit + + @precisionbigmemtest(size=_4G / 5 + 70, memuse=3) + def test_encode_utf7(self, size): + try: + return self.basic_encode_test(size, 'utf7') + except MemoryError: + pass # acceptable on 32-bit + + @precisionbigmemtest(size=_2G-1, memuse=2) + def test_decodeascii(self, size): + return self.basic_encode_test(size, 'ascii', c='A') + + @precisionbigmemtest(size=_4G / 5, memuse=6+2) + def test_unicode_repr_oflw(self, size): + try: + s = u"\uAAAA"*size + r = repr(s) + except MemoryError: + pass # acceptable on 32-bit + else: + self.failUnless(s == eval(r)) @bigmemtest(minsize=_2G, memuse=2) def test_endswith(self, size): @@ -459,6 +509,11 @@ class StrTest(unittest.TestCase): self.assertEquals(s.count('\\'), size) self.assertEquals(s.count('0'), size * 2) + @bigmemtest(minsize=2**32 / 5, memuse=6+2) + def test_unicode_repr(self, size): + s = u"\uAAAA" * size + self.failUnless(len(repr(s)) > size) + # This test is meaningful even with size < 2G, as long as the # doubled string is > 2G (but it tests more if both are > 2G :) @bigmemtest(minsize=_1G + 2, memuse=3) @@ -642,6 +697,35 @@ class TupleTest(unittest.TestCase): def test_repeat_large(self, size): return self.basic_test_repeat(size) + @bigmemtest(minsize=_1G - 1, memuse=12) + def test_repeat_large_2(self, size): + return self.basic_test_repeat(size) + + @precisionbigmemtest(size=_1G - 1, memuse=9) + def test_from_2G_generator(self, size): + try: + t = tuple(xrange(size)) + except MemoryError: + pass # acceptable on 32-bit + else: + count = 0 + for item in t: + self.assertEquals(item, count) + count += 1 + self.assertEquals(count, size) + + @precisionbigmemtest(size=_1G - 25, memuse=9) + def test_from_almost_2G_generator(self, size): + try: + t = tuple(xrange(size)) + count = 0 + for item in t: + self.assertEquals(item, count) + count += 1 + self.assertEquals(count, size) + except MemoryError: + pass # acceptable, expected on 32-bit + # Like test_concat, split in two. def basic_test_repr(self, size): t = (0,) * size @@ -957,8 +1041,34 @@ class ListTest(unittest.TestCase): self.assertEquals(l[:10], [1] * 10) self.assertEquals(l[-10:], [5] * 10) +class BufferTest(unittest.TestCase): + + @precisionbigmemtest(size=_1G, memuse=4) + def test_repeat(self, size): + try: + b = buffer("AAAA")*size + except MemoryError: + pass # acceptable on 32-bit + else: + count = 0 + for c in b: + self.assertEquals(c, 'A') + count += 1 + self.assertEquals(count, size*4) + def test_main(): - test_support.run_unittest(StrTest, TupleTest, ListTest) + test_support.run_unittest(StrTest, TupleTest, ListTest, BufferTest) + +# Expected failures (crashers) +# del StrTest.test_center_unicode +del StrTest.test_decodeascii +# del StrTest.test_encode_utf32 +# del StrTest.test_encode_utf7 +# del StrTest.test_encode_raw_unicode_escape +# +# del TupleTest.test_from_2G_generator +# +# del BufferTest.test_repeat if __name__ == '__main__': if len(sys.argv) > 1: Index: Python-2.5.2/Lib/test/test_support.py =================================================================== --- Python-2.5.2.orig/Lib/test/test_support.py +++ Python-2.5.2/Lib/test/test_support.py @@ -33,6 +33,7 @@ verbose = 1 # Flag set to 0 use_resources = None # Flag set to [] by regrtest.py max_memuse = 0 # Disable bigmem tests (they will still be run with # small sizes, to make sure they work.) +real_max_memuse = 0 # _original_stdout is meant to hold stdout at the time regrtest began. # This may be "the real" stdout, or IDLE's emulation of stdout, or whatever. @@ -323,6 +324,7 @@ def run_with_locale(catstr, *locales): _1M = 1024*1024 _1G = 1024 * _1M _2G = 2 * _1G +_4G = 4 * _1G # Hack to get at the maximum value an internal index can take. class _Dummy: @@ -333,6 +335,7 @@ MAX_Py_ssize_t = _Dummy()[:] def set_memlimit(limit): import re global max_memuse + global real_max_memuse sizes = { 'k': 1024, 'm': _1M, @@ -344,6 +347,7 @@ def set_memlimit(limit): if m is None: raise ValueError('Invalid memory limit %r' % (limit,)) memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()]) + real_max_memuse = memlimit if memlimit > MAX_Py_ssize_t: memlimit = MAX_Py_ssize_t if memlimit < _2G - 1: @@ -389,6 +393,27 @@ def bigmemtest(minsize, memuse, overhead return wrapper return decorator +def precisionbigmemtest(size, memuse, overhead=5*_1M): + def decorator(f): + def wrapper(self): + if not real_max_memuse: + maxsize = 5147 + else: + maxsize = size + + if real_max_memuse and real_max_memuse < maxsize * memuse: + if verbose: + sys.stderr.write("Skipping %s because of memory " + "constraint\n" % (f.__name__,)) + return + + return f(self, maxsize) + wrapper.size = size + wrapper.memuse = memuse + wrapper.overhead = overhead + return wrapper + return decorator + def bigaddrspacetest(f): """Decorator for tests that fill the address space.""" def wrapper(self): Index: Python-2.5.2/Modules/mmapmodule.c =================================================================== --- Python-2.5.2.orig/Modules/mmapmodule.c +++ Python-2.5.2/Modules/mmapmodule.c @@ -223,7 +223,7 @@ mmap_read_method(mmap_object *self, return(NULL); /* silently 'adjust' out-of-range requests */ - if ((self->pos + num_bytes) > self->size) { + if (num_bytes > self->size - self->pos) { num_bytes -= (self->pos+num_bytes) - self->size; } result = Py_BuildValue("s#", self->data+self->pos, num_bytes); Index: Python-2.5.2/Modules/stropmodule.c =================================================================== --- Python-2.5.2.orig/Modules/stropmodule.c +++ Python-2.5.2/Modules/stropmodule.c @@ -216,6 +216,13 @@ strop_joinfields(PyObject *self, PyObjec return NULL; } slen = PyString_GET_SIZE(item); + if (slen > PY_SSIZE_T_MAX - reslen || + seplen > PY_SSIZE_T_MAX - reslen - seplen) { + PyErr_SetString(PyExc_OverflowError, + "input too long"); + Py_DECREF(res); + return NULL; + } while (reslen + slen + seplen >= sz) { if (_PyString_Resize(&res, sz * 2) < 0) return NULL; @@ -253,6 +260,14 @@ strop_joinfields(PyObject *self, PyObjec return NULL; } slen = PyString_GET_SIZE(item); + if (slen > PY_SSIZE_T_MAX - reslen || + seplen > PY_SSIZE_T_MAX - reslen - seplen) { + PyErr_SetString(PyExc_OverflowError, + "input too long"); + Py_DECREF(res); + Py_XDECREF(item); + return NULL; + } while (reslen + slen + seplen >= sz) { if (_PyString_Resize(&res, sz * 2) < 0) { Py_DECREF(item); Index: Python-2.5.2/Modules/gcmodule.c =================================================================== --- Python-2.5.2.orig/Modules/gcmodule.c +++ Python-2.5.2/Modules/gcmodule.c @@ -1318,7 +1318,10 @@ PyObject * _PyObject_GC_Malloc(size_t basicsize) { PyObject *op; - PyGC_Head *g = (PyGC_Head *)PyObject_MALLOC( + PyGC_Head *g; + if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) + return PyErr_NoMemory(); + g = (PyGC_Head *)PyObject_MALLOC( sizeof(PyGC_Head) + basicsize); if (g == NULL) return PyErr_NoMemory(); @@ -1361,6 +1364,8 @@ _PyObject_GC_Resize(PyVarObject *op, Py_ { const size_t basicsize = _PyObject_VAR_SIZE(op->ob_type, nitems); PyGC_Head *g = AS_GC(op); + if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) + return (PyVarObject *)PyErr_NoMemory(); g = (PyGC_Head *)PyObject_REALLOC(g, sizeof(PyGC_Head) + basicsize); if (g == NULL) return (PyVarObject *)PyErr_NoMemory();
Index: Python-2.5.2/Lib/test/test_hashlib.py =================================================================== --- Python-2.5.2.orig/Lib/test/test_hashlib.py +++ Python-2.5.2/Lib/test/test_hashlib.py @@ -9,7 +9,7 @@ import hashlib import unittest from test import test_support - +from test.test_support import _4G, precisionbigmemtest def hexstr(s): import string @@ -55,7 +55,6 @@ class HashLibTestCase(unittest.TestCase) m2.update(aas + bees + cees) self.assertEqual(m1.digest(), m2.digest()) - def check(self, name, data, digest): # test the direct constructors computed = getattr(hashlib, name)(data).hexdigest() @@ -74,7 +73,22 @@ class HashLibTestCase(unittest.TestCase) def test_case_md5_2(self): self.check('md5', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'd174ab98d277d9f5a5611c2c9f419d9f') - + + @precisionbigmemtest(size=_4G + 5, memuse=1) + def test_case_md5_huge(self, size): + if size == _4G + 5: + try: + self.check('md5', 'A'*size, 'c9af2dff37468ce5dfee8f2cfc0a9c6d') + except OverflowError: + pass # 32-bit arch + + @precisionbigmemtest(size=_4G - 1, memuse=1) + def test_case_md5_uintmax(self, size): + if size == _4G - 1: + try: + self.check('md5', 'A'*size, '28138d306ff1b8281f1a9067e1a1a2b3') + except OverflowError: + pass # 32-bit arch # use the three examples from Federal Information Processing Standards # Publication 180-1, Secure Hash Standard, 1995 April 17 Index: Python-2.5.2/Modules/_hashopenssl.c =================================================================== --- Python-2.5.2.orig/Modules/_hashopenssl.c +++ Python-2.5.2/Modules/_hashopenssl.c @@ -19,6 +19,8 @@ /* EVP is the preferred interface to hashing in OpenSSL */ #include <openssl/evp.h> +#define MUNCH_SIZE INT_MAX + #ifndef HASH_OBJ_CONSTRUCTOR #define HASH_OBJ_CONSTRUCTOR 0 @@ -164,9 +166,18 @@ EVP_update(EVPobject *self, PyObject *ar if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) return NULL; + if (len > 0 && len <= MUNCH_SIZE) { EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int)); - + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } Py_INCREF(Py_None); return Py_None; } @@ -255,10 +266,21 @@ EVP_tp_init(EVPobject *self, PyObject *a self->name = name_obj; Py_INCREF(self->name); - if (cp && len) + if (cp && len) { + if (len > 0 && len <= MUNCH_SIZE) { EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int)); - + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } + } + return 0; } #endif @@ -328,7 +350,7 @@ static PyTypeObject EVPtype = { static PyObject * EVPnew(PyObject *name_obj, const EVP_MD *digest, const EVP_MD_CTX *initial_ctx, - const unsigned char *cp, unsigned int len) + const unsigned char *cp, Py_ssize_t len) { EVPobject *self; @@ -346,8 +368,20 @@ EVPnew(PyObject *name_obj, EVP_DigestInit(&self->ctx, digest); } - if (cp && len) - EVP_DigestUpdate(&self->ctx, cp, len); + if (cp && len) { + if (len > 0 && len <= MUNCH_SIZE) { + EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, + unsigned int)); + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } + } return (PyObject *)self; } @@ -384,8 +418,7 @@ EVP_new(PyObject *self, PyObject *args, digest = EVP_get_digestbyname(name); - return EVPnew(name_obj, digest, NULL, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, - unsigned int)); + return EVPnew(name_obj, digest, NULL, cp, len); } /* @@ -410,7 +443,7 @@ EVP_new(PyObject *self, PyObject *args, CONST_ ## NAME ## _name_obj, \ NULL, \ CONST_new_ ## NAME ## _ctx_p, \ - cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int)); \ + cp, len); \ } /* a PyMethodDef structure for the constructor */
r65261 | neal.norwitz | 2008-07-28 07:06:20 +0200 (Mon, 28 Jul 2008) | 10 lines Backport code from r65182: Issue #2620: Overflow checking when allocating or reallocating memory was not always being done properly in some python types and extension modules. PyMem_MALLOC, PyMem_REALLOC, PyMem_NEW and PyMem_RESIZE have all been updated to perform better checks and places in the code that would previously leak memory on the error path when such an allocation failed have been fixed. Index: Python-2.5.2/Include/pymem.h =================================================================== --- Python-2.5.2.orig/Include/pymem.h +++ Python-2.5.2/Include/pymem.h @@ -67,8 +67,12 @@ PyAPI_FUNC(void) PyMem_Free(void *); for malloc(0), which would be treated as an error. Some platforms would return a pointer with no memory behind it, which would break pymalloc. To solve these problems, allocate an extra byte. */ -#define PyMem_MALLOC(n) malloc((n) ? (n) : 1) -#define PyMem_REALLOC(p, n) realloc((p), (n) ? (n) : 1) +/* Returns NULL to indicate error if a negative size or size larger than + Py_ssize_t can represent is supplied. Helps prevents security holes. */ +#define PyMem_MALLOC(n) (((n) < 0 || (n) > PY_SSIZE_T_MAX) ? NULL \ + : malloc((n) ? (n) : 1)) +#define PyMem_REALLOC(p, n) (((n) < 0 || (n) > PY_SSIZE_T_MAX) ? NULL \ + : realloc((p), (n) ? (n) : 1)) #define PyMem_FREE free #endif /* PYMALLOC_DEBUG */ @@ -77,24 +81,31 @@ PyAPI_FUNC(void) PyMem_Free(void *); * Type-oriented memory interface * ============================== * - * These are carried along for historical reasons. There's rarely a good - * reason to use them anymore (you can just as easily do the multiply and - * cast yourself). + * Allocate memory for n objects of the given type. Returns a new pointer + * or NULL if the request was too large or memory allocation failed. Use + * these macros rather than doing the multiplication yourself so that proper + * overflow checking is always done. */ #define PyMem_New(type, n) \ - ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ + ( ((n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ ( (type *) PyMem_Malloc((n) * sizeof(type)) ) ) #define PyMem_NEW(type, n) \ - ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ + ( ((n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ ( (type *) PyMem_MALLOC((n) * sizeof(type)) ) ) +/* + * The value of (p) is always clobbered by this macro regardless of success. + * The caller MUST check if (p) is NULL afterwards and deal with the memory + * error if so. This means the original value of (p) MUST be saved for the + * caller's memory error handler to not lose track of it. + */ #define PyMem_Resize(p, type, n) \ - ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ - ( (p) = (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) ) + ( (p) = ((n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ + (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) #define PyMem_RESIZE(p, type, n) \ - ( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \ - ( (p) = (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) ) + ( (p) = ((n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \ + (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) /* PyMem{Del,DEL} are left over from ancient days, and shouldn't be used * anymore. They're just confusing aliases for PyMem_{Free,FREE} now. Index: Python-2.5.2/Objects/obmalloc.c =================================================================== --- Python-2.5.2.orig/Objects/obmalloc.c +++ Python-2.5.2/Objects/obmalloc.c @@ -727,6 +727,15 @@ PyObject_Malloc(size_t nbytes) uint size; /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for nbytes < 0 is not required. + */ + if (nbytes > PY_SSIZE_T_MAX) + return NULL; + + /* * This implicitly redirects malloc(0). */ if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { @@ -1130,6 +1139,15 @@ PyObject_Realloc(void *p, size_t nbytes) if (p == NULL) return PyObject_Malloc(nbytes); + /* + * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes. + * Most python internals blindly use a signed Py_ssize_t to track + * things without checking for overflows or negatives. + * As size_t is unsigned, checking for nbytes < 0 is not required. + */ + if (nbytes > PY_SSIZE_T_MAX) + return NULL; + pool = POOL_ADDR(p); if (Py_ADDRESS_IN_RANGE(p, pool)) { /* We're in charge of this block */ Index: Python-2.5.2/Misc/NEWS =================================================================== --- Python-2.5.2.orig/Misc/NEWS +++ Python-2.5.2/Misc/NEWS @@ -32,6 +32,13 @@ What's New in Python 2.5.2c1? Core and builtins ----------------- +- Issue #2620: Overflow checking when allocating or reallocating memory + was not always being done properly in some python types and extension + modules. PyMem_MALLOC, PyMem_REALLOC, PyMem_NEW and PyMem_RESIZE have + all been updated to perform better checks and places in the code that + would previously leak memory on the error path when such an allocation + failed have been fixed. + - Added checks for integer overflows, contributed by Google. Some are only available if asserts are left in the code, in cases where they can't be triggered from Python code. Index: Python-2.5.2/Modules/almodule.c =================================================================== --- Python-2.5.2.orig/Modules/almodule.c +++ Python-2.5.2/Modules/almodule.c @@ -1633,9 +1633,11 @@ al_QueryValues(PyObject *self, PyObject if (nvals < 0) goto cleanup; if (nvals > setsize) { + ALvalue *old_return_set = return_set; setsize = nvals; PyMem_RESIZE(return_set, ALvalue, setsize); if (return_set == NULL) { + return_set = old_return_set; PyErr_NoMemory(); goto cleanup; } Index: Python-2.5.2/Modules/arraymodule.c =================================================================== --- Python-2.5.2.orig/Modules/arraymodule.c +++ Python-2.5.2/Modules/arraymodule.c @@ -816,6 +816,7 @@ static int array_do_extend(arrayobject *self, PyObject *bb) { Py_ssize_t size; + char *old_item; if (!array_Check(bb)) return array_iter_extend(self, bb); @@ -831,10 +832,11 @@ array_do_extend(arrayobject *self, PyObj return -1; } size = self->ob_size + b->ob_size; + old_item = self->ob_item; PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize); if (self->ob_item == NULL) { - PyObject_Del(self); - PyErr_NoMemory(); + self->ob_item = old_item; + PyErr_NoMemory(); return -1; } memcpy(self->ob_item + self->ob_size*self->ob_descr->itemsize, @@ -886,7 +888,7 @@ array_inplace_repeat(arrayobject *self, if (size > PY_SSIZE_T_MAX / n) { return PyErr_NoMemory(); } - PyMem_Resize(items, char, n * size); + PyMem_RESIZE(items, char, n * size); if (items == NULL) return PyErr_NoMemory(); p = items; Index: Python-2.5.2/Modules/selectmodule.c =================================================================== --- Python-2.5.2.orig/Modules/selectmodule.c +++ Python-2.5.2/Modules/selectmodule.c @@ -349,10 +349,12 @@ update_ufd_array(pollObject *self) { Py_ssize_t i, pos; PyObject *key, *value; + struct pollfd *old_ufds = self->ufds; self->ufd_len = PyDict_Size(self->dict); - PyMem_Resize(self->ufds, struct pollfd, self->ufd_len); + PyMem_RESIZE(self->ufds, struct pollfd, self->ufd_len); if (self->ufds == NULL) { + self->ufds = old_ufds; PyErr_NoMemory(); return 0; }
r63883 | gregory.p.smith | 2008-06-02 02:07:25 +0200 (Mon, 02 Jun 2008) | 5 lines - Issue #2588, #2589: Fix potential integer underflow and overflow conditions in the PyOS_vsnprintf C API function. This is a backport of r63728 and r63734 from trunk. Index: Python-2.5.2/Python/mysnprintf.c =================================================================== --- Python-2.5.2.orig/Python/mysnprintf.c +++ Python-2.5.2/Python/mysnprintf.c @@ -54,18 +54,28 @@ int PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) { int len; /* # bytes written, excluding \0 */ -#ifndef HAVE_SNPRINTF +#ifdef HAVE_SNPRINTF +#define _PyOS_vsnprintf_EXTRA_SPACE 1 +#else +#define _PyOS_vsnprintf_EXTRA_SPACE 512 char *buffer; #endif assert(str != NULL); assert(size > 0); assert(format != NULL); + /* We take a size_t as input but return an int. Sanity check + * our input so that it won't cause an overflow in the + * vsnprintf return value or the buffer malloc size. */ + if (size > INT_MAX - _PyOS_vsnprintf_EXTRA_SPACE) { + len = -666; + goto Done; + } #ifdef HAVE_SNPRINTF len = vsnprintf(str, size, format, va); #else /* Emulate it. */ - buffer = PyMem_MALLOC(size + 512); + buffer = PyMem_MALLOC(size + _PyOS_vsnprintf_EXTRA_SPACE); if (buffer == NULL) { len = -666; goto Done; @@ -75,7 +85,7 @@ PyOS_vsnprintf(char *str, size_t size, c if (len < 0) /* ignore the error */; - else if ((size_t)len >= size + 512) + else if ((size_t)len >= size + _PyOS_vsnprintf_EXTRA_SPACE) Py_FatalError("Buffer overflow in PyOS_snprintf/PyOS_vsnprintf"); else { @@ -86,8 +96,10 @@ PyOS_vsnprintf(char *str, size_t size, c str[to_copy] = '\0'; } PyMem_FREE(buffer); -Done: #endif - str[size-1] = '\0'; +Done: + if (size > 0) + str[size-1] = '\0'; return len; +#undef _PyOS_vsnprintf_EXTRA_SPACE } Index: Python-2.5.2/Misc/NEWS =================================================================== --- Python-2.5.2.orig/Misc/NEWS +++ Python-2.5.2/Misc/NEWS @@ -109,6 +109,9 @@ Core and builtins threading.enumerate() list after the join() for a brief period until it actually exited. +- Issue #2588, #2589: Fix potential integer underflow and overflow + conditions in the PyOS_vsnprintf C API function. + Library -------
Attachment:
signature.asc
Description: PGP signature