summaryrefslogtreecommitdiffstats
path: root/Include
diff options
context:
space:
mode:
authorGregory P. Smith <greg@mad-scientist.com>2008-07-22 04:46:32 (GMT)
committerGregory P. Smith <greg@mad-scientist.com>2008-07-22 04:46:32 (GMT)
commit0470bab69783c13447cb634fa403ef1067fe56d1 (patch)
tree8930faf64b2224f282512ff18a41bb0b338beded /Include
parentf5574a0c290aac0ec581415fdd343641c00d5d42 (diff)
downloadcpython-0470bab69783c13447cb634fa403ef1067fe56d1.zip
cpython-0470bab69783c13447cb634fa403ef1067fe56d1.tar.gz
cpython-0470bab69783c13447cb634fa403ef1067fe56d1.tar.bz2
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.
Diffstat (limited to 'Include')
-rw-r--r--Include/pymem.h33
1 files changed, 22 insertions, 11 deletions
diff --git a/Include/pymem.h b/Include/pymem.h
index f9acb55..542acee 100644
--- a/Include/pymem.h
+++ b/Include/pymem.h
@@ -69,8 +69,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 */
@@ -79,24 +83,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.