Skip to content

Commit 7d6ddb9

Browse files
authored
bpo-28129: fix ctypes crashes (#386) (#3799)
* 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 ec3d34c commit 7d6ddb9

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
@@ -121,12 +121,21 @@ def test_from_buffer_copy_with_offset(self):
121121
(c_int * 1).from_buffer_copy(a, 16 * sizeof(c_int))
122122

123123
def test_abstract(self):
124+
from ctypes import _Pointer, _SimpleCData, _CFuncPtr
125+
124126
self.assertRaises(TypeError, Array.from_buffer, bytearray(10))
125127
self.assertRaises(TypeError, Structure.from_buffer, bytearray(10))
126128
self.assertRaises(TypeError, Union.from_buffer, bytearray(10))
129+
self.assertRaises(TypeError, _CFuncPtr.from_buffer, bytearray(10))
130+
self.assertRaises(TypeError, _Pointer.from_buffer, bytearray(10))
131+
self.assertRaises(TypeError, _SimpleCData.from_buffer, bytearray(10))
132+
127133
self.assertRaises(TypeError, Array.from_buffer_copy, b"123")
128134
self.assertRaises(TypeError, Structure.from_buffer_copy, b"123")
129135
self.assertRaises(TypeError, Union.from_buffer_copy, b"123")
136+
self.assertRaises(TypeError, _CFuncPtr.from_buffer_copy, b"123")
137+
self.assertRaises(TypeError, _Pointer.from_buffer_copy, b"123")
138+
self.assertRaises(TypeError, _SimpleCData.from_buffer_copy, b"123")
130139

131140
if __name__ == '__main__':
132141
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, b"\n"), b"c")
124124
self.assertEqual(strtok(None, b"\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
@@ -170,6 +170,16 @@ def from_param(cls, obj):
170170
# ArgumentError: argument 1: ValueError: 99
171171
self.assertRaises(ArgumentError, func, 99)
172172

173+
def test_abstract(self):
174+
from ctypes import (Array, Structure, Union, _Pointer,
175+
_SimpleCData, _CFuncPtr)
176+
177+
self.assertRaises(TypeError, Array.from_param, 42)
178+
self.assertRaises(TypeError, Structure.from_param, 42)
179+
self.assertRaises(TypeError, Union.from_param, 42)
180+
self.assertRaises(TypeError, _CFuncPtr.from_param, 42)
181+
self.assertRaises(TypeError, _Pointer.from_param, 42)
182+
self.assertRaises(TypeError, _SimpleCData.from_param, 42)
173183

174184
@test.support.cpython_only
175185
def test_issue31311(self):

Lib/ctypes/test/test_pointers.py

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

216+
def test_abstract(self):
217+
from ctypes import _Pointer
218+
219+
self.assertRaises(TypeError, _Pointer.set_type, 42)
220+
216221

217222
if __name__ == '__main__':
218223
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
@@ -990,6 +990,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
990990
if (proto) {
991991
StgDictObject *itemdict = PyType_stgdict(proto);
992992
const char *current_format;
993+
/* PyCPointerType_SetProto has verified proto has a stgdict. */
993994
assert(itemdict);
994995
/* If itemdict->format is NULL, then this is a pointer to an
995996
incomplete type. We create a generic format string
@@ -1036,7 +1037,11 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type)
10361037
StgDictObject *dict;
10371038

10381039
dict = PyType_stgdict((PyObject *)self);
1039-
assert(dict);
1040+
if (!dict) {
1041+
PyErr_SetString(PyExc_TypeError,
1042+
"abstract class");
1043+
return NULL;
1044+
}
10401045

10411046
if (-1 == PyCPointerType_SetProto(dict, type))
10421047
return NULL;
@@ -1062,7 +1067,11 @@ PyCPointerType_from_param(PyObject *type, PyObject *value)
10621067
}
10631068

10641069
typedict = PyType_stgdict(type);
1065-
assert(typedict); /* Cannot be NULL for pointer types */
1070+
if (!typedict) {
1071+
PyErr_SetString(PyExc_TypeError,
1072+
"abstract class");
1073+
return NULL;
1074+
}
10661075

10671076
/* If we expect POINTER(<type>), but receive a <type> instance, accept
10681077
it by calling byref(<type>).
@@ -2127,7 +2136,11 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
21272136
}
21282137

21292138
dict = PyType_stgdict(type);
2130-
assert(dict);
2139+
if (!dict) {
2140+
PyErr_SetString(PyExc_TypeError,
2141+
"abstract class");
2142+
return NULL;
2143+
}
21312144

21322145
/* I think we can rely on this being a one-character string */
21332146
fmt = PyUnicode_AsUTF8(dict->proto);
@@ -3231,7 +3244,11 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags)
32313244
PyObject *argtypes;
32323245

32333246
dict = PyType_stgdict((PyObject *)type);
3234-
assert(dict); /* Cannot be NULL. 'type' is a PyCFuncPtr type. */
3247+
if (!dict) {
3248+
PyErr_SetString(PyExc_TypeError,
3249+
"abstract class");
3250+
return 0;
3251+
}
32353252
argtypes = dict->argtypes;
32363253

32373254
if (paramflags == NULL || dict->argtypes == NULL)
@@ -4862,7 +4879,7 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
48624879
}
48634880

48644881
stgdict = PyObject_stgdict((PyObject *)self);
4865-
assert(stgdict); /* Cannot be NULL fr pointer instances */
4882+
assert(stgdict); /* Cannot be NULL for pointer instances */
48664883

48674884
proto = stgdict->proto;
48684885
assert(proto);
@@ -4890,7 +4907,7 @@ Pointer_get_contents(CDataObject *self, void *closure)
48904907
}
48914908

48924909
stgdict = PyObject_stgdict((PyObject *)self);
4893-
assert(stgdict); /* Cannot be NULL fr pointer instances */
4910+
assert(stgdict); /* Cannot be NULL for pointer instances */
48944911
return PyCData_FromBaseObj(stgdict->proto,
48954912
(PyObject *)self, 0,
48964913
*(void **)self->b_ptr);
@@ -4909,7 +4926,7 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure)
49094926
return -1;
49104927
}
49114928
stgdict = PyObject_stgdict((PyObject *)self);
4912-
assert(stgdict); /* Cannot be NULL fr pointer instances */
4929+
assert(stgdict); /* Cannot be NULL for pointer instances */
49134930
assert(stgdict->proto);
49144931
if (!CDataObject_Check(value)) {
49154932
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)