Skip to content

Commit 8b83687

Browse files
authored
bpo-28129: fix ctypes crashes (#386) (#3800)
* init commit, with initial tests for from_param and fields __set__ and __get__, and some additions to from_buffer and from_buffer_copy * added the rest of tests and patches. probably only a first draft. * removed trailing spaces * replace ctype with ctypes in error messages * change back from ctypes instance to ctype instance (cherry picked from commit 1bea762)
1 parent b4920d5 commit 8b83687

File tree

7 files changed

+87
-9
lines changed

7 files changed

+87
-9
lines changed

Lib/ctypes/test/test_frombuffer.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,21 @@ def test_fom_buffer_copy_with_offset(self):
7878
(c_int * 1).from_buffer_copy, a, 16 * sizeof(c_int))
7979

8080
def test_abstract(self):
81+
from ctypes import _Pointer, _SimpleCData, _CFuncPtr
82+
8183
self.assertRaises(TypeError, Array.from_buffer, bytearray(10))
8284
self.assertRaises(TypeError, Structure.from_buffer, bytearray(10))
8385
self.assertRaises(TypeError, Union.from_buffer, bytearray(10))
86+
self.assertRaises(TypeError, _CFuncPtr.from_buffer, bytearray(10))
87+
self.assertRaises(TypeError, _Pointer.from_buffer, bytearray(10))
88+
self.assertRaises(TypeError, _SimpleCData.from_buffer, bytearray(10))
89+
8490
self.assertRaises(TypeError, Array.from_buffer_copy, b"123")
8591
self.assertRaises(TypeError, Structure.from_buffer_copy, b"123")
8692
self.assertRaises(TypeError, Union.from_buffer_copy, b"123")
93+
self.assertRaises(TypeError, _CFuncPtr.from_buffer_copy, b"123")
94+
self.assertRaises(TypeError, _Pointer.from_buffer_copy, b"123")
95+
self.assertRaises(TypeError, _SimpleCData.from_buffer_copy, b"123")
8796

8897
if __name__ == '__main__':
8998
unittest.main()

Lib/ctypes/test/test_funcptr.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,10 @@ def c_string(init):
123123
self.assertEqual(strtok(None, "\n"), "c")
124124
self.assertEqual(strtok(None, "\n"), None)
125125

126+
def test_abstract(self):
127+
from ctypes import _CFuncPtr
128+
129+
self.assertRaises(TypeError, _CFuncPtr, 13, "name", 42, "iid")
130+
126131
if __name__ == '__main__':
127132
unittest.main()

Lib/ctypes/test/test_parameters.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,16 @@ def from_param(cls, obj):
175175
# ArgumentError: argument 1: ValueError: 99
176176
self.assertRaises(ArgumentError, func, 99)
177177

178+
def test_abstract(self):
179+
from ctypes import (Array, Structure, Union, _Pointer,
180+
_SimpleCData, _CFuncPtr)
181+
182+
self.assertRaises(TypeError, Array.from_param, 42)
183+
self.assertRaises(TypeError, Structure.from_param, 42)
184+
self.assertRaises(TypeError, Union.from_param, 42)
185+
self.assertRaises(TypeError, _CFuncPtr.from_param, 42)
186+
self.assertRaises(TypeError, _Pointer.from_param, 42)
187+
self.assertRaises(TypeError, _SimpleCData.from_param, 42)
178188

179189
@test.support.cpython_only
180190
def test_issue31311(self):

Lib/ctypes/test/test_pointers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ def test_pointer_type_str_name(self):
210210
from ctypes import _pointer_type_cache
211211
del _pointer_type_cache[id(P)]
212212

213+
def test_abstract(self):
214+
from ctypes import _Pointer
215+
216+
self.assertRaises(TypeError, _Pointer.set_type, 42)
217+
213218

214219
if __name__ == '__main__':
215220
unittest.main()

Lib/ctypes/test/test_struct_fields.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,29 @@ class Y(X):
4646
Y._fields_ = []
4747
self.assertRaises(AttributeError, setattr, X, "_fields_", [])
4848

49+
# __set__ and __get__ should raise a TypeError in case their self
50+
# argument is not a ctype instance.
51+
def test___set__(self):
52+
class MyCStruct(Structure):
53+
_fields_ = (("field", c_int),)
54+
self.assertRaises(TypeError,
55+
MyCStruct.field.__set__, 'wrong type self', 42)
56+
57+
class MyCUnion(Union):
58+
_fields_ = (("field", c_int),)
59+
self.assertRaises(TypeError,
60+
MyCUnion.field.__set__, 'wrong type self', 42)
61+
62+
def test___get__(self):
63+
class MyCStruct(Structure):
64+
_fields_ = (("field", c_int),)
65+
self.assertRaises(TypeError,
66+
MyCStruct.field.__get__, 'wrong type self', 42)
67+
68+
class MyCUnion(Union):
69+
_fields_ = (("field", c_int),)
70+
self.assertRaises(TypeError,
71+
MyCUnion.field.__get__, 'wrong type self', 42)
72+
4973
if __name__ == "__main__":
5074
unittest.main()

Modules/_ctypes/_ctypes.c

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
10361036
if (proto) {
10371037
StgDictObject *itemdict = PyType_stgdict(proto);
10381038
const char *current_format;
1039+
/* PyCPointerType_SetProto has verified proto has a stgdict. */
10391040
assert(itemdict);
10401041
/* If itemdict->format is NULL, then this is a pointer to an
10411042
incomplete type. We create a generic format string
@@ -1082,7 +1083,11 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type)
10821083
StgDictObject *dict;
10831084

10841085
dict = PyType_stgdict((PyObject *)self);
1085-
assert(dict);
1086+
if (!dict) {
1087+
PyErr_SetString(PyExc_TypeError,
1088+
"abstract class");
1089+
return NULL;
1090+
}
10861091

10871092
if (-1 == PyCPointerType_SetProto(dict, type))
10881093
return NULL;
@@ -1108,7 +1113,11 @@ PyCPointerType_from_param(PyObject *type, PyObject *value)
11081113
}
11091114

11101115
typedict = PyType_stgdict(type);
1111-
assert(typedict); /* Cannot be NULL for pointer types */
1116+
if (!typedict) {
1117+
PyErr_SetString(PyExc_TypeError,
1118+
"abstract class");
1119+
return NULL;
1120+
}
11121121

11131122
/* If we expect POINTER(<type>), but receive a <type> instance, accept
11141123
it by calling byref(<type>).
@@ -2226,7 +2235,11 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
22262235
}
22272236

22282237
dict = PyType_stgdict(type);
2229-
assert(dict);
2238+
if (!dict) {
2239+
PyErr_SetString(PyExc_TypeError,
2240+
"abstract class");
2241+
return NULL;
2242+
}
22302243

22312244
/* I think we can rely on this being a one-character string */
22322245
fmt = PyString_AsString(dict->proto);
@@ -3347,7 +3360,11 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags)
33473360
PyObject *argtypes;
33483361

33493362
dict = PyType_stgdict((PyObject *)type);
3350-
assert(dict); /* Cannot be NULL. 'type' is a PyCFuncPtr type. */
3363+
if (!dict) {
3364+
PyErr_SetString(PyExc_TypeError,
3365+
"abstract class");
3366+
return 0;
3367+
}
33513368
argtypes = dict->argtypes;
33523369

33533370
if (paramflags == NULL || dict->argtypes == NULL)
@@ -5090,7 +5107,7 @@ Pointer_ass_item(PyObject *_self, Py_ssize_t index, PyObject *value)
50905107
}
50915108

50925109
stgdict = PyObject_stgdict((PyObject *)self);
5093-
assert(stgdict); /* Cannot be NULL fr pointer instances */
5110+
assert(stgdict); /* Cannot be NULL for pointer instances */
50945111

50955112
proto = stgdict->proto;
50965113
assert(proto);
@@ -5118,7 +5135,7 @@ Pointer_get_contents(CDataObject *self, void *closure)
51185135
}
51195136

51205137
stgdict = PyObject_stgdict((PyObject *)self);
5121-
assert(stgdict); /* Cannot be NULL fr pointer instances */
5138+
assert(stgdict); /* Cannot be NULL for pointer instances */
51225139
return PyCData_FromBaseObj(stgdict->proto,
51235140
(PyObject *)self, 0,
51245141
*(void **)self->b_ptr);
@@ -5137,7 +5154,7 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure)
51375154
return -1;
51385155
}
51395156
stgdict = PyObject_stgdict((PyObject *)self);
5140-
assert(stgdict); /* Cannot be NULL fr pointer instances */
5157+
assert(stgdict); /* Cannot be NULL for pointer instances */
51415158
assert(stgdict->proto);
51425159
if (!CDataObject_Check(value)) {
51435160
int res = PyObject_IsInstance(value, stgdict->proto);

Modules/_ctypes/cfield.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,11 @@ PyCField_set(CFieldObject *self, PyObject *inst, PyObject *value)
205205
{
206206
CDataObject *dst;
207207
char *ptr;
208-
assert(CDataObject_Check(inst));
208+
if (!CDataObject_Check(inst)) {
209+
PyErr_SetString(PyExc_TypeError,
210+
"not a ctype instance");
211+
return -1;
212+
}
209213
dst = (CDataObject *)inst;
210214
ptr = dst->b_ptr + self->offset;
211215
if (value == NULL) {
@@ -225,7 +229,11 @@ PyCField_get(CFieldObject *self, PyObject *inst, PyTypeObject *type)
225229
Py_INCREF(self);
226230
return (PyObject *)self;
227231
}
228-
assert(CDataObject_Check(inst));
232+
if (!CDataObject_Check(inst)) {
233+
PyErr_SetString(PyExc_TypeError,
234+
"not a ctype instance");
235+
return NULL;
236+
}
229237
src = (CDataObject *)inst;
230238
return PyCData_get(self->proto, self->getfunc, inst,
231239
self->index, self->size, src->b_ptr + self->offset);

0 commit comments

Comments
 (0)