]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-138008: Fix segfaults in _ctypes due to invalid argtypes (GH-138285) (...
authorPetr Viktorin <encukou@gmail.com>
Thu, 18 Sep 2025 11:46:05 +0000 (13:46 +0200)
committerGitHub <noreply@github.com>
Thu, 18 Sep 2025 11:46:05 +0000 (12:46 +0100)
(cherry picked from commit 1ce05537a3ebaf1e5c54505b2272d61bb6cf5de0)

Signed-off-by: Nguyen Viet Dung <29406816+magnified103@users.noreply.github.com>
Signed-off-by: Nguyen Viet Dung <dung@ekluster.com>
Co-authored-by: Dung Nguyen <dung@ekluster.com>
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Yongzi Li <204532581+Yzi-Li@users.noreply.github.com>
Lib/test/test_ctypes/test_prototypes.py
Misc/NEWS.d/next/Library/2025-08-31-09-06-49.gh-issue-138008.heOvsU.rst [new file with mode: 0644]
Modules/_ctypes/_ctypes.c

index 63ae799ea86ab2d62cfff525e8d9bb0981de6f57..d976e8da0e2d30a151180cb2f9ae59de72b41e42 100644 (file)
@@ -72,6 +72,32 @@ class CharPointersTestCase(unittest.TestCase):
         self.assertEqual(func(None), None)
         self.assertEqual(func(input=None), None)
 
+    def test_invalid_paramflags(self):
+        proto = CFUNCTYPE(c_int, c_char_p)
+        with self.assertRaises(ValueError):
+            func = proto(("myprintf", testdll), ((1, "fmt"), (1, "arg1")))
+
+    def test_invalid_setattr_argtypes(self):
+        proto = CFUNCTYPE(c_int, c_char_p)
+        func = proto(("myprintf", testdll), ((1, "fmt"),))
+
+        with self.assertRaisesRegex(TypeError, "_argtypes_ must be a sequence of types"):
+            func.argtypes = 123
+        self.assertEqual(func.argtypes, (c_char_p,))
+
+        with self.assertRaisesRegex(ValueError, "paramflags must have the same length as argtypes"):
+            func.argtypes = (c_char_p, c_int)
+        self.assertEqual(func.argtypes, (c_char_p,))
+
+    def test_paramflags_outarg(self):
+        proto = CFUNCTYPE(c_int, c_char_p, c_int)
+        with self.assertRaisesRegex(TypeError, "must be a pointer type"):
+            func = proto(("myprintf", testdll), ((1, "fmt"), (2, "out")))
+
+        proto = CFUNCTYPE(c_int, c_char_p, c_void_p)
+        func = proto(("myprintf", testdll), ((1, "fmt"), (2, "out")))
+        with self.assertRaisesRegex(TypeError, "must be a pointer type"):
+            func.argtypes = (c_char_p, c_int)
 
     def test_int_pointer_arg(self):
         func = testdll._testfunc_p_p
diff --git a/Misc/NEWS.d/next/Library/2025-08-31-09-06-49.gh-issue-138008.heOvsU.rst b/Misc/NEWS.d/next/Library/2025-08-31-09-06-49.gh-issue-138008.heOvsU.rst
new file mode 100644 (file)
index 0000000..40930b8
--- /dev/null
@@ -0,0 +1 @@
+Fix segmentation faults in the :mod:`ctypes` module due to invalid :attr:`~ctypes._CFuncPtr.argtypes`. Patch by Dung Nguyen.
index 7f843a201cdb1fd2f56868e1276fd8ef9e933121..ad088b0ce8e2e7c7125e907b923f0600a175f695 100644 (file)
@@ -3396,6 +3396,9 @@ generic_pycdata_new(ctypes_state *st,
   PyCFuncPtr_Type
 */
 
+static int
+_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes);
+
 static int
 PyCFuncPtr_set_errcheck(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
 {
@@ -3470,21 +3473,26 @@ PyCFuncPtr_get_restype(PyCFuncPtrObject *self, void *Py_UNUSED(ignored))
 }
 
 static int
-PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *ob, void *Py_UNUSED(ignored))
+PyCFuncPtr_set_argtypes(PyCFuncPtrObject *self, PyObject *value, void *Py_UNUSED(ignored))
 {
-    PyObject *converters;
-
-    if (ob == NULL || ob == Py_None) {
+    if (value == NULL || value == Py_None) {
         Py_CLEAR(self->converters);
         Py_CLEAR(self->argtypes);
     } else {
-        ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self)));
-        converters = converters_from_argtypes(st, ob);
+        PyTypeObject *type = Py_TYPE(self);
+        ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
+        PyObject *converters = converters_from_argtypes(st, value);
         if (!converters)
             return -1;
+
+        /* Verify paramflags again due to constraints with argtypes */
+        if (!_validate_paramflags(st, type, self->paramflags, value)) {
+            Py_DECREF(converters);
+            return -1;
+        }
         Py_XSETREF(self->converters, converters);
-        Py_INCREF(ob);
-        Py_XSETREF(self->argtypes, ob);
+        Py_INCREF(value);
+        Py_XSETREF(self->argtypes, value);
     }
     return 0;
 }
@@ -3606,10 +3614,9 @@ _check_outarg_type(ctypes_state *st, PyObject *arg, Py_ssize_t index)
 
 /* Returns 1 on success, 0 on error */
 static int
-_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
+_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes)
 {
     Py_ssize_t i, len;
-    PyObject *argtypes;
 
     StgInfo *info;
     if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
@@ -3620,10 +3627,13 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
                         "abstract class");
         return 0;
     }
-    argtypes = info->argtypes;
+    if (argtypes == NULL) {
+        argtypes = info->argtypes;
+    }
 
-    if (paramflags == NULL || info->argtypes == NULL)
+    if (paramflags == NULL || argtypes == NULL) {
         return 1;
+    }
 
     if (!PyTuple_Check(paramflags)) {
         PyErr_SetString(PyExc_TypeError,
@@ -3632,7 +3642,7 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
     }
 
     len = PyTuple_GET_SIZE(paramflags);
-    if (len != PyTuple_GET_SIZE(info->argtypes)) {
+    if (len != PyTuple_GET_SIZE(argtypes)) {
         PyErr_SetString(PyExc_ValueError,
                         "paramflags must have the same length as argtypes");
         return 0;
@@ -3807,7 +3817,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
 #endif
 #undef USE_DLERROR
     ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
-    if (!_validate_paramflags(st, type, paramflags)) {
+    if (!_validate_paramflags(st, type, paramflags, NULL)) {
         Py_DECREF(ftuple);
         return NULL;
     }
@@ -3849,7 +3859,7 @@ PyCFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds)
         paramflags = NULL;
 
     ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
-    if (!_validate_paramflags(st, type, paramflags)) {
+    if (!_validate_paramflags(st, type, paramflags, NULL)) {
         return NULL;
     }
     self = (PyCFuncPtrObject *)generic_pycdata_new(st, type, args, kwds);