Commit 3bf0f3ad authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

bpo-37169: Rewrite _PyObject_IsFreed() unit tests (GH-13888)

Replace two Python function calls with a single one to ensure that no
memory allocation is done between the invalid object is created and
when _PyObject_IsFreed() is called.
parent 3f345c39
...@@ -705,28 +705,29 @@ class PyMemDebugTests(unittest.TestCase): ...@@ -705,28 +705,29 @@ class PyMemDebugTests(unittest.TestCase):
code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()' code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()'
self.check_malloc_without_gil(code) self.check_malloc_without_gil(code)
def check_pyobject_is_freed(self, func): def check_pyobject_is_freed(self, func_name):
code = textwrap.dedent(''' code = textwrap.dedent(f'''
import gc, os, sys, _testcapi import gc, os, sys, _testcapi
# Disable the GC to avoid crash on GC collection # Disable the GC to avoid crash on GC collection
gc.disable() gc.disable()
obj = _testcapi.{func}() try:
error = (_testcapi.pyobject_is_freed(obj) == False) _testcapi.{func_name}()
# Exit immediately to avoid a crash while deallocating # Exit immediately to avoid a crash while deallocating
# the invalid object # the invalid object
os._exit(int(error)) os._exit(0)
except _testcapi.error:
os._exit(1)
''') ''')
code = code.format(func=func)
assert_python_ok('-c', code, PYTHONMALLOC=self.PYTHONMALLOC) assert_python_ok('-c', code, PYTHONMALLOC=self.PYTHONMALLOC)
def test_pyobject_is_freed_uninitialized(self): def test_pyobject_uninitialized_is_freed(self):
self.check_pyobject_is_freed('pyobject_uninitialized') self.check_pyobject_is_freed('check_pyobject_uninitialized_is_freed')
def test_pyobject_is_freed_forbidden_bytes(self): def test_pyobject_forbidden_bytes_is_freed(self):
self.check_pyobject_is_freed('pyobject_forbidden_bytes') self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed')
def test_pyobject_is_freed_free(self): def test_pyobject_freed_is_freed(self):
self.check_pyobject_is_freed('pyobject_freed') self.check_pyobject_is_freed('check_pyobject_freed_is_freed')
class PyMemMallocDebugTests(PyMemDebugTests): class PyMemMallocDebugTests(PyMemDebugTests):
......
...@@ -4489,15 +4489,17 @@ test_pymem_getallocatorsname(PyObject *self, PyObject *args) ...@@ -4489,15 +4489,17 @@ test_pymem_getallocatorsname(PyObject *self, PyObject *args)
static PyObject* static PyObject*
pyobject_is_freed(PyObject *self, PyObject *op) test_pyobject_is_freed(const char *test_name, PyObject *op)
{ {
int res = _PyObject_IsFreed(op); if (!_PyObject_IsFreed(op)) {
return PyBool_FromLong(res); return raiseTestError(test_name, "object is not seen as freed");
}
Py_RETURN_NONE;
} }
static PyObject* static PyObject*
pyobject_uninitialized(PyObject *self, PyObject *args) check_pyobject_uninitialized_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
{ {
PyObject *op = (PyObject *)PyObject_Malloc(sizeof(PyObject)); PyObject *op = (PyObject *)PyObject_Malloc(sizeof(PyObject));
if (op == NULL) { if (op == NULL) {
...@@ -4506,12 +4508,12 @@ pyobject_uninitialized(PyObject *self, PyObject *args) ...@@ -4506,12 +4508,12 @@ pyobject_uninitialized(PyObject *self, PyObject *args)
/* Initialize reference count to avoid early crash in ceval or GC */ /* Initialize reference count to avoid early crash in ceval or GC */
Py_REFCNT(op) = 1; Py_REFCNT(op) = 1;
/* object fields like ob_type are uninitialized! */ /* object fields like ob_type are uninitialized! */
return op; return test_pyobject_is_freed("check_pyobject_uninitialized_is_freed", op);
} }
static PyObject* static PyObject*
pyobject_forbidden_bytes(PyObject *self, PyObject *args) check_pyobject_forbidden_bytes_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
{ {
/* Allocate an incomplete PyObject structure: truncate 'ob_type' field */ /* Allocate an incomplete PyObject structure: truncate 'ob_type' field */
PyObject *op = (PyObject *)PyObject_Malloc(offsetof(PyObject, ob_type)); PyObject *op = (PyObject *)PyObject_Malloc(offsetof(PyObject, ob_type));
...@@ -4522,12 +4524,12 @@ pyobject_forbidden_bytes(PyObject *self, PyObject *args) ...@@ -4522,12 +4524,12 @@ pyobject_forbidden_bytes(PyObject *self, PyObject *args)
Py_REFCNT(op) = 1; Py_REFCNT(op) = 1;
/* ob_type field is after the memory block: part of "forbidden bytes" /* ob_type field is after the memory block: part of "forbidden bytes"
when using debug hooks on memory allocatrs! */ when using debug hooks on memory allocatrs! */
return op; return test_pyobject_is_freed("check_pyobject_forbidden_bytes_is_freed", op);
} }
static PyObject* static PyObject*
pyobject_freed(PyObject *self, PyObject *args) check_pyobject_freed_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
{ {
PyObject *op = _PyObject_CallNoArg((PyObject *)&PyBaseObject_Type); PyObject *op = _PyObject_CallNoArg((PyObject *)&PyBaseObject_Type);
if (op == NULL) { if (op == NULL) {
...@@ -4537,7 +4539,7 @@ pyobject_freed(PyObject *self, PyObject *args) ...@@ -4537,7 +4539,7 @@ pyobject_freed(PyObject *self, PyObject *args)
/* Reset reference count to avoid early crash in ceval or GC */ /* Reset reference count to avoid early crash in ceval or GC */
Py_REFCNT(op) = 1; Py_REFCNT(op) = 1;
/* object memory is freed! */ /* object memory is freed! */
return op; return test_pyobject_is_freed("check_pyobject_freed_is_freed", op);
} }
...@@ -5264,10 +5266,9 @@ static PyMethodDef TestMethods[] = { ...@@ -5264,10 +5266,9 @@ static PyMethodDef TestMethods[] = {
{"pymem_api_misuse", pymem_api_misuse, METH_NOARGS}, {"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
{"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS}, {"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS},
{"pymem_getallocatorsname", test_pymem_getallocatorsname, METH_NOARGS}, {"pymem_getallocatorsname", test_pymem_getallocatorsname, METH_NOARGS},
{"pyobject_is_freed", (PyCFunction)(void(*)(void))pyobject_is_freed, METH_O}, {"check_pyobject_uninitialized_is_freed", check_pyobject_uninitialized_is_freed, METH_NOARGS},
{"pyobject_uninitialized", pyobject_uninitialized, METH_NOARGS}, {"check_pyobject_forbidden_bytes_is_freed", check_pyobject_forbidden_bytes_is_freed, METH_NOARGS},
{"pyobject_forbidden_bytes", pyobject_forbidden_bytes, METH_NOARGS}, {"check_pyobject_freed_is_freed", check_pyobject_freed_is_freed, METH_NOARGS},
{"pyobject_freed", pyobject_freed, METH_NOARGS},
{"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS}, {"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS},
{"tracemalloc_track", tracemalloc_track, METH_VARARGS}, {"tracemalloc_track", tracemalloc_track, METH_VARARGS},
{"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS}, {"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment