Skip to content

Commit 5b628ab

Browse files
Fix overflow vulnerabilities in PyMem_Malloc calls (#127681)
1 parent 548c731 commit 5b628ab

21 files changed

Lines changed: 120 additions & 44 deletions

Modules/_ctypes/_ctypes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1784,7 +1784,7 @@ PyCArrayType_init(PyObject *self, PyObject *args, PyObject *kwds)
17841784
if (stginfo->format == NULL)
17851785
goto error;
17861786
stginfo->ndim = iteminfo->ndim + 1;
1787-
stginfo->shape = PyMem_Malloc(sizeof(Py_ssize_t) * stginfo->ndim);
1787+
stginfo->shape = PyMem_New(Py_ssize_t, stginfo->ndim);
17881788
if (stginfo->shape == NULL) {
17891789
PyErr_NoMemory();
17901790
goto error;

Modules/_ctypes/stgdict.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,35 +42,49 @@ PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info)
4242
dst_info->pointer_type = NULL; // the cache cannot be shared
4343

4444
if (src_info->format) {
45-
dst_info->format = PyMem_Malloc(strlen(src_info->format) + 1);
45+
size_t s = strlen(src_info->format);
46+
if (s >= (size_t)PY_SSIZE_T_MAX) {
47+
goto oom;
48+
}
49+
dst_info->format = PyMem_Malloc(s + 1);
4650
if (dst_info->format == NULL) {
47-
PyErr_NoMemory();
48-
return -1;
51+
goto oom;
4952
}
5053
strcpy(dst_info->format, src_info->format);
5154
}
5255
if (src_info->shape) {
53-
dst_info->shape = PyMem_Malloc(sizeof(Py_ssize_t) * src_info->ndim);
56+
dst_info->shape = PyMem_New(Py_ssize_t, src_info->ndim);
5457
if (dst_info->shape == NULL) {
55-
PyErr_NoMemory();
56-
return -1;
58+
goto oom;
5759
}
5860
memcpy(dst_info->shape, src_info->shape,
5961
sizeof(Py_ssize_t) * src_info->ndim);
6062
}
6163

6264
if (src_info->ffi_type_pointer.elements == NULL)
6365
return 0;
66+
if ((size_t)src_info->length > (size_t)PY_SSIZE_T_MAX / sizeof(ffi_type *) - 1) {
67+
goto oom;
68+
}
6469
size = sizeof(ffi_type *) * (src_info->length + 1);
6570
dst_info->ffi_type_pointer.elements = PyMem_Malloc(size);
6671
if (dst_info->ffi_type_pointer.elements == NULL) {
67-
PyErr_NoMemory();
68-
return -1;
72+
goto oom;
6973
}
7074
memcpy(dst_info->ffi_type_pointer.elements,
7175
src_info->ffi_type_pointer.elements,
7276
size);
7377
return 0;
78+
79+
oom:
80+
if (src_info->format) {
81+
PyMem_Free(dst_info->format);
82+
}
83+
if (src_info->shape) {
84+
PyMem_Free(dst_info->shape);
85+
}
86+
PyErr_NoMemory();
87+
return -1;
7488
}
7589

7690
/* 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
342356
PyMem_Free(stginfo->format);
343357
stginfo->format = NULL;
344358
}
359+
if (format_spec_size >= PY_SSIZE_T_MAX) {
360+
PyErr_NoMemory();
361+
goto error;
362+
}
345363
stginfo->format = PyMem_Malloc(format_spec_size + 1);
346364
if (!stginfo->format) {
347365
PyErr_NoMemory();

Modules/_decimal/_decimal.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2267,10 +2267,10 @@ numeric_as_ascii(PyObject *u, int strip_ws, int ignore_underscores)
22672267
data = PyUnicode_DATA(u);
22682268
len = PyUnicode_GET_LENGTH(u);
22692269

2270+
// Note: this should not overflow since the number of digits is << 2^32.
22702271
cp = res = PyMem_Malloc(len+1);
22712272
if (res == NULL) {
2272-
PyErr_NoMemory();
2273-
return NULL;
2273+
goto oom;
22742274
}
22752275

22762276
j = 0;
@@ -2306,6 +2306,10 @@ numeric_as_ascii(PyObject *u, int strip_ws, int ignore_underscores)
23062306
}
23072307
*cp = '\0';
23082308
return res;
2309+
2310+
oom:
2311+
PyErr_NoMemory();
2312+
return NULL;
23092313
}
23102314

23112315
/* Return a new PyDecObject or a subtype from a C string. Use the context
@@ -2845,6 +2849,7 @@ dectuple_as_str(PyObject *dectuple)
28452849

28462850
tsize = PyTuple_Size(digits);
28472851
/* [sign][coeffdigits+1][E][-][expdigits+1]['\0'] */
2852+
// TODO: this should not overflow since we would be below the digits limit
28482853
mem = 1 + tsize + 3 + MPD_EXPDIGITS + 2;
28492854
cp = decstring = PyMem_Malloc(mem);
28502855
if (decstring == NULL) {

Modules/_multiprocessing/semaphore.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,11 @@ _multiprocessing_SemLock_impl(PyTypeObject *type, int kind, int value,
505505
}
506506

507507
if (!unlink) {
508-
name_copy = PyMem_Malloc(strlen(name) + 1);
508+
size_t name_len = strlen(name);
509+
if (name_len >= (size_t)PY_SSIZE_T_MAX) {
510+
return PyErr_NoMemory();
511+
}
512+
name_copy = PyMem_Malloc(name_len + 1);
509513
if (name_copy == NULL) {
510514
return PyErr_NoMemory();
511515
}
@@ -558,7 +562,11 @@ _multiprocessing_SemLock__rebuild_impl(PyTypeObject *type, SEM_HANDLE handle,
558562
char *name_copy = NULL;
559563

560564
if (name != NULL) {
561-
name_copy = PyMem_Malloc(strlen(name) + 1);
565+
size_t name_len = strlen(name);
566+
if (name_len >= (size_t)PY_SSIZE_T_MAX) {
567+
return PyErr_NoMemory();
568+
}
569+
name_copy = PyMem_Malloc(name_len + 1);
562570
if (name_copy == NULL)
563571
return PyErr_NoMemory();
564572
strcpy(name_copy, name);

Modules/_winapi.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1591,7 +1591,7 @@ _winapi_GetLongPathName_impl(PyObject *module, LPCWSTR path)
15911591
cchBuffer = GetLongPathNameW(path, NULL, 0);
15921592
Py_END_ALLOW_THREADS
15931593
if (cchBuffer) {
1594-
WCHAR *buffer = (WCHAR *)PyMem_Malloc(cchBuffer * sizeof(WCHAR));
1594+
WCHAR *buffer = PyMem_New(WCHAR, cchBuffer);
15951595
if (buffer) {
15961596
Py_BEGIN_ALLOW_THREADS
15971597
cchBuffer = GetLongPathNameW(path, buffer, cchBuffer);
@@ -1672,7 +1672,7 @@ _winapi_GetShortPathName_impl(PyObject *module, LPCWSTR path)
16721672
cchBuffer = GetShortPathNameW(path, NULL, 0);
16731673
Py_END_ALLOW_THREADS
16741674
if (cchBuffer) {
1675-
WCHAR *buffer = (WCHAR *)PyMem_Malloc(cchBuffer * sizeof(WCHAR));
1675+
WCHAR *buffer = PyMem_New(WCHAR, cchBuffer);
16761676
if (buffer) {
16771677
Py_BEGIN_ALLOW_THREADS
16781678
cchBuffer = GetShortPathNameW(path, buffer, cchBuffer);

Modules/_zoneinfo.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,13 +1042,12 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj)
10421042
self->num_ttinfos = (size_t)num_ttinfos;
10431043

10441044
// Load the transition indices and list
1045-
self->trans_list_utc =
1046-
PyMem_Malloc(self->num_transitions * sizeof(int64_t));
1045+
self->trans_list_utc = PyMem_New(int64_t, self->num_transitions);
10471046
if (self->trans_list_utc == NULL) {
10481047
PyErr_NoMemory();
10491048
goto error;
10501049
}
1051-
trans_idx = PyMem_Malloc(self->num_transitions * sizeof(Py_ssize_t));
1050+
trans_idx = PyMem_New(Py_ssize_t, self->num_transitions);
10521051
if (trans_idx == NULL) {
10531052
PyErr_NoMemory();
10541053
goto error;
@@ -1086,8 +1085,8 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj)
10861085
}
10871086

10881087
// Load UTC offsets and isdst (size num_ttinfos)
1089-
utcoff = PyMem_Malloc(self->num_ttinfos * sizeof(long));
1090-
isdst = PyMem_Malloc(self->num_ttinfos * sizeof(unsigned char));
1088+
utcoff = PyMem_New(long, self->num_ttinfos);
1089+
isdst = PyMem_New(unsigned char, self->num_ttinfos);
10911090

10921091
if (utcoff == NULL || isdst == NULL) {
10931092
PyErr_NoMemory();
@@ -1135,7 +1134,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj)
11351134
}
11361135

11371136
// Build _ttinfo objects from utcoff, dstoff and abbr
1138-
self->_ttinfos = PyMem_Malloc(self->num_ttinfos * sizeof(_ttinfo));
1137+
self->_ttinfos = PyMem_New(_ttinfo, self->num_ttinfos);
11391138
if (self->_ttinfos == NULL) {
11401139
PyErr_NoMemory();
11411140
goto error;
@@ -2148,7 +2147,7 @@ ts_to_local(size_t *trans_idx, int64_t *trans_utc, long *utcoff,
21482147

21492148
// Copy the UTC transitions into each array to be modified in place later
21502149
for (size_t i = 0; i < 2; ++i) {
2151-
trans_local[i] = PyMem_Malloc(num_transitions * sizeof(int64_t));
2150+
trans_local[i] = PyMem_New(int64_t, num_transitions);
21522151
if (trans_local[i] == NULL) {
21532152
PyErr_NoMemory();
21542153
return -1;

Modules/posixmodule.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1989,6 +1989,10 @@ win32_wchdir(LPCWSTR path)
19891989
if (!result)
19901990
return FALSE;
19911991
if (result > Py_ARRAY_LENGTH(path_buf)) {
1992+
if ((size_t)result > (size_t)PY_SSIZE_T_MAX / sizeof(wchar_t)) {
1993+
SetLastError(ERROR_OUTOFMEMORY);
1994+
return FALSE;
1995+
}
19921996
new_path = PyMem_RawMalloc(result * sizeof(wchar_t));
19931997
if (!new_path) {
19941998
SetLastError(ERROR_OUTOFMEMORY);
@@ -5317,7 +5321,7 @@ os_listmounts_impl(PyObject *module, path_t *volume)
53175321
if (buffer != default_buffer) {
53185322
PyMem_Free((void *)buffer);
53195323
}
5320-
buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * buflen);
5324+
buffer = PyMem_New(wchar_t, buflen);
53215325
if (!buffer) {
53225326
PyErr_NoMemory();
53235327
goto exit;
@@ -5689,7 +5693,7 @@ os__path_splitroot_impl(PyObject *module, path_t *path)
56895693
PyObject *result = NULL;
56905694
HRESULT ret;
56915695

5692-
buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * (wcslen(path->wide) + 1));
5696+
buffer = PyMem_New(wchar_t, wcslen(path->wide) + 1);
56935697
if (!buffer) {
56945698
return PyErr_NoMemory();
56955699
}
@@ -7291,6 +7295,11 @@ fsconvert_strdup(PyObject *o, EXECV_CHAR **out)
72917295
if (!PyUnicode_FSConverter(o, &ub))
72927296
return 0;
72937297
size = PyBytes_GET_SIZE(ub);
7298+
if (size == PY_SSIZE_T_MAX) {
7299+
PyErr_NoMemory();
7300+
Py_DECREF(ub);
7301+
return 0;
7302+
}
72947303
*out = PyMem_Malloc(size + 1);
72957304
if (*out) {
72967305
memcpy(*out, PyBytes_AS_STRING(ub), size + 1);

Objects/abstract.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ int PyObject_CopyData(PyObject *dest, PyObject *src)
719719
/* Otherwise a more elaborate copy scheme is needed */
720720

721721
/* XXX(nnorwitz): need to check for overflow! */
722-
indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*view_src.ndim);
722+
indices = PyMem_New(Py_ssize_t, view_src.ndim);
723723
if (indices == NULL) {
724724
PyErr_NoMemory();
725725
PyBuffer_Release(&view_dest);

Objects/call.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,10 +484,13 @@ _PyObject_Call_Prepend(PyThreadState *tstate, PyObject *callable,
484484
PyObject **stack;
485485

486486
Py_ssize_t argcount = PyTuple_GET_SIZE(args);
487-
if (argcount + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) {
487+
if (argcount <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack) - 1) {
488488
stack = small_stack;
489489
}
490490
else {
491+
// Note: argcount + 1 is likely small enough not to overflow
492+
// and we don't want to hurt performances by adding an
493+
// additional check.
491494
stack = PyMem_Malloc((argcount + 1) * sizeof(PyObject *));
492495
if (stack == NULL) {
493496
PyErr_NoMemory();
@@ -798,6 +801,9 @@ object_vacall(PyThreadState *tstate, PyObject *base,
798801
stack = small_stack;
799802
}
800803
else {
804+
// Note: nargs is likely small enough not to overflow and
805+
// we don't want to hurt performances by adding an
806+
// additional check.
801807
stack = PyMem_Malloc(nargs * sizeof(stack[0]));
802808
if (stack == NULL) {
803809
PyErr_NoMemory();

Objects/capsule.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ PyCapsule_Import(const char *name, int no_block)
232232
PyObject *object = NULL;
233233
void *return_value = NULL;
234234
char *trace;
235+
// Note: strlen(name) is likely smaller than the max alloc. size.
235236
size_t name_length = (strlen(name) + 1) * sizeof(char);
236237
char *name_dup = (char *)PyMem_Malloc(name_length);
237238

0 commit comments

Comments
 (0)