Repository navigation
gh-127681: Fix overflow vulnerabilities in PyMem_Malloc calls #153489
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
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 |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Fix overflow vulnerabilities in PyMem_Malloc calls by systematically replacing them with PyMem_New or adding explicit PY_SSIZE_T_MAX checks. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,35 +42,49 @@ PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info) | |
| dst_info->pointer_type = NULL; // the cache cannot be shared | ||
|
|
||
| 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) { | ||
|
Comment on lines
+45
to
+46
Member
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. you're checking strlen > PY_SSIZE_T_MAX in multiple places - this is impossible. |
||
| goto oom; | ||
| } | ||
| dst_info->format = PyMem_Malloc(s + 1); | ||
| 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); | ||
| 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; | ||
| if ((size_t)src_info->length > (size_t)PY_SSIZE_T_MAX / sizeof(ffi_type *) - 1) { | ||
| goto oom; | ||
| } | ||
| size = sizeof(ffi_type *) * (src_info->length + 1); | ||
| 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); | ||
|
Member
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. There is a risk of a double free in the destructor. If you free it here, please set the variable to |
||
| } | ||
| if (src_info->shape) { | ||
| PyMem_Free(dst_info->shape); | ||
|
Member
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 also a double free as this can be reached when the format allocation failed. |
||
| } | ||
| PyErr_NoMemory(); | ||
| return -1; | ||
| } | ||
|
|
||
| /* descr is the descriptor for a field marked as anonymous. Get all the | ||
|
|
@@ -342,6 +356,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; | ||
| } | ||
| stginfo->format = PyMem_Malloc(format_spec_size + 1); | ||
| if (!stginfo->format) { | ||
| PyErr_NoMemory(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1990,6 +1990,10 @@ win32_wchdir(LPCWSTR path) | |
| if (!result) | ||
| return FALSE; | ||
| if (result > Py_ARRAY_LENGTH(path_buf)) { | ||
| if ((size_t)result > (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) { | ||
| SetLastError(ERROR_OUTOFMEMORY); | ||
| return FALSE; | ||
| } | ||
| new_path = PyMem_RawMalloc(result * sizeof(wchar_t)); | ||
| if (!new_path) { | ||
| SetLastError(ERROR_OUTOFMEMORY); | ||
|
|
@@ -5319,7 +5323,7 @@ os_listmounts_impl(PyObject *module, path_t *volume) | |
| if (buffer != default_buffer) { | ||
| PyMem_Free((void *)buffer); | ||
| } | ||
| buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * buflen); | ||
| buffer = PyMem_New(wchar_t, buflen); | ||
| if (!buffer) { | ||
| PyErr_NoMemory(); | ||
| goto exit; | ||
|
|
@@ -5691,7 +5695,7 @@ os__path_splitroot_impl(PyObject *module, path_t *path) | |
| PyObject *result = NULL; | ||
| HRESULT ret; | ||
|
|
||
| buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * (wcslen(path->wide) + 1)); | ||
| buffer = PyMem_New(wchar_t, wcslen(path->wide) + 1); | ||
| if (!buffer) { | ||
| return PyErr_NoMemory(); | ||
| } | ||
|
|
@@ -7293,6 +7297,11 @@ fsconvert_strdup(PyObject *o, EXECV_CHAR **out) | |
| if (!PyUnicode_FSConverter(o, &ub)) | ||
| return 0; | ||
| size = PyBytes_GET_SIZE(ub); | ||
| if (size == PY_SSIZE_T_MAX) { | ||
|
Comment on lines
7299
to
+7300
Member
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. another impossible condition. |
||
| PyErr_NoMemory(); | ||
| Py_DECREF(ub); | ||
| return 0; | ||
| } | ||
|
Comment on lines
+7300
to
+7304
Member
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. Since size is obtained from PyUnicode_AsWideCharString(), adding 1 cannot cause an overflow. |
||
| *out = PyMem_Malloc(size + 1); | ||
| if (*out) { | ||
| memcpy(*out, PyBytes_AS_STRING(ub), size + 1); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -413,6 +413,10 @@ copy_single(PyMemoryViewObject *self, const Py_buffer *dest, const Py_buffer *sr | |
| return -1; | ||
|
|
||
| if (!last_dim_is_contiguous(dest, src)) { | ||
| if (dest->shape[0] > PY_SSIZE_T_MAX / dest->itemsize) { | ||
|
Member
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. be defensive, itemsize could be 0. |
||
| PyErr_NoMemory(); | ||
| return -1; | ||
| } | ||
| mem = PyMem_Malloc(dest->shape[0] * dest->itemsize); | ||
| if (mem == NULL) { | ||
| PyErr_NoMemory(); | ||
|
|
@@ -445,6 +449,10 @@ copy_buffer(const Py_buffer *dest, const Py_buffer *src) | |
| return -1; | ||
|
|
||
| if (!last_dim_is_contiguous(dest, src)) { | ||
| if (dest->shape[dest->ndim-1] > PY_SSIZE_T_MAX / dest->itemsize) { | ||
| PyErr_NoMemory(); | ||
| return -1; | ||
| } | ||
| mem = PyMem_Malloc(dest->shape[dest->ndim-1] * dest->itemsize); | ||
| if (mem == NULL) { | ||
| PyErr_NoMemory(); | ||
|
|
@@ -503,7 +511,7 @@ buffer_to_contiguous(char *mem, const Py_buffer *src, char order) | |
| assert(src->shape != NULL); | ||
| assert(src->strides != NULL); | ||
|
|
||
| strides = PyMem_Malloc(src->ndim * (sizeof *src->strides)); | ||
| strides = PyMem_New(Py_ssize_t, src->ndim); | ||
| if (strides == NULL) { | ||
| PyErr_NoMemory(); | ||
| return -1; | ||
|
|
@@ -861,7 +869,12 @@ static int | |
| mbuf_copy_format(_PyManagedBufferObject *mbuf, const char *fmt) | ||
| { | ||
| if (fmt != NULL) { | ||
| char *cp = PyMem_Malloc(strlen(fmt)+1); | ||
| size_t fmt_len = strlen(fmt); | ||
| if (fmt_len >= (size_t)PY_SSIZE_T_MAX) { | ||
| PyErr_NoMemory(); | ||
| return -1; | ||
| } | ||
| char *cp = PyMem_Malloc(fmt_len+1); | ||
| if (cp == NULL) { | ||
| PyErr_NoMemory(); | ||
| return -1; | ||
|
|
@@ -1065,6 +1078,10 @@ PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char orde | |
| } | ||
|
|
||
| /* buffer_to_contiguous() assumes PyBUF_FULL */ | ||
| if ((size_t)src->ndim > ((size_t)PY_SSIZE_T_MAX - sizeof *fb) / (3 * sizeof *fb->array)) { | ||
| PyErr_NoMemory(); | ||
| return -1; | ||
| } | ||
| fb = PyMem_Malloc(sizeof *fb + 3 * src->ndim * (sizeof *fb->array)); | ||
| if (fb == NULL) { | ||
| PyErr_NoMemory(); | ||
|
|
||
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.
these are not vulnerabilities. just bugs. "Fix potentially overflowing size computations on internal uses of PyMem_Malloc."