Skip to content

Commit 1bea762

Browse files
orenmnvstinner
authored andcommitted
bpo-28129: fix ctypes crashes (#386)
* 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
1 parent 8ae264c commit 1bea762

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
@@ -169,6 +169,16 @@ def from_param(cls, obj):
169169
# ArgumentError: argument 1: ValueError: 99
170170
self.assertRaises(ArgumentError, func, 99)
171171

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

173183
################################################################
174184

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
@@ -924,6 +924,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
924924
if (proto) {
925925
StgDictObject *itemdict = PyType_stgdict(proto);
926926
const char *current_format;
927+
/* PyCPointerType_SetProto has verified proto has a stgdict. */
927928
assert(itemdict);
928929
/* If itemdict->format is NULL, then this is a pointer to an
929930
incomplete type. We create a generic format string
@@ -970,7 +971,11 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type)
970971
StgDictObject *dict;
971972

972973
dict = PyType_stgdict((PyObject *)self);
973-
assert(dict);
974+
if (!dict) {
975+
PyErr_SetString(PyExc_TypeError,
976+
"abstract class");
977+
return NULL;
978+
}
974979

975980
if (-1 == PyCPointerType_SetProto(dict, type))
976981
return NULL;
@@ -995,7 +1000,11 @@ PyCPointerType_from_param(PyObject *type, PyObject *value)
9951000
}
9961001

9971002
typedict = PyType_stgdict(type);
998-
assert(typedict); /* Cannot be NULL for pointer types */
1003+
if (!typedict) {
1004+
PyErr_SetString(PyExc_TypeError,
1005+
"abstract class");
1006+
return NULL;
1007+
}
9991008

10001009
/* If we expect POINTER(<type>), but receive a <type> instance, accept
10011010
it by calling byref(<type>).
@@ -2057,7 +2066,11 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
20572066
}
20582067

20592068
dict = PyType_stgdict(type);
2060-
assert(dict);
2069+
if (!dict) {
2070+
PyErr_SetString(PyExc_TypeError,
2071+
"abstract class");
2072+
return NULL;
2073+
}
20612074

20622075
/* I think we can rely on this being a one-character string */
20632076
fmt = PyUnicode_AsUTF8(dict->proto);
@@ -3146,7 +3159,11 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags)
31463159
PyObject *argtypes;
31473160

31483161
dict = PyType_stgdict((PyObject *)type);
3149-
assert(dict); /* Cannot be NULL. 'type' is a PyCFuncPtr type. */
3162+
if (!dict) {
3163+
PyErr_SetString(PyExc_TypeError,
3164+
"abstract class");
3165+
return 0;
3166+
}
31503167
argtypes = dict->argtypes;
31513168

31523169
if (paramflags == NULL || dict->argtypes == NULL)
@@ -4779,7 +4796,7 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
47794796
}
47804797

47814798
stgdict = PyObject_stgdict((PyObject *)self);
4782-
assert(stgdict); /* Cannot be NULL fr pointer instances */
4799+
assert(stgdict); /* Cannot be NULL for pointer instances */
47834800

47844801
proto = stgdict->proto;
47854802
assert(proto);
@@ -4807,7 +4824,7 @@ Pointer_get_contents(CDataObject *self, void *closure)
48074824
}
48084825

48094826
stgdict = PyObject_stgdict((PyObject *)self);
4810-
assert(stgdict); /* Cannot be NULL fr pointer instances */
4827+
assert(stgdict); /* Cannot be NULL for pointer instances */
48114828
return PyCData_FromBaseObj(stgdict->proto,
48124829
(PyObject *)self, 0,
48134830
*(void **)self->b_ptr);
@@ -4826,7 +4843,7 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure)
48264843
return -1;
48274844
}
48284845
stgdict = PyObject_stgdict((PyObject *)self);
4829-
assert(stgdict); /* Cannot be NULL fr pointer instances */
4846+
assert(stgdict); /* Cannot be NULL for pointer instances */
48304847
assert(stgdict->proto);
48314848
if (!CDataObject_Check(value)) {
48324849
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)