Skip to content

Commit d7500f6

Browse files
miss-islingtonYzi-Liencukou
authored
[3.14] gh-138008: Fix segfaults in _ctypes due to invalid argtypes (GH-138285) (GH-138742)
(cherry picked from commit 1ce0553) Signed-off-by: Nguyen Viet Dung <[email protected]> Co-authored-by: Yongzi Li <[email protected]> Co-authored-by: Petr Viktorin <[email protected]>
1 parent 7aab50c commit d7500f6

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
@@ -3628,6 +3628,9 @@ atomic_xgetref(PyObject *obj, PyObject **field)
36283628
#endif
36293629
}
36303630

3631+
static int
3632+
_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes);
3633+
36313634

36323635

36333636
/*[clinic input]
@@ -3741,16 +3744,22 @@ static int
37413744
_ctypes_CFuncPtr_argtypes_set_impl(PyCFuncPtrObject *self, PyObject *value)
37423745
/*[clinic end generated code: output=596a36e2ae89d7d1 input=c4627573e980aa8b]*/
37433746
{
3744-
PyObject *converters;
3745-
37463747
if (value == NULL || value == Py_None) {
37473748
atomic_xsetref(&self->argtypes, NULL);
37483749
atomic_xsetref(&self->converters, NULL);
37493750
} else {
3750-
ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self)));
3751-
converters = converters_from_argtypes(st, value);
3751+
PyTypeObject *type = Py_TYPE(self);
3752+
ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
3753+
3754+
PyObject *converters = converters_from_argtypes(st, value);
37523755
if (!converters)
37533756
return -1;
3757+
3758+
/* Verify paramflags again due to constraints with argtypes */
3759+
if (!_validate_paramflags(st, type, self->paramflags, value)) {
3760+
Py_DECREF(converters);
3761+
return -1;
3762+
}
37543763
atomic_xsetref(&self->converters, converters);
37553764
Py_INCREF(value);
37563765
atomic_xsetref(&self->argtypes, value);
@@ -3880,10 +3889,9 @@ _check_outarg_type(ctypes_state *st, PyObject *arg, Py_ssize_t index)
38803889

38813890
/* Returns 1 on success, 0 on error */
38823891
static int
3883-
_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
3892+
_validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags, PyObject *argtypes)
38843893
{
38853894
Py_ssize_t i, len;
3886-
PyObject *argtypes;
38873895

38883896
StgInfo *info;
38893897
if (PyStgInfo_FromType(st, (PyObject *)type, &info) < 0) {
@@ -3894,10 +3902,13 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
38943902
"abstract class");
38953903
return 0;
38963904
}
3897-
argtypes = info->argtypes;
3905+
if (argtypes == NULL) {
3906+
argtypes = info->argtypes;
3907+
}
38983908

3899-
if (paramflags == NULL || info->argtypes == NULL)
3909+
if (paramflags == NULL || argtypes == NULL) {
39003910
return 1;
3911+
}
39013912

39023913
if (!PyTuple_Check(paramflags)) {
39033914
PyErr_SetString(PyExc_TypeError,
@@ -3906,7 +3917,7 @@ _validate_paramflags(ctypes_state *st, PyTypeObject *type, PyObject *paramflags)
39063917
}
39073918

39083919
len = PyTuple_GET_SIZE(paramflags);
3909-
if (len != PyTuple_GET_SIZE(info->argtypes)) {
3920+
if (len != PyTuple_GET_SIZE(argtypes)) {
39103921
PyErr_SetString(PyExc_ValueError,
39113922
"paramflags must have the same length as argtypes");
39123923
return 0;
@@ -4082,7 +4093,7 @@ PyCFuncPtr_FromDll(PyTypeObject *type, PyObject *args, PyObject *kwds)
40824093
#endif
40834094
#undef USE_DLERROR
40844095
ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
4085-
if (!_validate_paramflags(st, type, paramflags)) {
4096+
if (!_validate_paramflags(st, type, paramflags, NULL)) {
40864097
Py_DECREF(ftuple);
40874098
return NULL;
40884099
}
@@ -4126,7 +4137,7 @@ PyCFuncPtr_FromVtblIndex(PyTypeObject *type, PyObject *args, PyObject *kwds)
41264137
paramflags = NULL;
41274138

41284139
ctypes_state *st = get_module_state_by_def(Py_TYPE(type));
4129-
if (!_validate_paramflags(st, type, paramflags)) {
4140+
if (!_validate_paramflags(st, type, paramflags, NULL)) {
41304141
return NULL;
41314142
}
41324143
self = (PyCFuncPtrObject *)generic_pycdata_new(st, type, args, kwds);

0 commit comments

Comments
 (0)