Skip to content
Prev Previous commit
Next Next commit
INCREF before save() in batch_list_exact
  • Loading branch information
sweeneyde committed Jun 10, 2022
commit a2a8e35118ca472a12b2b68dcc51fce5f200d920
16 changes: 16 additions & 0 deletions Lib/test/pickletester.py
Original file line number Diff line number Diff line change
Expand Up @@ -3064,6 +3064,22 @@ def __reduce__(self):
expected = "changed size during iteration"
self.assertIn(expected, str(e))

def test_evil_class_mutating_list(self):
if not hasattr(self, "pickler"):
raise self.skipTest(f"{type(self)} has no associated pickler type")

global P
class P(self.pickler):
def persistent_id(self, obj):
if obj is a[0]:
a.clear()
return None

for proto in protocols:
a = [[[[]]]]
with self.assertRaises(IndexError):
P(io.BytesIO(), proto).dump(a)


class BigmemPickleTests:

Expand Down
10 changes: 8 additions & 2 deletions Modules/_pickle.c
Original file line number Diff line number Diff line change
Expand Up @@ -3006,7 +3006,10 @@ batch_list_exact(PicklerObject *self, PyObject *obj)

if (PyList_GET_SIZE(obj) == 1) {
item = PyList_GET_ITEM(obj, 0);
if (save(self, item, 0) < 0)
Py_INCREF(item);
int err = save(self, item, 0);
Py_DECREF(item);
if (err < 0)
return -1;
if (_Pickler_Write(self, &append_op, 1) < 0)
return -1;
Expand All @@ -3021,7 +3024,10 @@ batch_list_exact(PicklerObject *self, PyObject *obj)
return -1;
while (total < PyList_GET_SIZE(obj)) {
item = PyList_GET_ITEM(obj, total);
if (save(self, item, 0) < 0)
Py_INCREF(item);
int err = save(self, item, 0);
Py_DECREF(item);
if (err < 0)
return -1;
total++;
if (++this_batch == BATCHSIZE)
Expand Down