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

Signed-off-by: Nguyen Viet Dung <29406816+magnified103@users.noreply.github.com>
Signed-off-by: Nguyen Viet Dung <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>
This commit is contained in:
Dung Nguyen 2025-09-10 19:01:19 +07:00 committed by GitHub
parent d54b1091d4
commit 1ce05537a3
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

@ -3634,6 +3634,9 @@ atomic_xgetref(PyObject *obj, PyObject **field)
#endif
}
static int
_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes);
/*[clinic input]
@ -3747,16 +3750,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);
@ -3886,10 +3895,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) {
@ -3900,10 +3908,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,
@ -3912,7 +3923,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;
@ -4088,7 +4099,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;
}
@ -4132,7 +4143,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);