]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-132417: ctypes: Fix potential `Py_DECREF(NULL)` when handling functions returning...
authorNicolas Trangez <ikke@nicolast.be>
Sat, 12 Apr 2025 07:40:34 +0000 (09:40 +0200)
committerGitHub <noreply@github.com>
Sat, 12 Apr 2025 07:40:34 +0000 (07:40 +0000)
Some functions (such as `PyErr_Occurred`) with a `restype` set to `ctypes.py_object` may return NULL without setting an exception.

Lib/test/test_ctypes/test_refcounts.py
Misc/NEWS.d/next/Library/2025-04-11-21-48-49.gh-issue-132417.uILGdS.rst [new file with mode: 0644]
Modules/_ctypes/callproc.c

index 1fe4b3eca2c50ef8ede3863151034fc8f3dbf01f..5f2f5c4a84d52ef38afa13112c58c9d984397996 100644 (file)
@@ -123,5 +123,20 @@ class ModuleIsolationTest(unittest.TestCase):
         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 (file)
index 0000000..878651c
--- /dev/null
@@ -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``.
index 352487ed964b541380e4b2f4da9e473b16a593bd..4e69c4e0a99ca22481d24ce6ccbf822b9b74e4b6 100644 (file)
@@ -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 {