diff options
-rw-r--r-- | Include/pymem.h | 33 | ||||
-rw-r--r-- | Misc/NEWS | 7 | ||||
-rw-r--r-- | Modules/almodule.c | 2 | ||||
-rw-r--r-- | Modules/arraymodule.c | 8 | ||||
-rw-r--r-- | Modules/selectmodule.c | 4 | ||||
-rw-r--r-- | Objects/obmalloc.c | 18 |
6 files changed, 57 insertions, 15 deletions
diff --git a/Include/pymem.h b/Include/pymem.h index 0ab9463..caa58b1 100644 --- a/Include/pymem.h +++ b/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. @@ -12,6 +12,13 @@ What's New in Python 2.5.3? 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. + - Issue #2242: Fix a crash when decoding invalid utf-7 input on certain Windows / Visual Studio versions. diff --git a/Modules/almodule.c b/Modules/almodule.c index 0a45d2e..e4481cd 100644 --- a/Modules/almodule.c +++ b/Modules/almodule.c @@ -1633,9 +1633,11 @@ al_QueryValues(PyObject *self, PyObject *args) 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; } diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index eafea98..f262d20 100644 --- a/Modules/arraymodule.c +++ b/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, PyObject *bb) 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, Py_ssize_t n) 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; diff --git a/Modules/selectmodule.c b/Modules/selectmodule.c index 9eaae84..4deab6f 100644 --- a/Modules/selectmodule.c +++ b/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; } diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c index 03add6b..2d77906 100644 --- a/Objects/obmalloc.c +++ b/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 */ |