From bcb198385dee469d630a184182df9dc1463e2c47 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 8 Jun 2020 02:14:47 +0200 Subject: [PATCH] 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()). --- Objects/floatobject.c | 19 ++++++++++++++++--- Objects/frameobject.c | 12 ++++++++++++ Objects/genobject.c | 21 +++++++++++++++++++++ Objects/listobject.c | 12 ++++++++++++ Objects/tupleobject.c | 15 +++++++++++++++ Python/context.c | 12 ++++++++++++ 6 files changed, 88 insertions(+), 3 deletions(-) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index d72fd21f95f..65625fe88ca 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -116,6 +116,10 @@ PyFloat_FromDouble(double fval) struct _Py_float_state *state = &interp->float_state; PyFloatObject *op = state->free_list; if (op != NULL) { +#ifdef Py_DEBUG + // PyFloat_FromDouble() must not be called after _PyFloat_Fini() + assert(state->numfree != -1); +#endif state->free_list = (PyFloatObject *) Py_TYPE(op); state->numfree--; } @@ -219,6 +223,10 @@ float_dealloc(PyFloatObject *op) if (PyFloat_CheckExact(op)) { PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_float_state *state = &interp->float_state; +#ifdef Py_DEBUG + // float_dealloc() must not be called after _PyFloat_Fini() + assert(state->numfree != -1); +#endif if (state->numfree >= PyFloat_MAXFREELIST) { PyObject_FREE(op); return; @@ -1984,10 +1992,11 @@ void _PyFloat_ClearFreeList(PyThreadState *tstate) { struct _Py_float_state *state = &tstate->interp->float_state; - PyFloatObject *f = state->free_list, *next; - for (; f; f = next) { - next = (PyFloatObject*) Py_TYPE(f); + PyFloatObject *f = state->free_list; + while (f != NULL) { + PyFloatObject *next = (PyFloatObject*) Py_TYPE(f); PyObject_FREE(f); + f = next; } state->free_list = NULL; state->numfree = 0; @@ -1997,6 +2006,10 @@ void _PyFloat_Fini(PyThreadState *tstate) { _PyFloat_ClearFreeList(tstate); +#ifdef Py_DEBUG + struct _Py_float_state *state = &tstate->interp->float_state; + state->numfree = -1; +#endif } /* Print summary info about the state of the optimized allocator */ diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 0fe9f2a6666..0dad42ee7bf 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -595,6 +595,10 @@ frame_dealloc(PyFrameObject *f) else { PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_frame_state *state = &interp->frame; +#ifdef Py_DEBUG + // frame_dealloc() must not be called after _PyFrame_Fini() + assert(state->numfree != -1); +#endif if (state->numfree < PyFrame_MAXFREELIST) { ++state->numfree; f->f_back = state->free_list; @@ -790,6 +794,10 @@ frame_alloc(PyCodeObject *code) } } else { +#ifdef Py_DEBUG + // frame_alloc() must not be called after _PyFrame_Fini() + assert(state->numfree != -1); +#endif assert(state->numfree > 0); --state->numfree; f = state->free_list; @@ -1188,6 +1196,10 @@ void _PyFrame_Fini(PyThreadState *tstate) { _PyFrame_ClearFreeList(tstate); +#ifdef Py_DEBUG + struct _Py_frame_state *state = &tstate->interp->frame; + state->numfree = -1; +#endif } /* Print summary info about the state of the optimized allocator */ diff --git a/Objects/genobject.c b/Objects/genobject.c index f7dbfd74864..4207d5326cc 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1430,6 +1430,11 @@ void _PyAsyncGen_Fini(PyThreadState *tstate) { _PyAsyncGen_ClearFreeLists(tstate); +#ifdef Py_DEBUG + struct _Py_async_gen_state *state = &tstate->interp->async_gen; + state->value_numfree = -1; + state->asend_numfree = -1; +#endif } @@ -1474,6 +1479,10 @@ async_gen_asend_dealloc(PyAsyncGenASend *o) Py_CLEAR(o->ags_sendval); PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_async_gen_state *state = &interp->async_gen; +#ifdef Py_DEBUG + // async_gen_asend_dealloc() must not be called after _PyAsyncGen_Fini() + assert(state->asend_numfree != -1); +#endif if (state->asend_numfree < _PyAsyncGen_MAXFREELIST) { assert(PyAsyncGenASend_CheckExact(o)); state->asend_freelist[state->asend_numfree++] = o; @@ -1632,6 +1641,10 @@ async_gen_asend_new(PyAsyncGenObject *gen, PyObject *sendval) PyAsyncGenASend *o; PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_async_gen_state *state = &interp->async_gen; +#ifdef Py_DEBUG + // async_gen_asend_new() must not be called after _PyAsyncGen_Fini() + assert(state->asend_numfree != -1); +#endif if (state->asend_numfree) { state->asend_numfree--; o = state->asend_freelist[state->asend_numfree]; @@ -1667,6 +1680,10 @@ async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o) Py_CLEAR(o->agw_val); PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_async_gen_state *state = &interp->async_gen; +#ifdef Py_DEBUG + // async_gen_wrapped_val_dealloc() must not be called after _PyAsyncGen_Fini() + assert(state->value_numfree != -1); +#endif if (state->value_numfree < _PyAsyncGen_MAXFREELIST) { assert(_PyAsyncGenWrappedValue_CheckExact(o)); state->value_freelist[state->value_numfree++] = o; @@ -1737,6 +1754,10 @@ _PyAsyncGenValueWrapperNew(PyObject *val) PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_async_gen_state *state = &interp->async_gen; +#ifdef Py_DEBUG + // _PyAsyncGenValueWrapperNew() must not be called after _PyAsyncGen_Fini() + assert(state->value_numfree != -1); +#endif if (state->value_numfree) { state->value_numfree--; o = state->value_freelist[state->value_numfree]; diff --git a/Objects/listobject.c b/Objects/listobject.c index 043256d8adb..22cdbe3cfdd 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -111,6 +111,10 @@ void _PyList_Fini(PyThreadState *tstate) { _PyList_ClearFreeList(tstate); +#ifdef Py_DEBUG + struct _Py_list_state *state = &tstate->interp->list; + state->numfree = -1; +#endif } /* Print summary info about the state of the optimized allocator */ @@ -135,6 +139,10 @@ PyList_New(Py_ssize_t size) PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_list_state *state = &interp->list; PyListObject *op; +#ifdef Py_DEBUG + // PyList_New() must not be called after _PyList_Fini() + assert(state->numfree != -1); +#endif if (state->numfree) { state->numfree--; op = state->free_list[state->numfree]; @@ -330,6 +338,10 @@ list_dealloc(PyListObject *op) } PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_list_state *state = &interp->list; +#ifdef Py_DEBUG + // list_dealloc() must not be called after _PyList_Fini() + assert(state->numfree != -1); +#endif if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) { state->free_list[state->numfree++] = op; } diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 951cd1faf7e..8bfa0894a79 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -54,6 +54,10 @@ tuple_alloc(struct _Py_tuple_state *state, Py_ssize_t size) 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 @@ PyTuple_New(Py_ssize_t size) } #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 @@ tupledealloc(PyTupleObject *op) #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 @@ _PyTuple_Fini(PyThreadState *tstate) Py_CLEAR(state->free_list[0]); _PyTuple_ClearFreeList(tstate); +#ifdef Py_DEBUG + state->numfree[0] = -1; +#endif #endif } diff --git a/Python/context.c b/Python/context.c index 3cf8db4c90c..dedbca99384 100644 --- a/Python/context.c +++ b/Python/context.c @@ -335,6 +335,10 @@ _context_alloc(void) PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_context_state *state = &interp->context; PyContext *ctx; +#ifdef Py_DEBUG + // _context_alloc() must not be called after _PyContext_Fini() + assert(state->numfree != -1); +#endif if (state->numfree) { state->numfree--; ctx = state->freelist; @@ -460,6 +464,10 @@ context_tp_dealloc(PyContext *self) PyInterpreterState *interp = _PyInterpreterState_GET(); struct _Py_context_state *state = &interp->context; +#ifdef Py_DEBUG + // _context_alloc() must not be called after _PyContext_Fini() + assert(state->numfree != -1); +#endif if (state->numfree < CONTEXT_FREELIST_MAXLEN) { state->numfree++; self->ctx_weakreflist = (PyObject *)state->freelist; @@ -1290,6 +1298,10 @@ _PyContext_Fini(PyThreadState *tstate) { Py_CLEAR(_token_missing); _PyContext_ClearFreeList(tstate); +#ifdef Py_DEBUG + struct _Py_context_state *state = &tstate->interp->context; + state->numfree = -1; +#endif _PyHamt_Fini(); }