From 7f40914d3b1f0de0e00f40f25de686699e21559c Mon Sep 17 00:00:00 2001 From: Nicolas Trangez Date: Fri, 11 Apr 2025 20:58:21 +0100 Subject: [PATCH 1/3] GH-132417: ctypes: Fix potential Py_DECREF(NULL) in py_object handling (GH-132418) See: https://github.com/python/cpython/issues/132417 --- Lib/test/test_ctypes/test_refcounts.py | 17 +++++++++++++++++ ...25-04-11-21-48-49.gh-issue-132417.uILGdS.rst | 3 +++ Modules/_ctypes/callproc.c | 7 ++++--- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst diff --git a/Lib/test/test_ctypes/test_refcounts.py b/Lib/test/test_ctypes/test_refcounts.py index 1fe4b3eca2c50e..bcba12d683441d 100644 --- a/Lib/test/test_ctypes/test_refcounts.py +++ b/Lib/test/test_ctypes/test_refcounts.py @@ -123,5 +123,22 @@ def test_finalize(self): script_helper.assert_python_ok("-c", script) +class PyObjectRestypeTest(unittest.TestCase): + def test_restype_py_object_with_null_return(self): + # We need a function which returns a PyObject *, and returns NULL, + # without setting an exception. + # PyErr_Occurred is one such function (when no exception is set). + PyErr_Occurred = ctypes.pythonapi.PyErr_Occurred + + PyErr_Occurred.argtypes = [] + PyErr_Occurred.restype = ctypes.py_object + + # At this point, there's no exception set, so PyErr_Occurred + # returns NULL. Given the restype is py_object, the + # ctypes machinery will raise a custom error. + with self.assertRaisesRegex(ValueError, "PyObject is NULL"): + PyErr_Occurred() + + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst b/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst new file mode 100644 index 00000000000000..1196efaa39f465 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst @@ -0,0 +1,3 @@ +Fix a ``NULL``-pointer-dereference when a C function called using +:py:mod:`ctypes` with ``restype`` :py:obj:`~ctypes.py_object` returns +``NULL``. diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 352487ed964b54..4e69c4e0a99ca2 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1022,11 +1022,12 @@ static PyObject *GetResult(ctypes_state *st, if (info->getfunc && !_ctypes_simple_instance(st, restype)) { retval = info->getfunc(result, info->size); /* If restype is py_object (detected by comparing getfunc with - O_get), we have to call Py_DECREF because O_get has already - called Py_INCREF. + O_get), we have to call Py_XDECREF because O_get has already + called Py_INCREF, unless the result was NULL, in which case + an error is set (by the called function, or by O_get). */ if (info->getfunc == _ctypes_get_fielddesc("O")->getfunc) { - Py_DECREF(retval); + Py_XDECREF(retval); } } else { From d4e971eb30cd6b014d051ed72999dcf64658982f Mon Sep 17 00:00:00 2001 From: Nicolas Trangez Date: Fri, 11 Apr 2025 23:08:40 +0200 Subject: [PATCH 2/3] Update NEWS entry per review suggestion Co-authored-by: Tomas R. --- .../next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst b/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst index 1196efaa39f465..05f8c0fd7970c8 100644 --- a/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst +++ b/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst @@ -1,3 +1,3 @@ -Fix a ``NULL``-pointer-dereference when a C function called using +Fix a ``NULL`` pointer dereference when a C function called using :py:mod:`ctypes` with ``restype`` :py:obj:`~ctypes.py_object` returns ``NULL``. From f835efb4e08a3610a23d40b4956f0861d125395b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 12 Apr 2025 09:15:25 +0200 Subject: [PATCH 3/3] Apply suggestions from code review --- Lib/test/test_ctypes/test_refcounts.py | 6 ++---- .../Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_ctypes/test_refcounts.py b/Lib/test/test_ctypes/test_refcounts.py index bcba12d683441d..5f2f5c4a84d52e 100644 --- a/Lib/test/test_ctypes/test_refcounts.py +++ b/Lib/test/test_ctypes/test_refcounts.py @@ -125,11 +125,9 @@ def test_finalize(self): class PyObjectRestypeTest(unittest.TestCase): def test_restype_py_object_with_null_return(self): - # We need a function which returns a PyObject *, and returns NULL, - # without setting an exception. - # PyErr_Occurred is one such function (when no exception is set). + # Test that a function which returns a NULL PyObject * + # without setting an exception does not crash. PyErr_Occurred = ctypes.pythonapi.PyErr_Occurred - PyErr_Occurred.argtypes = [] PyErr_Occurred.restype = ctypes.py_object diff --git a/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst b/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst index 05f8c0fd7970c8..878651c8a0ad5c 100644 --- a/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst +++ b/Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst @@ -1,3 +1,3 @@ Fix a ``NULL`` pointer dereference when a C function called using -:py:mod:`ctypes` with ``restype`` :py:obj:`~ctypes.py_object` returns +:mod:`ctypes` with ``restype`` :class:`~ctypes.py_object` returns ``NULL``.