From 40f818e0df0cc8ca6f23aed41df71ca913b642d4 Mon Sep 17 00:00:00 2001 From: Nicolas Trangez Date: Sat, 12 Apr 2025 09:40:34 +0200 Subject: [PATCH] GH-132417: ctypes: Fix potential `Py_DECREF(NULL)` when handling functions returning `PyObject *` (GH-132418) Some functions (such as `PyErr_Occurred`) with a `restype` set to `ctypes.py_object` may return NULL without setting an exception. (cherry picked from commit 2aab2db1461ef49b42549255af16a74b1bf8a5ef) Co-authored-by: Nicolas Trangez --- Lib/test/test_ctypes/test_refcounts.py | 15 +++++++++++++++ ...2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst | 3 +++ Modules/_ctypes/callproc.c | 7 ++++--- 3 files changed, 22 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 012722d8486218..9e87cfc661edf1 100644 --- a/Lib/test/test_ctypes/test_refcounts.py +++ b/Lib/test/test_ctypes/test_refcounts.py @@ -124,5 +124,20 @@ def test_finalize(self): script_helper.assert_python_ok("-c", script) +class PyObjectRestypeTest(unittest.TestCase): + def test_restype_py_object_with_null_return(self): + # 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 + + # 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..878651c8a0ad5c --- /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 +:mod:`ctypes` with ``restype`` :class:`~ctypes.py_object` returns +``NULL``. diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index f9864ebb735ddf..8f94d19024e62c 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1016,11 +1016,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 {