[3.14] gh-138008: Fix segfaults in _ctypes due to invalid argtypes (GH-138285) (GH-138742)

(cherry picked from commit 1ce05537a3)

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>
This commit is contained in:
Miss Islington (bot) 2025-10-07 20:00:28 +02:00 committed by GitHub
parent 7aab50cd7a
commit d7500f6078
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 49 additions and 11 deletions

View file

@ -72,6 +72,32 @@ def test_paramflags(self):
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

View file

@ -0,0 +1 @@
Fix segmentation faults in the :mod:`ctypes` module due to invalid :attr:`~ctypes._CFuncPtr.argtypes`. Patch by Dung Nguyen.

View 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);