157 lines
6.5 KiB
Diff
157 lines
6.5 KiB
Diff
|
|
From 2131750c2bd6ae9d3e294ec14bfc2cbb9e7b497d Mon Sep 17 00:00:00 2001
|
||
|
|
From: Victor Stinner <vstinner@redhat.com>
|
||
|
|
Date: Fri, 7 Jun 2019 16:22:21 +0200
|
||
|
|
Subject: [PATCH] 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.
|
||
|
|
|
||
|
|
Reference:https://github.com/python/cpython/commit/3bf0f3ad2046ac674d8e8a2c074a5a8b3327797d
|
||
|
|
Conflict:NA
|
||
|
|
|
||
|
|
---
|
||
|
|
Lib/test/test_capi.py | 29 ++++++++++---------
|
||
|
|
.../2019-06-07-12-23-15.bpo-37169.yfXTFg.rst | 1 +
|
||
|
|
Modules/_testcapimodule.c | 27 ++++++++---------
|
||
|
|
3 files changed, 30 insertions(+), 27 deletions(-)
|
||
|
|
create mode 100644 Misc/NEWS.d/next/Tests/2019-06-07-12-23-15.bpo-37169.yfXTFg.rst
|
||
|
|
|
||
|
|
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
|
||
|
|
index ebcb692..097bbde 100644
|
||
|
|
--- a/Lib/test/test_capi.py
|
||
|
|
+++ b/Lib/test/test_capi.py
|
||
|
|
@@ -522,28 +522,29 @@ class PyMemDebugTests(unittest.TestCase):
|
||
|
|
code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()'
|
||
|
|
self.check_malloc_without_gil(code)
|
||
|
|
|
||
|
|
- def check_pyobject_is_freed(self, func):
|
||
|
|
- code = textwrap.dedent('''
|
||
|
|
+ def check_pyobject_is_freed(self, func_name):
|
||
|
|
+ code = textwrap.dedent(f'''
|
||
|
|
import gc, os, sys, _testcapi
|
||
|
|
# Disable the GC to avoid crash on GC collection
|
||
|
|
gc.disable()
|
||
|
|
- obj = _testcapi.{func}()
|
||
|
|
- error = (_testcapi.pyobject_is_freed(obj) == False)
|
||
|
|
- # Exit immediately to avoid a crash while deallocating
|
||
|
|
- # the invalid object
|
||
|
|
- os._exit(int(error))
|
||
|
|
+ try:
|
||
|
|
+ _testcapi.{func_name}()
|
||
|
|
+ # Exit immediately to avoid a crash while deallocating
|
||
|
|
+ # the invalid object
|
||
|
|
+ os._exit(0)
|
||
|
|
+ except _testcapi.error:
|
||
|
|
+ os._exit(1)
|
||
|
|
''')
|
||
|
|
- code = code.format(func=func)
|
||
|
|
assert_python_ok('-c', code, PYTHONMALLOC=self.PYTHONMALLOC)
|
||
|
|
|
||
|
|
- def test_pyobject_is_freed_uninitialized(self):
|
||
|
|
- self.check_pyobject_is_freed('pyobject_uninitialized')
|
||
|
|
+ def test_pyobject_uninitialized_is_freed(self):
|
||
|
|
+ self.check_pyobject_is_freed('check_pyobject_uninitialized_is_freed')
|
||
|
|
|
||
|
|
- def test_pyobject_is_freed_forbidden_bytes(self):
|
||
|
|
- self.check_pyobject_is_freed('pyobject_forbidden_bytes')
|
||
|
|
+ def test_pyobject_forbidden_bytes_is_freed(self):
|
||
|
|
+ self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed')
|
||
|
|
|
||
|
|
- def test_pyobject_is_freed_free(self):
|
||
|
|
- self.check_pyobject_is_freed('pyobject_freed')
|
||
|
|
+ def test_pyobject_freed_is_freed(self):
|
||
|
|
+ self.check_pyobject_is_freed('check_pyobject_freed_is_freed')
|
||
|
|
|
||
|
|
|
||
|
|
class PyMemMallocDebugTests(PyMemDebugTests):
|
||
|
|
diff --git a/Misc/NEWS.d/next/Tests/2019-06-07-12-23-15.bpo-37169.yfXTFg.rst b/Misc/NEWS.d/next/Tests/2019-06-07-12-23-15.bpo-37169.yfXTFg.rst
|
||
|
|
new file mode 100644
|
||
|
|
index 0000000..f2f0a8b
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/Misc/NEWS.d/next/Tests/2019-06-07-12-23-15.bpo-37169.yfXTFg.rst
|
||
|
|
@@ -0,0 +1 @@
|
||
|
|
+Rewrite ``_PyObject_IsFreed()`` unit tests.
|
||
|
|
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
|
||
|
|
index 6996d22..55cfdb8 100644
|
||
|
|
--- a/Modules/_testcapimodule.c
|
||
|
|
+++ b/Modules/_testcapimodule.c
|
||
|
|
@@ -4248,15 +4248,17 @@ test_pymem_getallocatorsname(PyObject *self, PyObject *args)
|
||
|
|
|
||
|
|
|
||
|
|
static PyObject*
|
||
|
|
-pyobject_is_freed(PyObject *self, PyObject *op)
|
||
|
|
+test_pyobject_is_freed(const char *test_name, PyObject *op)
|
||
|
|
{
|
||
|
|
- int res = _PyObject_IsFreed(op);
|
||
|
|
- return PyBool_FromLong(res);
|
||
|
|
+ if (!_PyObject_IsFreed(op)) {
|
||
|
|
+ return raiseTestError(test_name, "object is not seen as freed");
|
||
|
|
+ }
|
||
|
|
+ Py_RETURN_NONE;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
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));
|
||
|
|
if (op == NULL) {
|
||
|
|
@@ -4265,12 +4267,12 @@ pyobject_uninitialized(PyObject *self, PyObject *args)
|
||
|
|
/* Initialize reference count to avoid early crash in ceval or GC */
|
||
|
|
Py_REFCNT(op) = 1;
|
||
|
|
/* object fields like ob_type are uninitialized! */
|
||
|
|
- return op;
|
||
|
|
+ return test_pyobject_is_freed("check_pyobject_uninitialized_is_freed", op);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
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 */
|
||
|
|
PyObject *op = (PyObject *)PyObject_Malloc(offsetof(PyObject, ob_type));
|
||
|
|
@@ -4281,12 +4283,12 @@ pyobject_forbidden_bytes(PyObject *self, PyObject *args)
|
||
|
|
Py_REFCNT(op) = 1;
|
||
|
|
/* ob_type field is after the memory block: part of "forbidden bytes"
|
||
|
|
when using debug hooks on memory allocatrs! */
|
||
|
|
- return op;
|
||
|
|
+ return test_pyobject_is_freed("check_pyobject_forbidden_bytes_is_freed", op);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
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);
|
||
|
|
if (op == NULL) {
|
||
|
|
@@ -4296,7 +4298,7 @@ pyobject_freed(PyObject *self, PyObject *args)
|
||
|
|
/* Reset reference count to avoid early crash in ceval or GC */
|
||
|
|
Py_REFCNT(op) = 1;
|
||
|
|
/* object memory is freed! */
|
||
|
|
- return op;
|
||
|
|
+ return test_pyobject_is_freed("check_pyobject_freed_is_freed", op);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
@@ -4876,10 +4878,9 @@ static PyMethodDef TestMethods[] = {
|
||
|
|
{"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
|
||
|
|
{"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS},
|
||
|
|
{"pymem_getallocatorsname", test_pymem_getallocatorsname, METH_NOARGS},
|
||
|
|
- {"pyobject_is_freed", (PyCFunction)(void(*)(void))pyobject_is_freed, METH_O},
|
||
|
|
- {"pyobject_uninitialized", pyobject_uninitialized, METH_NOARGS},
|
||
|
|
- {"pyobject_forbidden_bytes", pyobject_forbidden_bytes, METH_NOARGS},
|
||
|
|
- {"pyobject_freed", pyobject_freed, METH_NOARGS},
|
||
|
|
+ {"check_pyobject_uninitialized_is_freed", check_pyobject_uninitialized_is_freed, METH_NOARGS},
|
||
|
|
+ {"check_pyobject_forbidden_bytes_is_freed", check_pyobject_forbidden_bytes_is_freed, METH_NOARGS},
|
||
|
|
+ {"check_pyobject_freed_is_freed", check_pyobject_freed_is_freed, METH_NOARGS},
|
||
|
|
{"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS},
|
||
|
|
{"tracemalloc_track", tracemalloc_track, METH_VARARGS},
|
||
|
|
{"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS},
|
||
|
|
--
|
||
|
|
2.27.0
|
||
|
|
|