mirror of
https://github.com/python/cpython.git
synced 2025-08-31 22:18:28 +00:00
gh-109496: Detect Py_DECREF() after dealloc in debug mode (#109539)
On a Python built in debug mode, Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory: memory filled with 0xDD "dead byte" by the debug hook on memory allocators. The fix is to check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc.
This commit is contained in:
parent
ef659b9616
commit
0bb0d88e2d
4 changed files with 56 additions and 16 deletions
|
@ -660,17 +660,15 @@ static inline void Py_DECREF(PyObject *op) {
|
||||||
#elif defined(Py_REF_DEBUG)
|
#elif defined(Py_REF_DEBUG)
|
||||||
static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
|
static inline void Py_DECREF(const char *filename, int lineno, PyObject *op)
|
||||||
{
|
{
|
||||||
|
if (op->ob_refcnt <= 0) {
|
||||||
|
_Py_NegativeRefcount(filename, lineno, op);
|
||||||
|
}
|
||||||
if (_Py_IsImmortal(op)) {
|
if (_Py_IsImmortal(op)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_Py_DECREF_STAT_INC();
|
_Py_DECREF_STAT_INC();
|
||||||
_Py_DECREF_DecRefTotal();
|
_Py_DECREF_DecRefTotal();
|
||||||
if (--op->ob_refcnt != 0) {
|
if (--op->ob_refcnt == 0) {
|
||||||
if (op->ob_refcnt < 0) {
|
|
||||||
_Py_NegativeRefcount(filename, lineno, op);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_Py_Dealloc(op);
|
_Py_Dealloc(op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,24 +301,40 @@ class CAPITest(unittest.TestCase):
|
||||||
def test_buildvalue_N(self):
|
def test_buildvalue_N(self):
|
||||||
_testcapi.test_buildvalue_N()
|
_testcapi.test_buildvalue_N()
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'),
|
def check_negative_refcount(self, code):
|
||||||
'need _testcapi.negative_refcount')
|
|
||||||
def test_negative_refcount(self):
|
|
||||||
# bpo-35059: Check that Py_DECREF() reports the correct filename
|
# bpo-35059: Check that Py_DECREF() reports the correct filename
|
||||||
# when calling _Py_NegativeRefcount() to abort Python.
|
# when calling _Py_NegativeRefcount() to abort Python.
|
||||||
code = textwrap.dedent("""
|
code = textwrap.dedent(code)
|
||||||
import _testcapi
|
|
||||||
from test import support
|
|
||||||
|
|
||||||
with support.SuppressCrashReport():
|
|
||||||
_testcapi.negative_refcount()
|
|
||||||
""")
|
|
||||||
rc, out, err = assert_python_failure('-c', code)
|
rc, out, err = assert_python_failure('-c', code)
|
||||||
self.assertRegex(err,
|
self.assertRegex(err,
|
||||||
br'_testcapimodule\.c:[0-9]+: '
|
br'_testcapimodule\.c:[0-9]+: '
|
||||||
br'_Py_NegativeRefcount: Assertion failed: '
|
br'_Py_NegativeRefcount: Assertion failed: '
|
||||||
br'object has negative ref count')
|
br'object has negative ref count')
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(_testcapi, 'negative_refcount'),
|
||||||
|
'need _testcapi.negative_refcount()')
|
||||||
|
def test_negative_refcount(self):
|
||||||
|
code = """
|
||||||
|
import _testcapi
|
||||||
|
from test import support
|
||||||
|
|
||||||
|
with support.SuppressCrashReport():
|
||||||
|
_testcapi.negative_refcount()
|
||||||
|
"""
|
||||||
|
self.check_negative_refcount(code)
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(_testcapi, 'decref_freed_object'),
|
||||||
|
'need _testcapi.decref_freed_object()')
|
||||||
|
def test_decref_freed_object(self):
|
||||||
|
code = """
|
||||||
|
import _testcapi
|
||||||
|
from test import support
|
||||||
|
|
||||||
|
with support.SuppressCrashReport():
|
||||||
|
_testcapi.decref_freed_object()
|
||||||
|
"""
|
||||||
|
self.check_negative_refcount(code)
|
||||||
|
|
||||||
def test_trashcan_subclass(self):
|
def test_trashcan_subclass(self):
|
||||||
# bpo-35983: Check that the trashcan mechanism for "list" is NOT
|
# bpo-35983: Check that the trashcan mechanism for "list" is NOT
|
||||||
# activated when its tp_dealloc is being called by a subclass
|
# activated when its tp_dealloc is being called by a subclass
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
On a Python built in debug mode, :c:func:`Py_DECREF()` now calls
|
||||||
|
``_Py_NegativeRefcount()`` if the object is a dangling pointer to
|
||||||
|
deallocated memory: memory filled with ``0xDD`` "dead byte" by the debug
|
||||||
|
hook on memory allocators. The fix is to check the reference count *before*
|
||||||
|
checking for ``_Py_IsImmortal()``. Patch by Victor Stinner.
|
|
@ -2034,6 +2034,26 @@ negative_refcount(PyObject *self, PyObject *Py_UNUSED(args))
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
decref_freed_object(PyObject *self, PyObject *Py_UNUSED(args))
|
||||||
|
{
|
||||||
|
PyObject *obj = PyUnicode_FromString("decref_freed_object");
|
||||||
|
if (obj == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
assert(Py_REFCNT(obj) == 1);
|
||||||
|
|
||||||
|
// Deallocate the memory
|
||||||
|
Py_DECREF(obj);
|
||||||
|
// obj is a now a dangling pointer
|
||||||
|
|
||||||
|
// gh-109496: If Python is built in debug mode, Py_DECREF() must call
|
||||||
|
// _Py_NegativeRefcount() and abort Python.
|
||||||
|
Py_DECREF(obj);
|
||||||
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -3299,6 +3319,7 @@ static PyMethodDef TestMethods[] = {
|
||||||
{"bad_get", _PyCFunction_CAST(bad_get), METH_FASTCALL},
|
{"bad_get", _PyCFunction_CAST(bad_get), METH_FASTCALL},
|
||||||
#ifdef Py_REF_DEBUG
|
#ifdef Py_REF_DEBUG
|
||||||
{"negative_refcount", negative_refcount, METH_NOARGS},
|
{"negative_refcount", negative_refcount, METH_NOARGS},
|
||||||
|
{"decref_freed_object", decref_freed_object, METH_NOARGS},
|
||||||
#endif
|
#endif
|
||||||
{"meth_varargs", meth_varargs, METH_VARARGS},
|
{"meth_varargs", meth_varargs, METH_VARARGS},
|
||||||
{"meth_varargs_keywords", _PyCFunction_CAST(meth_varargs_keywords), METH_VARARGS|METH_KEYWORDS},
|
{"meth_varargs_keywords", _PyCFunction_CAST(meth_varargs_keywords), METH_VARARGS|METH_KEYWORDS},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue