]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-138008: Fix segfaults in _ctypes due to invalid argtypes (GH-138285) (GH...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 7 Oct 2025 18:00:28 +0000 (20:00 +0200)
committerGitHub <noreply@github.com>
Tue, 7 Oct 2025 18:00:28 +0000 (20:00 +0200)
(cherry picked from commit 1ce05537a3ebaf1e5c54505b2272d61bb6cf5de0)

Signed-off-by: Nguyen Viet Dung <29406816+magnified103@users.noreply.github.com>
Co-authored-by: Yongzi Li <204532581+Yzi-Li@users.noreply.github.com>
Co-authored-by: Petr Viktorin <encukou@gmail.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 08e00adfd0c3e6d7ae0a2ce6d22cbd986631d5ed..bac119f2ae7a367188e4e8c60ce81a7376cfc3ed 100644 (file)
@@ -3628,6 +3628,9 @@ atomic_xgetref(PyObject *obj, PyObject **field)
 #endif
 }
 
+static int
+_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes);
+
 
 
 /*[clinic input]
@@ -3741,16 +3744,22 @@ static int
 _ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value)
 /*[clinic end generated code: output=596a36e2ae89d7d1 input=c4627573e980aa8b]*/
 {
-    PyObject *converters;
-
     if (value == NULL || value == Py_None) {
         atomic_xsetref(&self->argtypes, NULL);
         atomic_xsetref(&self->converters, NULL);
     } else {
-        ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self)));
-        converters = converters_from_argtypes(st, value);
+        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;
+        }
         atomic_xsetref(&self->converters, converters);
         Py_INCREF(value);
         atomic_xsetref(&self->argtypes, value);
@@ -3880,10 +3889,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) {
@@ -3894,10 +3902,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,
@@ -3906,7 +3917,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;
@@ -4082,7 +4093,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;
     }
@@ -4126,7 +4137,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);