Skip to content

Commit 3bf0f3a

Browse files
authored
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.
1 parent 3f345c3 commit 3bf0f3a

File tree

3 files changed

+30
-27
lines changed

3 files changed

+30
-27
lines changed

Lib/test/test_capi.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -705,28 +705,29 @@ def test_pyobject_malloc_without_gil(self):
705705
code = 'import _testcapi; _testcapi.pyobject_malloc_without_gil()'
706706
self.check_malloc_without_gil(code)
707707

708-
def check_pyobject_is_freed(self, func):
709-
code = textwrap.dedent('''
708+
def check_pyobject_is_freed(self, func_name):
709+
code = textwrap.dedent(f'''
710710
import gc, os, sys, _testcapi
711711
# Disable the GC to avoid crash on GC collection
712712
gc.disable()
713-
obj = _testcapi.{func}()
714-
error = (_testcapi.pyobject_is_freed(obj) == False)
715-
# Exit immediately to avoid a crash while deallocating
716-
# the invalid object
717-
os._exit(int(error))
713+
try:
714+
_testcapi.{func_name}()
715+
# Exit immediately to avoid a crash while deallocating
716+
# the invalid object
717+
os._exit(0)
718+
except _testcapi.error:
719+
os._exit(1)
718720
''')
719-
code = code.format(func=func)
720721
assert_python_ok('-c', code, PYTHONMALLOC=self.PYTHONMALLOC)
721722

722-
def test_pyobject_is_freed_uninitialized(self):
723-
self.check_pyobject_is_freed('pyobject_uninitialized')
723+
def test_pyobject_uninitialized_is_freed(self):
724+
self.check_pyobject_is_freed('check_pyobject_uninitialized_is_freed')
724725

725-
def test_pyobject_is_freed_forbidden_bytes(self):
726-
self.check_pyobject_is_freed('pyobject_forbidden_bytes')
726+
def test_pyobject_forbidden_bytes_is_freed(self):
727+
self.check_pyobject_is_freed('check_pyobject_forbidden_bytes_is_freed')
727728

728-
def test_pyobject_is_freed_free(self):
729-
self.check_pyobject_is_freed('pyobject_freed')
729+
def test_pyobject_freed_is_freed(self):
730+
self.check_pyobject_is_freed('check_pyobject_freed_is_freed')
730731

731732

732733
class PyMemMallocDebugTests(PyMemDebugTests):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Rewrite ``_PyObject_IsFreed()`` unit tests.

Modules/_testcapimodule.c

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4489,15 +4489,17 @@ test_pymem_getallocatorsname(PyObject *self, PyObject *args)
44894489

44904490

44914491
static PyObject*
4492-
pyobject_is_freed(PyObject *self, PyObject *op)
4492+
test_pyobject_is_freed(const char *test_name, PyObject *op)
44934493
{
4494-
int res = _PyObject_IsFreed(op);
4495-
return PyBool_FromLong(res);
4494+
if (!_PyObject_IsFreed(op)) {
4495+
return raiseTestError(test_name, "object is not seen as freed");
4496+
}
4497+
Py_RETURN_NONE;
44964498
}
44974499

44984500

44994501
static PyObject*
4500-
pyobject_uninitialized(PyObject *self, PyObject *args)
4502+
check_pyobject_uninitialized_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
45014503
{
45024504
PyObject *op = (PyObject *)PyObject_Malloc(sizeof(PyObject));
45034505
if (op == NULL) {
@@ -4506,12 +4508,12 @@ pyobject_uninitialized(PyObject *self, PyObject *args)
45064508
/* Initialize reference count to avoid early crash in ceval or GC */
45074509
Py_REFCNT(op) = 1;
45084510
/* object fields like ob_type are uninitialized! */
4509-
return op;
4511+
return test_pyobject_is_freed("check_pyobject_uninitialized_is_freed", op);
45104512
}
45114513

45124514

45134515
static PyObject*
4514-
pyobject_forbidden_bytes(PyObject *self, PyObject *args)
4516+
check_pyobject_forbidden_bytes_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
45154517
{
45164518
/* Allocate an incomplete PyObject structure: truncate 'ob_type' field */
45174519
PyObject *op = (PyObject *)PyObject_Malloc(offsetof(PyObject, ob_type));
@@ -4522,12 +4524,12 @@ pyobject_forbidden_bytes(PyObject *self, PyObject *args)
45224524
Py_REFCNT(op) = 1;
45234525
/* ob_type field is after the memory block: part of "forbidden bytes"
45244526
when using debug hooks on memory allocatrs! */
4525-
return op;
4527+
return test_pyobject_is_freed("check_pyobject_forbidden_bytes_is_freed", op);
45264528
}
45274529

45284530

45294531
static PyObject*
4530-
pyobject_freed(PyObject *self, PyObject *args)
4532+
check_pyobject_freed_is_freed(PyObject *self, PyObject *Py_UNUSED(args))
45314533
{
45324534
PyObject *op = _PyObject_CallNoArg((PyObject *)&PyBaseObject_Type);
45334535
if (op == NULL) {
@@ -4537,7 +4539,7 @@ pyobject_freed(PyObject *self, PyObject *args)
45374539
/* Reset reference count to avoid early crash in ceval or GC */
45384540
Py_REFCNT(op) = 1;
45394541
/* object memory is freed! */
4540-
return op;
4542+
return test_pyobject_is_freed("check_pyobject_freed_is_freed", op);
45414543
}
45424544

45434545

@@ -5264,10 +5266,9 @@ static PyMethodDef TestMethods[] = {
52645266
{"pymem_api_misuse", pymem_api_misuse, METH_NOARGS},
52655267
{"pymem_malloc_without_gil", pymem_malloc_without_gil, METH_NOARGS},
52665268
{"pymem_getallocatorsname", test_pymem_getallocatorsname, METH_NOARGS},
5267-
{"pyobject_is_freed", (PyCFunction)(void(*)(void))pyobject_is_freed, METH_O},
5268-
{"pyobject_uninitialized", pyobject_uninitialized, METH_NOARGS},
5269-
{"pyobject_forbidden_bytes", pyobject_forbidden_bytes, METH_NOARGS},
5270-
{"pyobject_freed", pyobject_freed, METH_NOARGS},
5269+
{"check_pyobject_uninitialized_is_freed", check_pyobject_uninitialized_is_freed, METH_NOARGS},
5270+
{"check_pyobject_forbidden_bytes_is_freed", check_pyobject_forbidden_bytes_is_freed, METH_NOARGS},
5271+
{"check_pyobject_freed_is_freed", check_pyobject_freed_is_freed, METH_NOARGS},
52715272
{"pyobject_malloc_without_gil", pyobject_malloc_without_gil, METH_NOARGS},
52725273
{"tracemalloc_track", tracemalloc_track, METH_VARARGS},
52735274
{"tracemalloc_untrack", tracemalloc_untrack, METH_VARARGS},

0 commit comments

Comments
 (0)