8000 closes bpo-34656: Avoid relying on signed overflow in _pickle memos. … · python/cpython@ef33dd6 · GitHub
[go: up one dir, main page]

Skip to content

Commit ef33dd6

Browse files
vstinnerlarryhastings
authored andcommitted
closes bpo-34656: Avoid relying on signed overflow in _pickle memos. (GH-9261) (#11869)
(cherry picked from commit a4ae828)
1 parent efec763 commit ef33dd6

File tree

1 file changed

+32
-31
lines changed

1 file changed

+32
-31
lines changed

Modules/_pickle.c

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -527,9 +527,9 @@ typedef struct {
527527
} PyMemoEntry;
528528

529529
typedef struct {
530-
Py_ssize_t mt_mask;
531-
Py_ssize_t mt_used;
532-
Py_ssize_t mt_allocated;
530+
size_t mt_mask;
531+
size_t mt_used;
532+
size_t mt_allocated;
533533
PyMemoEntry *mt_table;
534534
} PyMemoTable;
535535

@@ -573,8 +573,8 @@ typedef struct UnpicklerObject {
573573
/* The unpickler memo is just an array of PyObject *s. Using a dict
574574
is unnecessary, since the keys are contiguous ints. */
575575
PyObject **memo;
576-
Py_ssize_t memo_size; /* Capacity of the memo array */
577-
Py_ssize_t memo_len; /* Number of objects in the memo */
576+
size_t memo_size; /* Capacity of the memo array */
577+
size_t memo_len; /* Number of objects in the memo */
578578

579579
PyObject *pers_func; /* persistent_load() method, can be NULL. */
580580

@@ -658,7 +658,6 @@ PyMemoTable_New(void)
658658
static PyMemoTable *
659659
PyMemoTable_Copy(PyMemoTable *self)
660660
{
661-
Py_ssize_t i;
662661
PyMemoTable *new = PyMemoTable_New();
663662
if (new == NULL)
664663
return NULL;
@@ -675,7 +674,7 @@ PyMemoTable_Copy(PyMemoTable *self)
675674
PyErr_NoMemory();
676675
return NULL;
677676
}
678-
for (i = 0; i < self->mt_allocated; i++) {
677+
for (size_t i = 0; i < self->mt_allocated; i++) {
679678
Py_XINCREF(self->mt_table[i].me_key);
680679
}
681680
memcpy(new->mt_table, self->mt_table,
@@ -721,7 +720,7 @@ _PyMemoTable_Lookup(PyMemoTable *self, PyObject *key)
721720
{
722721
size_t i;
723722
size_t perturb;
724-
size_t mask = (size_t)self->mt_mask;
723+
size_t mask = self->mt_mask;
725724
PyMemoEntry *table = self->mt_table;
726725
PyMemoEntry *entry;
727726
Py_hash_t hash = (Py_hash_t)key >> 3;
@@ -743,22 +742,24 @@ _PyMemoTable_Lookup(PyMemoTable *self, PyObject *key)
743742

744743
/* Returns -1 on failure, 0 on success. */
745744
static int
746-
_PyMemoTable_ResizeTable(PyMemoTable *self, Py_ssize_t min_size)
745+
_PyMemoTable_ResizeTable(PyMemoTable *self, size_t min_size)
747746
{
748747
PyMemoEntry *oldtable = NULL;
749748
PyMemoEntry *oldentry, *newentry;
750-
Py_ssize_t new_size = MT_MINSIZE;
751-
Py_ssize_t to_process;
749+
size_t new_size = MT_MINSIZE;
750+
size_t to_process;
752751

753752
assert(min_size > 0);
754753

755-
/* Find the smallest valid table size >= min_size. */
756-
while (new_size < min_size && new_size > 0)
757-
new_size <<= 1;
758-
if (new_size <= 0) {
754+
if (min_size > PY_SSIZE_T_MAX) {
759755
PyErr_NoMemory();
760756
return -1;
761757
}
758+
759+
/* Find the smallest valid table size >= min_size. */
760+
while (new_size < min_size) {
761+
new_size <<= 1;
762+
}
762763
/* new_size needs to be a power of two. */
763764
assert((new_size & (new_size - 1)) == 0);
764765

@@ -808,6 +809,7 @@ static int
808809
PyMemoTable_Set(PyMemoTable *self, PyObject *key, Py_ssize_t value)
809810
{
810811
PyMemoEntry *entry;
812+
size_t desired_size;
811813

812814
assert(key != NULL);
813815

@@ -831,10 +833,12 @@ PyMemoTable_Set(PyMemoTable *self, PyObject *key, Py_ssize_t value)
831833
* Very large memo tables (over 50K items) use doubling instead.
832834
* This may help applications with severe memory constraints.
833835
*/
834-
if (!(self->mt_used * 3 >= (self->mt_mask + 1) * 2))
836+
if (SIZE_MAX / 3 >= self->mt_used && self->mt_used * 3 < self->mt_allocated * 2) {
835837
return 0;
836-
return _PyMemoTable_ResizeTable(self,
837-
(self->mt_used > 50000 ? 2 : 4) * self->mt_used);
838+
}
839+
// self->mt_used is always < PY_SSIZE_T_MAX, so this can't overflow.
840+
desired_size = (self->mt_used > 50000 ? 2 : 4) * self->mt_used;
841+
return _PyMemoTable_ResizeTable(self, desired_size);
838842
}
839843

840844
#undef MT_MINSIZE
@@ -1273,9 +1277,9 @@ _Unpickler_Readline(UnpicklerObject *self, char **result)
12731277
/* Returns -1 (with an exception set) on failure, 0 on success. The memo array
12741278
will be modified in place. */
12751279
static int
1276-
_Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size)
1280+
_Unpickler_ResizeMemoList(UnpicklerObject *self, size_t new_size)
12771281
{
1278-
Py_ssize_t i;
1282+
size_t i;
12791283

12801284
assert(new_size > self->memo_size);
12811285

@@ -1292,9 +1296,9 @@ _Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size)
12921296

12931297
/* Returns NULL if idx is out of bounds. */
12941298
static PyObject *
1295-
_Unpickler_MemoGet(UnpicklerObject *self, Py_ssize_t idx)
1299+
_Unpickler_MemoGet(UnpicklerObject *self, size_t idx)
12961300
{
1297-
if (idx < 0 || idx >= self->memo_size)
1301+
if (idx >= self->memo_size)
12981302
return NULL;
12991303

13001304
return self->memo[idx];
@@ -1303,7 +1307,7 @@ _Unpickler_MemoGet(UnpicklerObject *self, Py_ssize_t idx)
13031307
/* Returns -1 (with an exception set) on failure, 0 on success.
13041308
This takes its own reference to `value`. */
13051309
static int
1306-
_Unpickler_MemoPut(UnpicklerObject *self, Py_ssize_t idx, PyObject *value)
1310+
_Unpickler_MemoPut(UnpicklerObject *self, size_t idx, PyObject *value)
13071311
{
13081312
PyObject *old_item;
13091313

@@ -4194,14 +4198,13 @@ static PyObject *
41944198
_pickle_PicklerMemoProxy_copy_impl(PicklerMemoProxyObject *self)
41954199
/*[clinic end generated code: output=bb83a919d29225ef input=b73043485ac30b36]*/
41964200
{
4197-
Py_ssize_t i;
41984201
PyMemoTable *memo;
41994202
PyObject *new_memo = PyDict_New();
42004203
if (new_memo == NULL)
42014204
return NULL;
42024205

42034206
memo = self->pickler->memo;
4204-
for (i = 0; i < memo->mt_allocated; ++i) {
4207+
for (size_t i = 0; i < memo->mt_allocated; ++i) {
42054208
PyMemoEntry entry = memo->mt_table[i];
42064209
if (entry.me_key != NULL) {
42074210
int status;
@@ -6620,7 +6623,7 @@ static PyObject *
66206623
_pickle_UnpicklerMemoProxy_copy_impl(UnpicklerMemoProxyObject *self)
66216624
/*[clinic end generated code: output=e12af7e9bc1e4c77 input=97769247ce032c1d]*/
66226625
{
6623-
Py_ssize_t i;
6626+
size_t i;
66246627
PyObject *new_memo = PyDict_New();
66256628
if (new_memo == NULL)
66266629
return NULL;
@@ -6771,8 +6774,7 @@ static int
67716774
Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
67726775
{
67736776
PyObject **new_memo;
6774-
Py_ssize_t new_memo_size = 0;
6775-
Py_ssize_t i;
6777+
size_t new_memo_size = 0;
67766778

67776779
if (obj == NULL) {
67786780
PyErr_SetString(PyExc_TypeError,
@@ -6789,7 +6791,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
67896791
if (new_memo == NULL)
67906792
return -1;
67916793

6792-
for (i = 0; i < new_memo_size; i++) {
6794+
for (size_t i = 0; i < new_memo_size; i++) {
67936795
Py_XINCREF(unpickler->memo[i]);
67946796
new_memo[i] = unpickler->memo[i];
67956797
}
@@ -6837,8 +6839,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
68376839

68386840
error:
68396841
if (new_memo_size) {
6840-
i = new_memo_size;
6841-
while (--i >= 0) {
6842+
for (size_t i = new_memo_size - 1; i != SIZE_MAX; i--) {
68426843
Py_XDECREF(new_memo[i]);
68436844
}
68446845
PyMem_FREE(new_memo);

0 commit comments

Comments
 (0)
0