Skip to content

Commit d492ad8

Browse files
committed
Merged revisions 65182 via svnmerge from
svn+ssh://[email protected]/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. ........
1 parent c1c54c1 commit d492ad8

File tree

5 files changed

+50
-14
lines changed

5 files changed

+50
-14
lines changed

Doc/c-api/memory.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ The following type-oriented macros are provided for convenience. Note that
136136

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

141143

142144
.. cfunction:: void PyMem_Del(void *p)

Include/pymem.h

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

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

8792
#define PyMem_New(type, n) \
88-
( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
93+
( ((n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \
8994
( (type *) PyMem_Malloc((n) * sizeof(type)) ) )
9095
#define PyMem_NEW(type, n) \
91-
( assert((n) <= PY_SIZE_MAX / sizeof(type)) , \
96+
( ((n) > PY_SSIZE_T_MAX / sizeof(type)) ? NULL : \
9297
( (type *) PyMem_MALLOC((n) * sizeof(type)) ) )
9398

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

101112
/* PyMem{Del,DEL} are left over from ancient days, and shouldn't be used
102113
* anymore. They're just confusing aliases for PyMem_{Free,FREE} now.

Modules/arraymodule.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,7 @@ static int
805805
array_do_extend(arrayobject *self, PyObject *bb)
806806
{
807807
Py_ssize_t size;
808+
char *old_item;
808809

809810
if (!array_Check(bb))
810811
return array_iter_extend(self, bb);
@@ -820,8 +821,10 @@ array_do_extend(arrayobject *self, PyObject *bb)
820821
return -1;
821822
}
822823
size = Py_SIZE(self) + Py_SIZE(b);
824+
old_item = self->ob_item;
823825
PyMem_RESIZE(self->ob_item, char, size*self->ob_descr->itemsize);
824826
if (self->ob_item == NULL) {
827+
self->ob_item = old_item;
825828
PyErr_NoMemory();
826829
return -1;
827830
}
@@ -874,7 +877,7 @@ array_inplace_repeat(arrayobject *self, Py_ssize_t n)
874877
if (size > PY_SSIZE_T_MAX / n) {
875878
return PyErr_NoMemory();
876879
}
877-
PyMem_Resize(items, char, n * size);
880+
PyMem_RESIZE(items, char, n * size);
878881
if (items == NULL)
879882
return PyErr_NoMemory();
880883
p = items;

Modules/selectmodule.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,12 @@ update_ufd_array(pollObject *self)
346346
{
347347
Py_ssize_t i, pos;
348348
PyObject *key, *value;
349+
struct pollfd *old_ufds = self->ufds;
349350

350351
self->ufd_len = PyDict_Size(self->dict);
351-
PyMem_Resize(self->ufds, struct pollfd, self->ufd_len);
352+
PyMem_RESIZE(self->ufds, struct pollfd, self->ufd_len);
352353
if (self->ufds == NULL) {
354+
self->ufds = old_ufds;
353355
PyErr_NoMemory();
354356
return 0;
355357
}

Objects/obmalloc.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,15 @@ PyObject_Malloc(size_t nbytes)
726726
poolp next;
727727
uint size;
728728

729+
/*
730+
* Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
731+
* Most python internals blindly use a signed Py_ssize_t to track
732+
* things without checking for overflows or negatives.
733+
* As size_t is unsigned, checking for nbytes < 0 is not required.
734+
*/
735+
if (nbytes > PY_SSIZE_T_MAX)
736+
return NULL;
737+
729738
/*
730739
* This implicitly redirects malloc(0).
731740
*/
@@ -1130,6 +1139,15 @@ PyObject_Realloc(void *p, size_t nbytes)
11301139
if (p == NULL)
11311140
return PyObject_Malloc(nbytes);
11321141

1142+
/*
1143+
* Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
1144+
* Most python internals blindly use a signed Py_ssize_t to track
1145+
* things without checking for overflows or negatives.
1146+
* As size_t is unsigned, checking for nbytes < 0 is not required.
1147+
*/
1148+
if (nbytes > PY_SSIZE_T_MAX)
1149+
return NULL;
1150+
11331151
pool = POOL_ADDR(p);
11341152
if (Py_ADDRESS_IN_RANGE(p, pool)) {
11351153
/* We're in charge of this block */

0 commit comments

Comments
 (0)