Skip to content

Commit 4b42d57

Browse files
vstinnerlarryhastings
authored andcommitted
[3.4] bpo-34656: Avoid relying on signed overflow in _pickle memos (GH-9261) (#11870)
* bpo-34656: Avoid relying on signed overflow in _pickle memos (GH-9261) (cherry picked from commit a4ae828)
1 parent 6c655ce commit 4b42d57

1 file changed

Lines changed: 33 additions & 28 deletions

File tree

‎Modules/_pickle.c‎

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -516,9 +516,9 @@ typedef struct {
516516
} PyMemoEntry;
517517

518518
typedef struct {
519-
Py_ssize_t mt_mask;
520-
Py_ssize_t mt_used;
521-
Py_ssize_t mt_allocated;
519+
size_t mt_mask;
520+
size_t mt_used;
521+
size_t mt_allocated;
522522
PyMemoEntry *mt_table;
523523
} PyMemoTable;
524524

@@ -562,8 +562,8 @@ typedef struct UnpicklerObject {
562562
/* The unpickler memo is just an array of PyObject *s. Using a dict
563563
is unnecessary, since the keys are contiguous ints. */
564564
PyObject **memo;
565-
Py_ssize_t memo_size; /* Capacity of the memo array */
566-
Py_ssize_t memo_len; /* Number of objects in the memo */
565+
size_t memo_size; /* Capacity of the memo array */
566+
size_t memo_len; /* Number of objects in the memo */
567567

568568
PyObject *pers_func; /* persistent_load() method, can be NULL. */
569569

@@ -647,7 +647,7 @@ PyMemoTable_New(void)
647647
static PyMemoTable *
648648
PyMemoTable_Copy(PyMemoTable *self)
649649
{
650-
Py_ssize_t i;
650+
size_t i;
651651
PyMemoTable *new = PyMemoTable_New();
652652
if (new == NULL)
653653
return NULL;
@@ -710,7 +710,7 @@ _PyMemoTable_Lookup(PyMemoTable *self, PyObject *key)
710710
{
711711
size_t i;
712712
size_t perturb;
713-
size_t mask = (size_t)self->mt_mask;
713+
size_t mask = self->mt_mask;
714714
PyMemoEntry *table = self->mt_table;
715715
PyMemoEntry *entry;
716716
Py_hash_t hash = (Py_hash_t)key >> 3;
@@ -732,22 +732,24 @@ _PyMemoTable_Lookup(PyMemoTable *self, PyObject *key)
732732

733733
/* Returns -1 on failure, 0 on success. */
734734
static int
735-
_PyMemoTable_ResizeTable(PyMemoTable *self, Py_ssize_t min_size)
735+
_PyMemoTable_ResizeTable(PyMemoTable *self, size_t min_size)
736736
{
737737
PyMemoEntry *oldtable = NULL;
738738
PyMemoEntry *oldentry, *newentry;
739-
Py_ssize_t new_size = MT_MINSIZE;
740-
Py_ssize_t to_process;
739+
size_t new_size = MT_MINSIZE;
740+
size_t to_process;
741741

742742
assert(min_size > 0);
743743

744-
/* Find the smallest valid table size >= min_size. */
745-
while (new_size < min_size && new_size > 0)
746-
new_size <<= 1;
747-
if (new_size <= 0) {
744+
if (min_size > PY_SSIZE_T_MAX) {
748745
PyErr_NoMemory();
749746
return -1;
750747
}
748+
749+
/* Find the smallest valid table size >= min_size. */
750+
while (new_size < min_size) {
751+
new_size <<= 1;
752+
}
751753
/* new_size needs to be a power of two. */
752754
assert((new_size & (new_size - 1)) == 0);
753755

@@ -797,6 +799,7 @@ static int
797799
PyMemoTable_Set(PyMemoTable *self, PyObject *key, Py_ssize_t value)
798800
{
799801
PyMemoEntry *entry;
802+
size_t desired_size;
800803

801804
assert(key != NULL);
802805

@@ -820,10 +823,12 @@ PyMemoTable_Set(PyMemoTable *self, PyObject *key, Py_ssize_t value)
820823
* Very large memo tables (over 50K items) use doubling instead.
821824
* This may help applications with severe memory constraints.
822825
*/
823-
if (!(self->mt_used * 3 >= (self->mt_mask + 1) * 2))
826+
if (SIZE_MAX / 3 >= self->mt_used && self->mt_used * 3 < self->mt_allocated * 2) {
824827
return 0;
825-
return _PyMemoTable_ResizeTable(self,
826-
(self->mt_used > 50000 ? 2 : 4) * self->mt_used);
828+
}
829+
// self->mt_used is always < PY_SSIZE_T_MAX, so this can't overflow.
830+
desired_size = (self->mt_used > 50000 ? 2 : 4) * self->mt_used;
831+
return _PyMemoTable_ResizeTable(self, desired_size);
827832
}
828833

829834
#undef MT_MINSIZE
@@ -1263,9 +1268,9 @@ _Unpickler_Readline(UnpicklerObject *self, char **result)
12631268
/* Returns -1 (with an exception set) on failure, 0 on success. The memo array
12641269
will be modified in place. */
12651270
static int
1266-
_Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size)
1271+
_Unpickler_ResizeMemoList(UnpicklerObject *self, size_t new_size)
12671272
{
1268-
Py_ssize_t i;
1273+
size_t i;
12691274

12701275
assert(new_size > self->memo_size);
12711276

@@ -1282,9 +1287,9 @@ _Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size)
12821287

12831288
/* Returns NULL if idx is out of bounds. */
12841289
static PyObject *
1285-
_Unpickler_MemoGet(UnpicklerObject *self, Py_ssize_t idx)
1290+
_Unpickler_MemoGet(UnpicklerObject *self, size_t idx)
12861291
{
1287-
if (idx < 0 || idx >= self->memo_size)
1292+
if (idx >= self->memo_size)
12881293
return NULL;
12891294

12901295
return self->memo[idx];
@@ -1293,7 +1298,7 @@ _Unpickler_MemoGet(UnpicklerObject *self, Py_ssize_t idx)
12931298
/* Returns -1 (with an exception set) on failure, 0 on success.
12941299
This takes its own reference to `value`. */
12951300
static int
1296-
_Unpickler_MemoPut(UnpicklerObject *self, Py_ssize_t idx, PyObject *value)
1301+
_Unpickler_MemoPut(UnpicklerObject *self, size_t idx, PyObject *value)
12971302
{
12981303
PyObject *old_item;
12991304

@@ -4129,7 +4134,7 @@ static PyObject *
41294134
_pickle_PicklerMemoProxy_copy_impl(PicklerMemoProxyObject *self)
41304135
/*[clinic end generated code: output=bb83a919d29225ef input=b73043485ac30b36]*/
41314136
{
4132-
Py_ssize_t i;
4137+
size_t i;
41334138
PyMemoTable *memo;
41344139
PyObject *new_memo = PyDict_New();
41354140
if (new_memo == NULL)
@@ -6545,7 +6550,7 @@ static PyObject *
65456550
_pickle_UnpicklerMemoProxy_copy_impl(UnpicklerMemoProxyObject *self)
65466551
/*[clinic end generated code: output=e12af7e9bc1e4c77 input=97769247ce032c1d]*/
65476552
{
6548-
Py_ssize_t i;
6553+
size_t i;
65496554
PyObject *new_memo = PyDict_New();
65506555
if (new_memo == NULL)
65516556
return NULL;
@@ -6696,8 +6701,7 @@ static int
66966701
Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
66976702
{
66986703
PyObject **new_memo;
6699-
Py_ssize_t new_memo_size = 0;
6700-
Py_ssize_t i;
6704+
size_t new_memo_size = 0;
67016705

67026706
if (obj == NULL) {
67036707
PyErr_SetString(PyExc_TypeError,
@@ -6706,6 +6710,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
67066710
}
67076711

67086712
if (Py_TYPE(obj) == &UnpicklerMemoProxyType) {
6713+
size_t i;
67096714
UnpicklerObject *unpickler =
67106715
((UnpicklerMemoProxyObject *)obj)->unpickler;
67116716

@@ -6762,8 +6767,8 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
67626767

67636768
error:
67646769
if (new_memo_size) {
6765-
i = new_memo_size;
6766-
while (--i >= 0) {
6770+
size_t i;
6771+
for (i = new_memo_size - 1; i != SIZE_MAX; i--) {
67676772
Py_XDECREF(new_memo[i]);
67686773
}
67696774
PyMem_FREE(new_memo);

0 commit comments

Comments
 (0)