mirror of
https://github.com/python/cpython.git
synced 2026-10-28 18:35:21 +08:00
gh-159739: Fully clear the unpickler memo (#160173)
Clearing an Unpickler memo left its sequential index and sparse entries intact, so reusing the unpickler for an independent record could raise UnpicklingError or retrieve an object from a previous record. Replace the memo and reset all of its state before releasing the old entries. Backport to 3.15; manually backport only the counter reset to 3.14, which has no sparse memo.
This commit is contained in:
1 parent
0b8bb7154c
commit
cc36604f7b
3 files changed
+33
-9
No files matched your search
@@ -5299,6 +5299,22 @@ class AbstractPicklerUnpicklerObjectTests:
|
||||
self.assertNotEqual(first_pickled, second_pickled)
|
||||
self.assertEqual(first_pickled, third_pickled)
|
||||
|
||||
def test_clear_unpickler_memo(self):
|
||||
value = [b'repeated'] * 2
|
||||
data = pickle.dumps(value, protocol=4)
|
||||
unpickler = self.unpickler_class(io.BytesIO(data * 2))
|
||||
self.assertEqual(unpickler.load(), value)
|
||||
unpickler.memo.clear()
|
||||
self.assertEqual(unpickler.load(), value)
|
||||
|
||||
def test_clear_unpickler_memo_sparse(self):
|
||||
# Store None at index 1000, then retrieve it in a second record.
|
||||
unpickler = self.unpickler_class(io.BytesIO(b'Np1000\n.g1000\n.'))
|
||||
self.assertIsNone(unpickler.load())
|
||||
unpickler.memo.clear()
|
||||
with self.assertRaises(pickle.UnpicklingError):
|
||||
unpickler.load()
|
||||
|
||||
def test_priming_pickler_memo(self):
|
||||
# Verify that we can set the Pickler's memo attribute.
|
||||
data = ["abcdefg", "abcdefg", 44]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Fix clearing a :class:`pickle.Unpickler` memo so that the unpickler can be
|
||||
reused for independent records.
|
||||
+15
-9
@@ -1762,16 +1762,16 @@ _Unpickler_NewMemo(Py_ssize_t new_size)
|
||||
return memo;
|
||||
}
|
||||
|
||||
/* Free the unpickler's memo, taking care to decref any items left in it. */
|
||||
/* Replace the unpickler's memo and release the old entries. */
|
||||
static void
|
||||
_Unpickler_MemoCleanup(UnpicklerObject *self)
|
||||
_Unpickler_MemoCleanup(UnpicklerObject *self, PyObject **replacement)
|
||||
{
|
||||
Py_ssize_t i;
|
||||
PyObject **memo = self->memo;
|
||||
|
||||
if (self->memo == NULL)
|
||||
self->memo = replacement;
|
||||
if (memo == NULL)
|
||||
return;
|
||||
self->memo = NULL;
|
||||
i = self->memo_size;
|
||||
while (--i >= 0) {
|
||||
Py_XDECREF(memo[i]);
|
||||
@@ -7769,7 +7769,7 @@ Unpickler_clear(PyObject *op)
|
||||
}
|
||||
|
||||
Py_CLEAR(self->memo_dict);
|
||||
_Unpickler_MemoCleanup(self);
|
||||
_Unpickler_MemoCleanup(self, NULL);
|
||||
PyMem_Free(self->marks);
|
||||
self->marks = NULL;
|
||||
PyMem_Free(self->input_line);
|
||||
@@ -7916,10 +7916,16 @@ static PyObject *
|
||||
_pickle_UnpicklerMemoProxy_clear_impl(UnpicklerMemoProxyObject *self)
|
||||
/*[clinic end generated code: output=d20cd43f4ba1fb1f input=b1df7c52e7afd9bd]*/
|
||||
{
|
||||
_Unpickler_MemoCleanup(self->unpickler);
|
||||
self->unpickler->memo = _Unpickler_NewMemo(self->unpickler->memo_size);
|
||||
if (self->unpickler->memo == NULL)
|
||||
UnpicklerObject *unpickler = self->unpickler;
|
||||
PyObject **memo = _Unpickler_NewMemo(unpickler->memo_size);
|
||||
if (memo == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyObject *memo_dict = unpickler->memo_dict;
|
||||
unpickler->memo_dict = NULL;
|
||||
unpickler->memo_len = 0;
|
||||
_Unpickler_MemoCleanup(unpickler, memo);
|
||||
Py_XDECREF(memo_dict);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@@ -8124,7 +8130,7 @@ Unpickler_set_memo(PyObject *op, PyObject *obj, void *Py_UNUSED(closure))
|
||||
return -1;
|
||||
}
|
||||
|
||||
_Unpickler_MemoCleanup(self);
|
||||
_Unpickler_MemoCleanup(self, NULL);
|
||||
self->memo_size = new_memo_size;
|
||||
self->memo = new_memo;
|
||||
|
||||
|
||||
Reference in new issue
Block a user