bpo-40887: Don't use finalized free lists (GH-20700)
In debug mode, ensure that free lists are no longer used after being
finalized. Set numfree to -1 in finalization functions
(eg. _PyList_Fini()), and then check that numfree is not equal to -1
before using a free list (e.g list_dealloc()).
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 951cd1f..8bfa089 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -54,6 +54,10 @@
return NULL;
}
#if PyTuple_MAXSAVESIZE > 0
+#ifdef Py_DEBUG
+ // tuple_alloc() must not be called after _PyTuple_Fini()
+ assert(state->numfree[0] != -1);
+#endif
if (size < PyTuple_MAXSAVESIZE && (op = state->free_list[size]) != NULL) {
assert(size != 0);
state->free_list[size] = (PyTupleObject *) op->ob_item[0];
@@ -102,6 +106,10 @@
}
#if PyTuple_MAXSAVESIZE > 0
if (size == 0) {
+#ifdef Py_DEBUG
+ // PyTuple_New() must not be called after _PyTuple_Fini()
+ assert(state->numfree[0] != -1);
+#endif
state->free_list[0] = op;
++state->numfree[0];
Py_INCREF(op); /* extra INCREF so that this is never freed */
@@ -227,6 +235,10 @@
#if PyTuple_MAXSAVESIZE > 0
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _Py_tuple_state *state = &interp->tuple;
+#ifdef Py_DEBUG
+ // tupledealloc() must not be called after _PyTuple_Fini()
+ assert(state->numfree[0] != -1);
+#endif
if (len < PyTuple_MAXSAVESIZE &&
state->numfree[len] < PyTuple_MAXFREELIST &&
Py_IS_TYPE(op, &PyTuple_Type))
@@ -984,6 +996,9 @@
Py_CLEAR(state->free_list[0]);
_PyTuple_ClearFreeList(tstate);
+#ifdef Py_DEBUG
+ state->numfree[0] = -1;
+#endif
#endif
}