diff options
author | Victor Stinner <vstinner@python.org> | 2021-09-21 21:04:34 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-21 21:04:34 (GMT) |
commit | 79a31480992c3fa5890fc7a6c5d9af6d337d5844 (patch) | |
tree | a3ef89bb0340669e86c52f5b924cfb0e71845956 /Objects | |
parent | f604cf1c377a7648e0686044e6e49900bfc9feef (diff) | |
download | cpython-79a31480992c3fa5890fc7a6c5d9af6d337d5844.zip cpython-79a31480992c3fa5890fc7a6c5d9af6d337d5844.tar.gz cpython-79a31480992c3fa5890fc7a6c5d9af6d337d5844.tar.bz2 |
bpo-45061: Detect refcount bug on empty tuple singleton (GH-28503)
Detect refcount bugs in C extensions when the empty tuple singleton
is destroyed by mistake.
Add the _Py_FatalRefcountErrorFunc() function.
Diffstat (limited to 'Objects')
-rw-r--r-- | Objects/boolobject.c | 4 | ||||
-rw-r--r-- | Objects/object.c | 3 | ||||
-rw-r--r-- | Objects/tupleobject.c | 19 |
3 files changed, 21 insertions, 5 deletions
diff --git a/Objects/boolobject.c b/Objects/boolobject.c index bc1666f..c72243a 100644 --- a/Objects/boolobject.c +++ b/Objects/boolobject.c @@ -1,6 +1,7 @@ /* Boolean type, a subtype of int */ #include "Python.h" +#include "pycore_pyerrors.h" // _Py_FatalRefcountError() #include "longintrepr.h" /* We define bool_repr to return "False" or "True" */ @@ -156,8 +157,7 @@ static PyNumberMethods bool_as_number = { static void _Py_NO_RETURN bool_dealloc(PyObject* Py_UNUSED(ignore)) { - Py_FatalError("deallocating True or False likely caused by " - "a refcount bug in a C extension"); + _Py_FatalRefcountError("deallocating True or False"); } /* The type object for bool. Note that this cannot be subclassed! */ diff --git a/Objects/object.c b/Objects/object.c index 026262b..aa84815 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1563,8 +1563,7 @@ none_repr(PyObject *op) static void _Py_NO_RETURN none_dealloc(PyObject* Py_UNUSED(ignore)) { - Py_FatalError("deallocating None likely caused by a refcount bug " - "in a C extension"); + _Py_FatalRefcountError("deallocating None"); } static PyObject * diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index 3e3aea4..e64b93b 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -6,6 +6,7 @@ #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_object.h" // _PyObject_GC_TRACK() +#include "pycore_pyerrors.h" // _Py_FatalRefcountError() /*[clinic input] class tuple "PyTupleObject *" "&PyTuple_Type" @@ -287,6 +288,17 @@ tupledealloc(PyTupleObject *op) } #endif } +#if defined(Py_DEBUG) && PyTuple_MAXSAVESIZE > 0 + else { + assert(len == 0); + struct _Py_tuple_state *state = get_tuple_state(); + // The empty tuple singleton must only be deallocated by + // _PyTuple_Fini(): not before, not after + if (op == state->free_list[0] && state->numfree[0] != 0) { + _Py_FatalRefcountError("deallocating the empty tuple singleton"); + } + } +#endif Py_TYPE(op)->tp_free((PyObject *)op); #if PyTuple_MAXSAVESIZE > 0 @@ -1048,11 +1060,16 @@ _PyTuple_Fini(PyInterpreterState *interp) struct _Py_tuple_state *state = &interp->tuple; // The empty tuple singleton must not be tracked by the GC assert(!_PyObject_GC_IS_TRACKED(state->free_list[0])); + +#ifdef Py_DEBUG + state->numfree[0] = 0; +#endif Py_CLEAR(state->free_list[0]); - _PyTuple_ClearFreeList(interp); #ifdef Py_DEBUG state->numfree[0] = -1; #endif + + _PyTuple_ClearFreeList(interp); #endif } |