Unverified Kaydet (Commit) a4ae828e authored tarafından Benjamin Peterson's avatar Benjamin Peterson Kaydeden (comit) GitHub

closes bpo-34656: Avoid relying on signed overflow in _pickle memos. (GH-9261)

üst f14c28f3
...@@ -602,9 +602,9 @@ typedef struct { ...@@ -602,9 +602,9 @@ typedef struct {
} PyMemoEntry; } PyMemoEntry;
typedef struct { typedef struct {
Py_ssize_t mt_mask; size_t mt_mask;
Py_ssize_t mt_used; size_t mt_used;
Py_ssize_t mt_allocated; size_t mt_allocated;
PyMemoEntry *mt_table; PyMemoEntry *mt_table;
} PyMemoTable; } PyMemoTable;
...@@ -650,8 +650,8 @@ typedef struct UnpicklerObject { ...@@ -650,8 +650,8 @@ typedef struct UnpicklerObject {
/* The unpickler memo is just an array of PyObject *s. Using a dict /* The unpickler memo is just an array of PyObject *s. Using a dict
is unnecessary, since the keys are contiguous ints. */ is unnecessary, since the keys are contiguous ints. */
PyObject **memo; PyObject **memo;
Py_ssize_t memo_size; /* Capacity of the memo array */ size_t memo_size; /* Capacity of the memo array */
Py_ssize_t memo_len; /* Number of objects in the memo */ size_t memo_len; /* Number of objects in the memo */
PyObject *pers_func; /* persistent_load() method, can be NULL. */ PyObject *pers_func; /* persistent_load() method, can be NULL. */
PyObject *pers_func_self; /* borrowed reference to self if pers_func PyObject *pers_func_self; /* borrowed reference to self if pers_func
...@@ -737,7 +737,6 @@ PyMemoTable_New(void) ...@@ -737,7 +737,6 @@ PyMemoTable_New(void)
static PyMemoTable * static PyMemoTable *
PyMemoTable_Copy(PyMemoTable *self) PyMemoTable_Copy(PyMemoTable *self)
{ {
Py_ssize_t i;
PyMemoTable *new = PyMemoTable_New(); PyMemoTable *new = PyMemoTable_New();
if (new == NULL) if (new == NULL)
return NULL; return NULL;
...@@ -754,7 +753,7 @@ PyMemoTable_Copy(PyMemoTable *self) ...@@ -754,7 +753,7 @@ PyMemoTable_Copy(PyMemoTable *self)
PyErr_NoMemory(); PyErr_NoMemory();
return NULL; return NULL;
} }
for (i = 0; i < self->mt_allocated; i++) { for (size_t i = 0; i < self->mt_allocated; i++) {
Py_XINCREF(self->mt_table[i].me_key); Py_XINCREF(self->mt_table[i].me_key);
} }
memcpy(new->mt_table, self->mt_table, memcpy(new->mt_table, self->mt_table,
...@@ -800,7 +799,7 @@ _PyMemoTable_Lookup(PyMemoTable *self, PyObject *key) ...@@ -800,7 +799,7 @@ _PyMemoTable_Lookup(PyMemoTable *self, PyObject *key)
{ {
size_t i; size_t i;
size_t perturb; size_t perturb;
size_t mask = (size_t)self->mt_mask; size_t mask = self->mt_mask;
PyMemoEntry *table = self->mt_table; PyMemoEntry *table = self->mt_table;
PyMemoEntry *entry; PyMemoEntry *entry;
Py_hash_t hash = (Py_hash_t)key >> 3; Py_hash_t hash = (Py_hash_t)key >> 3;
...@@ -821,22 +820,24 @@ _PyMemoTable_Lookup(PyMemoTable *self, PyObject *key) ...@@ -821,22 +820,24 @@ _PyMemoTable_Lookup(PyMemoTable *self, PyObject *key)
/* Returns -1 on failure, 0 on success. */ /* Returns -1 on failure, 0 on success. */
static int static int
_PyMemoTable_ResizeTable(PyMemoTable *self, Py_ssize_t min_size) _PyMemoTable_ResizeTable(PyMemoTable *self, size_t min_size)
{ {
PyMemoEntry *oldtable = NULL; PyMemoEntry *oldtable = NULL;
PyMemoEntry *oldentry, *newentry; PyMemoEntry *oldentry, *newentry;
Py_ssize_t new_size = MT_MINSIZE; size_t new_size = MT_MINSIZE;
Py_ssize_t to_process; size_t to_process;
assert(min_size > 0); assert(min_size > 0);
/* Find the smallest valid table size >= min_size. */ if (min_size > PY_SSIZE_T_MAX) {
while (new_size < min_size && new_size > 0)
new_size <<= 1;
if (new_size <= 0) {
PyErr_NoMemory(); PyErr_NoMemory();
return -1; return -1;
} }
/* Find the smallest valid table size >= min_size. */
while (new_size < min_size) {
new_size <<= 1;
}
/* new_size needs to be a power of two. */ /* new_size needs to be a power of two. */
assert((new_size & (new_size - 1)) == 0); assert((new_size & (new_size - 1)) == 0);
...@@ -909,10 +910,12 @@ PyMemoTable_Set(PyMemoTable *self, PyObject *key, Py_ssize_t value) ...@@ -909,10 +910,12 @@ PyMemoTable_Set(PyMemoTable *self, PyObject *key, Py_ssize_t value)
* Very large memo tables (over 50K items) use doubling instead. * Very large memo tables (over 50K items) use doubling instead.
* This may help applications with severe memory constraints. * This may help applications with severe memory constraints.
*/ */
if (!(self->mt_used * 3 >= (self->mt_mask + 1) * 2)) if (SIZE_MAX / 3 >= self->mt_used && self->mt_used * 3 < self->mt_allocated * 2) {
return 0; return 0;
return _PyMemoTable_ResizeTable(self, }
(self->mt_used > 50000 ? 2 : 4) * self->mt_used); // self->mt_used is always < PY_SSIZE_T_MAX, so this can't overflow.
size_t desired_size = (self->mt_used > 50000 ? 2 : 4) * self->mt_used;
return _PyMemoTable_ResizeTable(self, desired_size);
} }
#undef MT_MINSIZE #undef MT_MINSIZE
...@@ -1376,9 +1379,9 @@ _Unpickler_Readline(UnpicklerObject *self, char **result) ...@@ -1376,9 +1379,9 @@ _Unpickler_Readline(UnpicklerObject *self, char **result)
/* Returns -1 (with an exception set) on failure, 0 on success. The memo array /* Returns -1 (with an exception set) on failure, 0 on success. The memo array
will be modified in place. */ will be modified in place. */
static int static int
_Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size) _Unpickler_ResizeMemoList(UnpicklerObject *self, size_t new_size)
{ {
Py_ssize_t i; size_t i;
assert(new_size > self->memo_size); assert(new_size > self->memo_size);
...@@ -1397,9 +1400,9 @@ _Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size) ...@@ -1397,9 +1400,9 @@ _Unpickler_ResizeMemoList(UnpicklerObject *self, Py_ssize_t new_size)
/* Returns NULL if idx is out of bounds. */ /* Returns NULL if idx is out of bounds. */
static PyObject * static PyObject *
_Unpickler_MemoGet(UnpicklerObject *self, Py_ssize_t idx) _Unpickler_MemoGet(UnpicklerObject *self, size_t idx)
{ {
if (idx < 0 || idx >= self->memo_size) if (idx >= self->memo_size)
return NULL; return NULL;
return self->memo[idx]; return self->memo[idx];
...@@ -1408,7 +1411,7 @@ _Unpickler_MemoGet(UnpicklerObject *self, Py_ssize_t idx) ...@@ -1408,7 +1411,7 @@ _Unpickler_MemoGet(UnpicklerObject *self, Py_ssize_t idx)
/* Returns -1 (with an exception set) on failure, 0 on success. /* Returns -1 (with an exception set) on failure, 0 on success.
This takes its own reference to `value`. */ This takes its own reference to `value`. */
static int static int
_Unpickler_MemoPut(UnpicklerObject *self, Py_ssize_t idx, PyObject *value) _Unpickler_MemoPut(UnpicklerObject *self, size_t idx, PyObject *value)
{ {
PyObject *old_item; PyObject *old_item;
...@@ -4413,14 +4416,13 @@ static PyObject * ...@@ -4413,14 +4416,13 @@ static PyObject *
_pickle_PicklerMemoProxy_copy_impl(PicklerMemoProxyObject *self) _pickle_PicklerMemoProxy_copy_impl(PicklerMemoProxyObject *self)
/*[clinic end generated code: output=bb83a919d29225ef input=b73043485ac30b36]*/ /*[clinic end generated code: output=bb83a919d29225ef input=b73043485ac30b36]*/
{ {
Py_ssize_t i;
PyMemoTable *memo; PyMemoTable *memo;
PyObject *new_memo = PyDict_New(); PyObject *new_memo = PyDict_New();
if (new_memo == NULL) if (new_memo == NULL)
return NULL; return NULL;
memo = self->pickler->memo; memo = self->pickler->memo;
for (i = 0; i < memo->mt_allocated; ++i) { for (size_t i = 0; i < memo->mt_allocated; ++i) {
PyMemoEntry entry = memo->mt_table[i]; PyMemoEntry entry = memo->mt_table[i];
if (entry.me_key != NULL) { if (entry.me_key != NULL) {
int status; int status;
...@@ -6843,7 +6845,7 @@ static PyObject * ...@@ -6843,7 +6845,7 @@ static PyObject *
_pickle_UnpicklerMemoProxy_copy_impl(UnpicklerMemoProxyObject *self) _pickle_UnpicklerMemoProxy_copy_impl(UnpicklerMemoProxyObject *self)
/*[clinic end generated code: output=e12af7e9bc1e4c77 input=97769247ce032c1d]*/ /*[clinic end generated code: output=e12af7e9bc1e4c77 input=97769247ce032c1d]*/
{ {
Py_ssize_t i; size_t i;
PyObject *new_memo = PyDict_New(); PyObject *new_memo = PyDict_New();
if (new_memo == NULL) if (new_memo == NULL)
return NULL; return NULL;
...@@ -6994,8 +6996,7 @@ static int ...@@ -6994,8 +6996,7 @@ static int
Unpickler_set_memo(UnpicklerObject *self, PyObject *obj) Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
{ {
PyObject **new_memo; PyObject **new_memo;
Py_ssize_t new_memo_size = 0; size_t new_memo_size = 0;
Py_ssize_t i;
if (obj == NULL) { if (obj == NULL) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
...@@ -7012,7 +7013,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj) ...@@ -7012,7 +7013,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
if (new_memo == NULL) if (new_memo == NULL)
return -1; return -1;
for (i = 0; i < new_memo_size; i++) { for (size_t i = 0; i < new_memo_size; i++) {
Py_XINCREF(unpickler->memo[i]); Py_XINCREF(unpickler->memo[i]);
new_memo[i] = unpickler->memo[i]; new_memo[i] = unpickler->memo[i];
} }
...@@ -7060,8 +7061,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj) ...@@ -7060,8 +7061,7 @@ Unpickler_set_memo(UnpicklerObject *self, PyObject *obj)
error: error:
if (new_memo_size) { if (new_memo_size) {
i = new_memo_size; for (size_t i = new_memo_size - 1; i != SIZE_MAX; i--) {
while (--i >= 0) {
Py_XDECREF(new_memo[i]); Py_XDECREF(new_memo[i]);
} }
PyMem_FREE(new_memo); PyMem_FREE(new_memo);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment