mirror of
https://github.com/python/cpython.git
synced 2025-12-04 00:30:19 +00:00
closes bpo-34656: Avoid relying on signed overflow in _pickle memos. (GH-9261)
This commit is contained in:
parent
f14c28f397
commit
a4ae828ee4
1 changed files with 31 additions and 31 deletions
|
|
@ -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 {
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
|
||||||
/* 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)
|
||||||
* 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)
|
||||||
/* 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)
|
||||||
|
|
||||||
/* 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)
|
||||||
/* 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 *
|
||||||
_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 *
|
||||||
_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
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue