Skip to content

Commit 46b3a79

Browse files
committed
Backport r65182. This change modified from using the unsigned max value
to the signed max value similar to 2.5 and trunk. Issue python#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.
1 parent 5cdbf77 commit 46b3a79

File tree

6 files changed

+57
-15
lines changed

6 files changed

+57
-15
lines changed

Include/pymem.h

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,12 @@ PyAPI_FUNC(void) PyMem_Free(void *);
6666
for malloc(0), which would be treated as an error. Some platforms
6767
would return a pointer with no memory behind it, which would break
6868
pymalloc. To solve these problems, allocate an extra byte. */
69-
#define PyMem_MALLOC(n) malloc((n) ? (n) : 1)
70-
#define PyMem_REALLOC(p, n) realloc((p), (n) ? (n) : 1)
69+
/* Returns NULL to indicate error if a negative size or size larger than
70+
Py_ssize_t can represent is supplied. Helps prevents security holes. */
71+
#define PyMem_MALLOC(n) (((n) < 0 || (n) > INT_MAX) ? NULL \
72+
: malloc((n) ? (n) : 1))
73+
#define PyMem_REALLOC(p, n) (((n) < 0 || (n) > INT_MAX) ? NULL \
74+
: realloc((p), (n) ? (n) : 1))
7175

7276
#endif /* PYMALLOC_DEBUG */
7377

@@ -80,24 +84,31 @@ PyAPI_FUNC(void) PyMem_Free(void *);
8084
* Type-oriented memory interface
8185
* ==============================
8286
*
83-
* These are carried along for historical reasons. There's rarely a good
84-
* reason to use them anymore (you can just as easily do the multiply and
85-
* cast yourself).
87+
* Allocate memory for n objects of the given type. Returns a new pointer
88+
* or NULL if the request was too large or memory allocation failed. Use
89+
* these macros rather than doing the multiplication yourself so that proper
90+
* overflow checking is always done.
8691
*/
8792

8893
#define PyMem_New(type, n) \
89-
( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
94+
( ((n) > INT_MAX / sizeof(type)) ? NULL : \
9095
( (type *) PyMem_Malloc((n) * sizeof(type)) ) )
9196
#define PyMem_NEW(type, n) \
92-
( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
97+
( ((n) > INT_MAX / sizeof(type)) ? NULL : \
9398
( (type *) PyMem_MALLOC((n) * sizeof(type)) ) )
9499

100+
/*
101+
* The value of (p) is always clobbered by this macro regardless of success.
102+
* The caller MUST check if (p) is NULL afterwards and deal with the memory
103+
* error if so. This means the original value of (p) MUST be saved for the
104+
* caller's memory error handler to not lose track of it.
105+
*/
95106
#define PyMem_Resize(p, type, n) \
96-
( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
97-
( (p) = (type *) PyMem_Realloc((p), (n) * sizeof(type)) ) )
107+
( (p) = ((n) > INT_MAX / sizeof(type)) ? NULL : \
108+
(type *) PyMem_Realloc((p), (n) * sizeof(type)) )
98109
#define PyMem_RESIZE(p, type, n) \
99-
( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
100-
( (p) = (type *) PyMem_REALLOC((p), (n) * sizeof(type)) ) )
110+
( (p) = ((n) > INT_MAX / sizeof(type)) ? NULL : \
111+
(type *) PyMem_REALLOC((p), (n) * sizeof(type)) )
101112

102113
/* In order to avoid breaking old code mixing PyObject_{New, NEW} with
103114
PyMem_{Del, DEL} and PyMem_{Free, FREE}, the PyMem "release memory"

Misc/NEWS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ What's New in Python 2.4.5c1?
1818
Core and builtins
1919
-----------------
2020

21+
- Issue #2620: Overflow checking when allocating or reallocating memory
22+
was not always being done properly in some python types and extension
23+
modules. PyMem_MALLOC, PyMem_REALLOC, PyMem_NEW and PyMem_RESIZE have
24+
all been updated to perform better checks and places in the code that
25+
would previously leak memory on the error path when such an allocation
26+
failed have been fixed.
27+
2128
- Added checks for integer overflows, contributed by Google. Some are
2229
only available if asserts are left in the code, in cases where they
2330
can't be triggered from Python code.

Modules/almodule.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,9 +1633,11 @@ al_QueryValues(PyObject *self, PyObject *args)
16331633
if (nvals < 0)
16341634
goto cleanup;
16351635
if (nvals > setsize) {
1636+
ALvalue *old_return_set = return_set;
16361637
setsize = nvals;
16371638
PyMem_RESIZE(return_set, ALvalue, setsize);
16381639
if (return_set == NULL) {
1640+
return_set = old_return_set;
16391641
PyErr_NoMemory();
16401642
goto cleanup;
16411643
}

Modules/arraymodule.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,7 @@ static int
814814
array_do_extend(arrayobject *self, PyObject *bb)
815815
{
816816
int size;
817+
char *old_item;
817818

818819
if (!array_Check(bb))
819820
return array_iter_extend(self, bb);
@@ -829,10 +830,11 @@ array_do_extend(arrayobject *self, PyObject *bb)
829830
return -1;
830831
}
831832
size = self->ob_size + b->ob_size;
833+
old_item = self->ob_item;
832834
PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize);
833835
if (self->ob_item == NULL) {
834-
PyObject_Del(self);
835-
PyErr_NoMemory();
836+
self->ob_item = old_item;
837+
PyErr_NoMemory();
836838
return -1;
837839
}
838840
memcpy(self->ob_item + self->ob_size*self->ob_descr->itemsize,
@@ -884,7 +886,7 @@ array_inplace_repeat(arrayobject *self, int n)
884886
if (size > INT_MAX / n) {
885887
return PyErr_NoMemory();
886888
}
887-
PyMem_Resize(items, char, n * size);
889+
PyMem_RESIZE(items, char, n * size);
888890
if (items == NULL)
889891
return PyErr_NoMemory();
890892
p = items;

Modules/selectmodule.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,12 @@ update_ufd_array(pollObject *self)
342342
{
343343
int i, pos;
344344
PyObject *key, *value;
345+
struct pollfd *old_ufds = self->ufds;
345346

346347
self->ufd_len = PyDict_Size(self->dict);
347-
PyMem_Resize(self->ufds, struct pollfd, self->ufd_len);
348+
PyMem_RESIZE(self->ufds, struct pollfd, self->ufd_len);
348349
if (self->ufds == NULL) {
350+
self->ufds = old_ufds;
349351
PyErr_NoMemory();
350352
return 0;
351353
}

Objects/obmalloc.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,15 @@ PyObject_Malloc(size_t nbytes)
584584
poolp next;
585585
uint size;
586586

587+
/*
588+
* Limit ourselves to INT_MAX bytes to prevent security holes.
589+
* Most python internals blindly use a signed Py_ssize_t to track
590+
* things without checking for overflows or negatives.
591+
* As size_t is unsigned, checking for nbytes < 0 is not required.
592+
*/
593+
if (nbytes > INT_MAX)
594+
return NULL;
595+
587596
/*
588597
* This implicitly redirects malloc(0).
589598
*/
@@ -814,6 +823,15 @@ PyObject_Realloc(void *p, size_t nbytes)
814823
if (p == NULL)
815824
return PyObject_Malloc(nbytes);
816825

826+
/*
827+
* Limit ourselves to INT_MAX bytes to prevent security holes.
828+
* Most python internals blindly use a signed Py_ssize_t to track
829+
* things without checking for overflows or negatives.
830+
* As size_t is unsigned, checking for nbytes < 0 is not required.
831+
*/
832+
if (nbytes > INT_MAX)
833+
return NULL;
834+
817835
pool = POOL_ADDR(p);
818836
if (Py_ADDRESS_IN_RANGE(p, pool)) {
819837
/* We're in charge of this block */

0 commit comments

Comments
 (0)