Skip to content

Commit

Permalink
Merged revisions 65182 via svnmerge from
Browse files Browse the repository at this point in the history
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r65182 | gregory.p.smith | 2008-07-22 06:46:32 +0200 (Tue, 22 Jul 2008) | 7 lines

  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.
........
  • Loading branch information
birkenfeld committed Jul 23, 2008
1 parent c1c54c1 commit d492ad8
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 14 deletions.
4 changes: 3 additions & 1 deletion Doc/c-api/memory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ The following type-oriented macros are provided for convenience. Note that

Same as :cfunc:`PyMem_Realloc`, but the memory block is resized to ``(n *
sizeof(TYPE))`` bytes. Returns a pointer cast to :ctype:`TYPE\*`. On return,
*p* will be a pointer to the new memory area, or *NULL* in the event of failure.
*p* will be a pointer to the new memory area, or *NULL* in the event of
failure. This is a C preprocessor macro; p is always reassigned. Save
the original value of p to avoid losing memory when handling errors.


.. cfunction:: void PyMem_Del(void *p)
Expand Down
33 changes: 22 additions & 11 deletions Include/pymem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,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);
Expand All @@ -820,8 +821,10 @@ array_do_extend(arrayobject *self, PyObject *bb)
return -1;
}
size = Py_SIZE(self) + Py_SIZE(b);
old_item = self->ob_item;
PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize);
if (self->ob_item == NULL) {
self->ob_item = old_item;
PyErr_NoMemory();
return -1;
}
Expand Down Expand Up @@ -874,7 +877,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;
Expand Down
4 changes: 3 additions & 1 deletion Modules/selectmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,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;
}
Expand Down
18 changes: 18 additions & 0 deletions Objects/obmalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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).
*/
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit d492ad8

Please sign in to comment.