]> granicus.if.org Git - python/commitdiff
Backport code from r65182:
authorNeal Norwitz <nnorwitz@gmail.com>
Mon, 28 Jul 2008 05:06:20 +0000 (05:06 +0000)
committerNeal Norwitz <nnorwitz@gmail.com>
Mon, 28 Jul 2008 05:06:20 +0000 (05:06 +0000)
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.

Include/pymem.h
Misc/NEWS
Modules/almodule.c
Modules/arraymodule.c
Modules/selectmodule.c
Objects/obmalloc.c

index 0ab946301601d9b821f2d8c3c81d18ab3c15a789..caa58b11696b2492021cdf2329cd4f71b414bb91 100644 (file)
@@ -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 0f36f179e255dc23284ed3d485352e285c6f70b0..222e408596e9625f0dd9c342202ed2d0d35e83dd 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.
 
index 0a45d2e0c02b04a0ed606608ea00157f0f247595..e4481cd3c01a0e2361364ba9d851b5e4f22573f9 100644 (file)
@@ -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;
                }
index eafea988c9674b8c7f3fa9eb2ee17ce359d040e1..f262d206a875c4072b3329df600a060724ad0cc8 100644 (file)
@@ -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;
index 9eaae8488c962081ba62c47705dbd3fa38435148..4deab6fb84b03e786577705774143972e6d0be78 100644 (file)
@@ -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;
        }
index 03add6b0893889bfb1dc9e5232194ab210c483b9..2d7790679d5bad831dccd0ce7241fc06c6b6a67b 100644 (file)
@@ -726,6 +726,15 @@ PyObject_Malloc(size_t nbytes)
        poolp next;
        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).
         */
@@ -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 */