Skip to content

Commit 1ce0553

Browse files
magnified103blurb-it[bot]Yzi-Li
authored
pythongh-138008: Fix segfaults in _ctypes due to invalid argtypes (pythonGH-138285)
Signed-off-by: Nguyen Viet Dung <[email protected]> Signed-off-by: Nguyen Viet Dung <[email protected]> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Yongzi Li <[email protected]>
1 parent d54b109 commit 1ce0553

File tree

3 files changed

+49
-11
lines changed

3 files changed

+49
-11
lines changed

Lib/test/test_ctypes/test_prototypes.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,32 @@ def test_paramflags(self):
7272
self.assertEqual(func(None), None)
7373
self.assertEqual(func(input=None), None)
7474

75+
def test_invalid_paramflags(self):
76+
proto = CFUNCTYPE(c_int, c_char_p)
77+
with self.assertRaises(ValueError):
78+
func = proto(("myprintf", testdll), ((1, "fmt"), (1, "arg1")))
79+
80+
def test_invalid_setattr_argtypes(self):
81+
proto = CFUNCTYPE(c_int, c_char_p)
82+
func = proto(("myprintf", testdll), ((1, "fmt"),))
83+
84+
with self.assertRaisesRegex(TypeError, "_argtypes_ must be a sequence of types"):
85+
func.argtypes = 123
86+
self.assertEqual(func.argtypes, (c_char_p,))
87+
88+
with self.assertRaisesRegex(ValueError, "paramflags must have the same length as argtypes"):
89+
func.argtypes = (c_char_p, c_int)
90+
self.assertEqual(func.argtypes, (c_char_p,))
91+
92+
def test_paramflags_outarg(self):
93+
proto = CFUNCTYPE(c_int, c_char_p, c_int)
94+
with self.assertRaisesRegex(TypeError, "must be a pointer type"):
95+
func = proto(("myprintf", testdll), ((1, "fmt"), (2, "out")))
96+
97+
proto = CFUNCTYPE(c_int, c_char_p, c_void_p)
98+
func = proto(("myprintf", testdll), ((1, "fmt"), (2, "out")))
99+
with self.assertRaisesRegex(TypeError, "must be a pointer type"):
100+
func.argtypes = (c_char_p, c_int)
75101

76102
def test_int_pointer_arg(self):
77103
func = testdll._testfunc_p_p
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix segmentation faults in the :mod:`ctypes` module due to invalid :attr:`~ctypes._CFuncPtr.argtypes`. Patch by Dung Nguyen.

Modules/_ctypes/_ctypes.c

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3634,6 +3634,9 @@ atomic_xgetref(PyObject *obj, PyObject **field)
36343634
#endif
36353635
}
36363636

3637+
static int
3638+
_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes);
3639+
36373640

36383641

36393642
/*[clinic input]
@@ -3747,16 +3750,22 @@ static int
37473750
_ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value)
37483751
/*[clinic end generated code: output=596a36e2ae89d7d1 input=c4627573e980aa8b]*/
37493752
{
3750-
PyObject *converters;
3751-
37523753
if (value == NULL || value == Py_None) {
37533754
atomic_xsetref(&self->argtypes, NULL);
37543755
atomic_xsetref(&self->converters, NULL);
37553756
} else {
3756-
ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self)));
3757-
converters = converters_from_argtypes(st, value);
3757+
PyTypeObject *type = Py_TYPE(self);
3758+
ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
3759+
3760+
PyObject *converters = converters_from_argtypes(st, value);
37583761
if (!converters)
37593762
return -1;
3763+
3764+
/* Verify paramflags again due to constraints with argtypes */
3765+
if (!_validate_paramflags(st, type, self->paramflags, value)) {
3766+
Py_DECREF(converters);
3767+
return -1;
3768+
}
37603769
atomic_xsetref(&self->converters, converters);
37613770
Py_INCREF(value);
37623771
atomic_xsetref(&self->argtypes, value);
@@ -3886,10 +3895,9 @@ _check_outarg_type(ctypes_state *st, PyObject *arg, Py_ssize_t index)
38863895

38873896
/* Returns 1 on success, 0 on error */
38883897
static int
3889-
_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
3898+
_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes)
38903899
{
38913900
Py_ssize_t i, len;
3892-
PyObject *argtypes;
38933901

38943902
StgInfo *info;
38953903
if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
@@ -3900,10 +3908,13 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
39003908
"abstract class");
39013909
return 0;
39023910
}
3903-
argtypes = info->argtypes;
3911+
if (argtypes == NULL) {
3912+
argtypes = info->argtypes;
3913+
}
39043914

3905-
if (paramflags == NULL || info->argtypes == NULL)
3915+
if (paramflags == NULL || argtypes == NULL) {
39063916
return 1;
3917+
}
39073918

39083919
if (!PyTuple_Check(paramflags)) {
39093920
PyErr_SetString(PyExc_TypeError,
@@ -3912,7 +3923,7 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
39123923
}
39133924

39143925
len = PyTuple_GET_SIZE(paramflags);
3915-
if (len != PyTuple_GET_SIZE(info->argtypes)) {
3926+
if (len != PyTuple_GET_SIZE(argtypes)) {
39163927
PyErr_SetString(PyExc_ValueError,
39173928
"paramflags must have the same length as argtypes");
39183929
return 0;
@@ -4088,7 +4099,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
40884099
#endif
40894100
#undef USE_DLERROR
40904101
ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
4091-
if (!_validate_paramflags(st, type, paramflags)) {
4102+
if (!_validate_paramflags(st, type, paramflags, NULL)) {
40924103
Py_DECREF(ftuple);
40934104
return NULL;
40944105
}
@@ -4132,7 +4143,7 @@ PyCFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds)
41324143
paramflags = NULL;
41334144

41344145
ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
4135-
if (!_validate_paramflags(st, type, paramflags)) {
4146+
if (!_validate_paramflags(st, type, paramflags, NULL)) {
41364147
return NULL;
41374148
}
41384149
self = (PyCFuncPtrObject *)generic_pycdata_new(st, type, args, kwds);

0 commit comments

Comments
 (0)