-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
gh-127681: add overflow checks when using PyMem_Malloc
#127686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
adf0276
3dce338
73796f7
8f40dc0
b79c86e
010a3ab
33dc820
999d5e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -331,19 +331,30 @@ _ctypes_alloc_format_string(const char *prefix, const char *suffix) | |
return NULL; | ||
} | ||
len = strlen(suffix); | ||
if (prefix) | ||
len += strlen(prefix); | ||
if (prefix) { | ||
size_t prefix_len = strlen(prefix); | ||
if (len > PY_SIZE_MAX - prefix_len) { | ||
goto oom; | ||
} | ||
len += prefix_len; | ||
} | ||
if (len == PY_SIZE_MAX) { | ||
goto oom; | ||
} | ||
result = PyMem_Malloc(len + 1); | ||
if (result == NULL) { | ||
PyErr_NoMemory(); | ||
return NULL; | ||
goto oom; | ||
} | ||
if (prefix) | ||
strcpy(result, prefix); | ||
else | ||
result[0] = '\0'; | ||
strcat(result, suffix); | ||
return result; | ||
|
||
oom: | ||
PyErr_NoMemory(); | ||
return NULL; | ||
} | ||
|
||
/* | ||
|
@@ -363,12 +374,16 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, | |
int k; | ||
|
||
prefix_len = 32 * ndim + 3; | ||
if (prefix) | ||
prefix_len += strlen(prefix); | ||
if (prefix) { | ||
size_t l = strlen(prefix); | ||
if ((size_t)prefix > (size_t)PY_SSIZE_T_MAX - l) { | ||
goto oom; | ||
} | ||
prefix_len += l; | ||
} | ||
Comment on lines
+377
to
+383
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
new_prefix = PyMem_Malloc(prefix_len); | ||
if (new_prefix == NULL) { | ||
PyErr_NoMemory(); | ||
return NULL; | ||
goto oom; | ||
} | ||
new_prefix[0] = '\0'; | ||
if (prefix) | ||
|
@@ -388,6 +403,10 @@ _ctypes_alloc_format_string_with_shape(int ndim, const Py_ssize_t *shape, | |
result = _ctypes_alloc_format_string(new_prefix, suffix); | ||
PyMem_Free(new_prefix); | ||
return result; | ||
|
||
oom: | ||
PyErr_NoMemory(); | ||
return NULL; | ||
} | ||
|
||
/* StructParamObject and StructParam_Type are used in _ctypes_callproc() | ||
|
@@ -1666,7 +1685,7 @@ PyCArrayType_init(PyObject *self, PyObject *args, PyObject *kwds) | |
if (stginfo->format == NULL) | ||
goto error; | ||
stginfo->ndim = iteminfo->ndim + 1; | ||
stginfo->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stginfo->ndim); | ||
stginfo->shape = PyMem_New(Py_ssize_t, stginfo->ndim); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This never overflows. |
||
if (stginfo->shape == NULL) { | ||
PyErr_NoMemory(); | ||
goto error; | ||
|
@@ -3094,7 +3113,7 @@ PyCData_MallocBuffer(CDataObject *obj, StgInfo *info) | |
/* In python 2.4, and ctypes 0.9.6, the malloc call took about | ||
33% of the creation time for c_int(). | ||
*/ | ||
obj->b_ptr = (char *)PyMem_Malloc(info->size); | ||
obj->b_ptr = PyMem_New(char, info->size); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same. |
||
if (obj->b_ptr == NULL) { | ||
PyErr_NoMemory(); | ||
return -1; | ||
|
@@ -4775,7 +4794,7 @@ Array_subscript(PyObject *myself, PyObject *item) | |
return PyBytes_FromStringAndSize(ptr + start, | ||
slicelen); | ||
} | ||
dest = (char *)PyMem_Malloc(slicelen); | ||
dest = PyMem_New(char, slicelen); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same. |
||
|
||
if (dest == NULL) | ||
return PyErr_NoMemory(); | ||
|
@@ -5461,7 +5480,7 @@ Pointer_subscript(PyObject *myself, PyObject *item) | |
return PyBytes_FromStringAndSize(ptr + start, | ||
len); | ||
} | ||
dest = (char *)PyMem_Malloc(len); | ||
dest = PyMem_New(char, len); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same. |
||
if (dest == NULL) | ||
return PyErr_NoMemory(); | ||
for (cur = start, i = 0; i < len; cur += step, i++) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,8 +23,6 @@ | |
int | ||
PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info) | ||
{ | ||
Py_ssize_t size; | ||
|
||
ctype_clear_stginfo(dst_info); | ||
PyMem_Free(dst_info->ffi_type_pointer.elements); | ||
PyMem_Free(dst_info->format); | ||
|
@@ -43,35 +41,49 @@ PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info) | |
Py_XINCREF(dst_info->module); | ||
|
||
if (src_info->format) { | ||
dst_info->format = PyMem_Malloc(strlen(src_info->format) + 1); | ||
size_t s = strlen(src_info->format); | ||
if (s >= (size_t)PY_SSIZE_T_MAX) { | ||
goto oom; | ||
} | ||
dst_info->format = PyMem_Malloc(s + 1); | ||
Comment on lines
+44
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This never overflows. |
||
if (dst_info->format == NULL) { | ||
PyErr_NoMemory(); | ||
return -1; | ||
goto oom; | ||
} | ||
strcpy(dst_info->format, src_info->format); | ||
} | ||
if (src_info->shape) { | ||
dst_info->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src_info->ndim); | ||
dst_info->shape = PyMem_New(Py_ssize_t, src_info->ndim); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It never overflows. |
||
if (dst_info->shape == NULL) { | ||
PyErr_NoMemory(); | ||
return -1; | ||
goto oom; | ||
} | ||
memcpy(dst_info->shape, src_info->shape, | ||
sizeof(Py_ssize_t) * src_info->ndim); | ||
} | ||
|
||
if (src_info->ffi_type_pointer.elements == NULL) | ||
return 0; | ||
size = sizeof(ffi_type *) * (src_info->length + 1); | ||
if ((size_t)src_info->length > (size_t)PY_SSIZE_T_MAX / sizeof(ffi_type *) - 1) { | ||
goto oom; | ||
} | ||
Py_ssize_t size = sizeof(ffi_type *) * (src_info->length + 1); | ||
Comment on lines
+65
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have an example of overflow? |
||
dst_info->ffi_type_pointer.elements = PyMem_Malloc(size); | ||
if (dst_info->ffi_type_pointer.elements == NULL) { | ||
PyErr_NoMemory(); | ||
return -1; | ||
goto oom; | ||
} | ||
memcpy(dst_info->ffi_type_pointer.elements, | ||
src_info->ffi_type_pointer.elements, | ||
size); | ||
return 0; | ||
|
||
oom: | ||
if (src_info->format) { | ||
PyMem_Free(dst_info->format); | ||
} | ||
if (src_info->shape) { | ||
PyMem_Free(dst_info->shape); | ||
} | ||
PyErr_NoMemory(); | ||
return -1; | ||
} | ||
|
||
/* descr is the descriptor for a field marked as anonymous. Get all the | ||
|
@@ -331,6 +343,10 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct | |
PyMem_Free(stginfo->format); | ||
stginfo->format = NULL; | ||
} | ||
if (format_spec_size >= PY_SSIZE_T_MAX) { | ||
PyErr_NoMemory(); | ||
goto error; | ||
} | ||
Comment on lines
+346
to
+349
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It never overflows. There is a memory array at least |
||
stginfo->format = PyMem_Malloc(format_spec_size + 1); | ||
if (!stginfo->format) { | ||
PyErr_NoMemory(); | ||
|
@@ -564,8 +580,9 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct | |
* dummy fields standing in for array elements, and the | ||
* ffi_types representing the dummy structures. | ||
*/ | ||
alloc_size = (ffi_ofs + 1 + len + num_ffi_type_pointers) * sizeof(ffi_type *) + | ||
num_ffi_types * sizeof(ffi_type); | ||
// TODO: check if this can overflow | ||
alloc_size = (ffi_ofs + 1 + len + num_ffi_type_pointers) * sizeof(ffi_type *) | ||
+ num_ffi_types * sizeof(ffi_type); | ||
type_block = PyMem_Malloc(alloc_size); | ||
|
||
if (type_block == NULL) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -465,6 +465,10 @@ _PyIncrementalNewlineDecoder_decode(PyObject *myself, | |
when there is something to translate. On the other hand, | ||
we already know there is a \r byte, so chances are high | ||
that something needs to be done. */ | ||
if (len > PY_SSIZE_T_MAX / kind) { | ||
PyErr_NoMemory(); | ||
goto error; | ||
} | ||
Comment on lines
+468
to
+471
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It never overflows. The size of |
||
translated = PyMem_Malloc(kind * len); | ||
if (translated == NULL) { | ||
PyErr_NoMemory(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -610,7 +610,7 @@ static wchar_t * | |
read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) { | ||
int err = 0, sig = 0; | ||
|
||
wchar_t *buf = (wchar_t*)PyMem_Malloc(maxlen * sizeof(wchar_t)); | ||
wchar_t *buf = PyMem_New(wchar_t, maxlen); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It never overflows. |
||
if (!buf) { | ||
PyErr_NoMemory(); | ||
goto error; | ||
|
@@ -875,8 +875,10 @@ _io__WindowsConsoleIO_readall_impl(winconsoleio *self) | |
return NULL; | ||
|
||
bufsize = BUFSIZ; | ||
|
||
buf = (wchar_t*)PyMem_Malloc((bufsize + 1) * sizeof(wchar_t)); | ||
// Note: BUFSIZ is small enough not to overflow, so no need | ||
// to check that 'bufsize + 1' overflows. However, we | ||
// still need to check (bufsize + 1) * sizeof(wchar_t). | ||
buf = PyMem_New(wchar_t, bufsize); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It never overflows. |
||
if (buf == NULL) { | ||
PyErr_NoMemory(); | ||
return NULL; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1125,13 +1125,16 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, | |
if (extra_group_size < 0) | ||
goto cleanup; | ||
|
||
if (extra_group_size > MAX_GROUPS) { | ||
if (extra_group_size > MAX_GROUPS | ||
|| (size_t)extra_group_size > (size_t)PY_SSIZE_T_MAX / sizeof(gid_t)) | ||
Comment on lines
+1128
to
+1129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can be sure that MAX_GROUPS is small enough. |
||
{ | ||
PyErr_SetString(PyExc_ValueError, "too many extra_groups"); | ||
goto cleanup; | ||
} | ||
|
||
/* Deliberately keep extra_groups == NULL for extra_group_size == 0 */ | ||
if (extra_group_size > 0) { | ||
/* extra_group_size < MAX_GROUPS */ | ||
extra_groups = PyMem_RawMalloc(extra_group_size * sizeof(gid_t)); | ||
if (extra_groups == NULL) { | ||
PyErr_SetString(PyExc_MemoryError, | ||
|
@@ -1192,7 +1195,7 @@ subprocess_fork_exec_impl(PyObject *module, PyObject *process_args, | |
#endif /* HAVE_SETREUID */ | ||
} | ||
|
||
c_fds_to_keep = PyMem_Malloc(fds_to_keep_len * sizeof(int)); | ||
c_fds_to_keep = PyMem_New(int, fds_to_keep_len); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It never overflows. |
||
if (c_fds_to_keep == NULL) { | ||
PyErr_SetString(PyExc_MemoryError, "failed to malloc c_fds_to_keep"); | ||
goto cleanup; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1725,7 +1725,7 @@ prepare_s(PyStructObject *self) | |
} | ||
|
||
/* check for overflow */ | ||
if ((ncodes + 1) > ((size_t)PY_SSIZE_T_MAX / sizeof(formatcode))) { | ||
if (ncodes > ((size_t)PY_SSIZE_T_MAX / sizeof(formatcode)) - 1) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
PyErr_NoMemory(); | ||
return -1; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have an example of overflow? AFAIK,
prefix
is smaller than 1030 bytes, andsuffix
also is a short string.