mirror of https://github.com/python/cpython
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.
This commit is contained in:
parent
f604cf1c37
commit
79a3148099
|
@ -90,6 +90,12 @@ extern PyObject* _Py_Offer_Suggestions(PyObject* exception);
|
||||||
PyAPI_FUNC(Py_ssize_t) _Py_UTF8_Edit_Cost(PyObject *str_a, PyObject *str_b,
|
PyAPI_FUNC(Py_ssize_t) _Py_UTF8_Edit_Cost(PyObject *str_a, PyObject *str_b,
|
||||||
Py_ssize_t max_cost);
|
Py_ssize_t max_cost);
|
||||||
|
|
||||||
|
PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
|
||||||
|
const char *func,
|
||||||
|
const char *message);
|
||||||
|
|
||||||
|
#define _Py_FatalRefcountError(message) _Py_FatalRefcountErrorFunc(__func__, message)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Add a deallocator to the bool type to detect refcount bugs in C extensions
|
||||||
|
which call Py_DECREF(Py_True) or Py_DECREF(Py_False) by mistake. Detect also
|
||||||
|
refcount bugs when the empty tuple singleton is destroyed by mistake. Patch
|
||||||
|
by Victor Stinner.
|
|
@ -1,6 +1,7 @@
|
||||||
/* Boolean type, a subtype of int */
|
/* Boolean type, a subtype of int */
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
|
#include "pycore_pyerrors.h" // _Py_FatalRefcountError()
|
||||||
#include "longintrepr.h"
|
#include "longintrepr.h"
|
||||||
|
|
||||||
/* We define bool_repr to return "False" or "True" */
|
/* We define bool_repr to return "False" or "True" */
|
||||||
|
@ -156,8 +157,7 @@ static PyNumberMethods bool_as_number = {
|
||||||
static void _Py_NO_RETURN
|
static void _Py_NO_RETURN
|
||||||
bool_dealloc(PyObject* Py_UNUSED(ignore))
|
bool_dealloc(PyObject* Py_UNUSED(ignore))
|
||||||
{
|
{
|
||||||
Py_FatalError("deallocating True or False likely caused by "
|
_Py_FatalRefcountError("deallocating True or False");
|
||||||
"a refcount bug in a C extension");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The type object for bool. Note that this cannot be subclassed! */
|
/* The type object for bool. Note that this cannot be subclassed! */
|
||||||
|
|
|
@ -1563,8 +1563,7 @@ none_repr(PyObject *op)
|
||||||
static void _Py_NO_RETURN
|
static void _Py_NO_RETURN
|
||||||
none_dealloc(PyObject* Py_UNUSED(ignore))
|
none_dealloc(PyObject* Py_UNUSED(ignore))
|
||||||
{
|
{
|
||||||
Py_FatalError("deallocating None likely caused by a refcount bug "
|
_Py_FatalRefcountError("deallocating None");
|
||||||
"in a C extension");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
|
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
|
||||||
#include "pycore_initconfig.h" // _PyStatus_OK()
|
#include "pycore_initconfig.h" // _PyStatus_OK()
|
||||||
#include "pycore_object.h" // _PyObject_GC_TRACK()
|
#include "pycore_object.h" // _PyObject_GC_TRACK()
|
||||||
|
#include "pycore_pyerrors.h" // _Py_FatalRefcountError()
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
class tuple "PyTupleObject *" "&PyTuple_Type"
|
class tuple "PyTupleObject *" "&PyTuple_Type"
|
||||||
|
@ -287,6 +288,17 @@ tupledealloc(PyTupleObject *op)
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
Py_TYPE(op)->tp_free((PyObject *)op);
|
||||||
|
|
||||||
#if PyTuple_MAXSAVESIZE > 0
|
#if PyTuple_MAXSAVESIZE > 0
|
||||||
|
@ -1048,11 +1060,16 @@ _PyTuple_Fini(PyInterpreterState *interp)
|
||||||
struct _Py_tuple_state *state = &interp->tuple;
|
struct _Py_tuple_state *state = &interp->tuple;
|
||||||
// The empty tuple singleton must not be tracked by the GC
|
// The empty tuple singleton must not be tracked by the GC
|
||||||
assert(!_PyObject_GC_IS_TRACKED(state->free_list[0]));
|
assert(!_PyObject_GC_IS_TRACKED(state->free_list[0]));
|
||||||
|
|
||||||
|
#ifdef Py_DEBUG
|
||||||
|
state->numfree[0] = 0;
|
||||||
|
#endif
|
||||||
Py_CLEAR(state->free_list[0]);
|
Py_CLEAR(state->free_list[0]);
|
||||||
_PyTuple_ClearFreeList(interp);
|
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
state->numfree[0] = -1;
|
state->numfree[0] = -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
_PyTuple_ClearFreeList(interp);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2805,6 +2805,16 @@ _Py_FatalErrorFormat(const char *func, const char *format, ...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _Py_NO_RETURN
|
||||||
|
_Py_FatalRefcountErrorFunc(const char *func, const char *msg)
|
||||||
|
{
|
||||||
|
_Py_FatalErrorFormat(func,
|
||||||
|
"%s: bug likely caused by a refcount error "
|
||||||
|
"in a C extension",
|
||||||
|
msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void _Py_NO_RETURN
|
void _Py_NO_RETURN
|
||||||
Py_ExitStatusException(PyStatus status)
|
Py_ExitStatusException(PyStatus status)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue