Commit 8b83687b authored by Victor Stinner's avatar Victor Stinner Committed by GitHub

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 1bea762d)
parent b4920d56
...@@ -78,12 +78,21 @@ class Test(unittest.TestCase): ...@@ -78,12 +78,21 @@ class Test(unittest.TestCase):
(c_int * 1).from_buffer_copy, a, 16 * sizeof(c_int)) (c_int * 1).from_buffer_copy, a, 16 * sizeof(c_int))
def test_abstract(self): def test_abstract(self):
from ctypes import _Pointer, _SimpleCData, _CFuncPtr
self.assertRaises(TypeError, Array.from_buffer, bytearray(10)) self.assertRaises(TypeError, Array.from_buffer, bytearray(10))
self.assertRaises(TypeError, Structure.from_buffer, bytearray(10)) self.assertRaises(TypeError, Structure.from_buffer, bytearray(10))
self.assertRaises(TypeError, Union.from_buffer, bytearray(10)) self.assertRaises(TypeError, Union.from_buffer, bytearray(10))
self.assertRaises(TypeError, _CFuncPtr.from_buffer, bytearray(10))
self.assertRaises(TypeError, _Pointer.from_buffer, bytearray(10))
self.assertRaises(TypeError, _SimpleCData.from_buffer, bytearray(10))
self.assertRaises(TypeError, Array.from_buffer_copy, b"123") self.assertRaises(TypeError, Array.from_buffer_copy, b"123")
self.assertRaises(TypeError, Structure.from_buffer_copy, b"123") self.assertRaises(TypeError, Structure.from_buffer_copy, b"123")
self.assertRaises(TypeError, Union.from_buffer_copy, b"123") self.assertRaises(TypeError, Union.from_buffer_copy, b"123")
self.assertRaises(TypeError, _CFuncPtr.from_buffer_copy, b"123")
self.assertRaises(TypeError, _Pointer.from_buffer_copy, b"123")
self.assertRaises(TypeError, _SimpleCData.from_buffer_copy, b"123")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -123,5 +123,10 @@ class CFuncPtrTestCase(unittest.TestCase): ...@@ -123,5 +123,10 @@ class CFuncPtrTestCase(unittest.TestCase):
self.assertEqual(strtok(None, "\n"), "c") self.assertEqual(strtok(None, "\n"), "c")
self.assertEqual(strtok(None, "\n"), None) self.assertEqual(strtok(None, "\n"), None)
def test_abstract(self):
from ctypes import _CFuncPtr
self.assertRaises(TypeError, _CFuncPtr, 13, "name", 42, "iid")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -175,6 +175,16 @@ class SimpleTypesTestCase(unittest.TestCase): ...@@ -175,6 +175,16 @@ class SimpleTypesTestCase(unittest.TestCase):
# ArgumentError: argument 1: ValueError: 99 # ArgumentError: argument 1: ValueError: 99
self.assertRaises(ArgumentError, func, 99) self.assertRaises(ArgumentError, func, 99)
def test_abstract(self):
from ctypes import (Array, Structure, Union, _Pointer,
_SimpleCData, _CFuncPtr)
self.assertRaises(TypeError, Array.from_param, 42)
self.assertRaises(TypeError, Structure.from_param, 42)
self.assertRaises(TypeError, Union.from_param, 42)
self.assertRaises(TypeError, _CFuncPtr.from_param, 42)
self.assertRaises(TypeError, _Pointer.from_param, 42)
self.assertRaises(TypeError, _SimpleCData.from_param, 42)
@test.support.cpython_only @test.support.cpython_only
def test_issue31311(self): def test_issue31311(self):
......
...@@ -210,6 +210,11 @@ class PointersTestCase(unittest.TestCase): ...@@ -210,6 +210,11 @@ class PointersTestCase(unittest.TestCase):
from ctypes import _pointer_type_cache from ctypes import _pointer_type_cache
del _pointer_type_cache[id(P)] del _pointer_type_cache[id(P)]
def test_abstract(self):
from ctypes import _Pointer
self.assertRaises(TypeError, _Pointer.set_type, 42)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -46,5 +46,29 @@ class StructFieldsTestCase(unittest.TestCase): ...@@ -46,5 +46,29 @@ class StructFieldsTestCase(unittest.TestCase):
Y._fields_ = [] Y._fields_ = []
self.assertRaises(AttributeError, setattr, X, "_fields_", []) self.assertRaises(AttributeError, setattr, X, "_fields_", [])
# __set__ and __get__ should raise a TypeError in case their self
# argument is not a ctype instance.
def test___set__(self):
class MyCStruct(Structure):
_fields_ = (("field", c_int),)
self.assertRaises(TypeError,
MyCStruct.field.__set__, 'wrong type self', 42)
class MyCUnion(Union):
_fields_ = (("field", c_int),)
self.assertRaises(TypeError,
MyCUnion.field.__set__, 'wrong type self', 42)
def test___get__(self):
class MyCStruct(Structure):
_fields_ = (("field", c_int),)
self.assertRaises(TypeError,
MyCStruct.field.__get__, 'wrong type self', 42)
class MyCUnion(Union):
_fields_ = (("field", c_int),)
self.assertRaises(TypeError,
MyCUnion.field.__get__, 'wrong type self', 42)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
...@@ -1036,6 +1036,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ...@@ -1036,6 +1036,7 @@ PyCPointerType_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (proto) { if (proto) {
StgDictObject *itemdict = PyType_stgdict(proto); StgDictObject *itemdict = PyType_stgdict(proto);
const char *current_format; const char *current_format;
/* PyCPointerType_SetProto has verified proto has a stgdict. */
assert(itemdict); assert(itemdict);
/* If itemdict->format is NULL, then this is a pointer to an /* If itemdict->format is NULL, then this is a pointer to an
incomplete type. We create a generic format string incomplete type. We create a generic format string
...@@ -1082,7 +1083,11 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type) ...@@ -1082,7 +1083,11 @@ PyCPointerType_set_type(PyTypeObject *self, PyObject *type)
StgDictObject *dict; StgDictObject *dict;
dict = PyType_stgdict((PyObject *)self); dict = PyType_stgdict((PyObject *)self);
assert(dict); if (!dict) {
PyErr_SetString(PyExc_TypeError,
"abstract class");
return NULL;
}
if (-1 == PyCPointerType_SetProto(dict, type)) if (-1 == PyCPointerType_SetProto(dict, type))
return NULL; return NULL;
...@@ -1108,7 +1113,11 @@ PyCPointerType_from_param(PyObject *type, PyObject *value) ...@@ -1108,7 +1113,11 @@ PyCPointerType_from_param(PyObject *type, PyObject *value)
} }
typedict = PyType_stgdict(type); typedict = PyType_stgdict(type);
assert(typedict); /* Cannot be NULL for pointer types */ if (!typedict) {
PyErr_SetString(PyExc_TypeError,
"abstract class");
return NULL;
}
/* If we expect POINTER(<type>), but receive a <type> instance, accept /* If we expect POINTER(<type>), but receive a <type> instance, accept
it by calling byref(<type>). it by calling byref(<type>).
...@@ -2226,7 +2235,11 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value) ...@@ -2226,7 +2235,11 @@ PyCSimpleType_from_param(PyObject *type, PyObject *value)
} }
dict = PyType_stgdict(type); dict = PyType_stgdict(type);
assert(dict); if (!dict) {
PyErr_SetString(PyExc_TypeError,
"abstract class");
return NULL;
}
/* I think we can rely on this being a one-character string */ /* I think we can rely on this being a one-character string */
fmt = PyString_AsString(dict->proto); fmt = PyString_AsString(dict->proto);
...@@ -3347,7 +3360,11 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags) ...@@ -3347,7 +3360,11 @@ _validate_paramflags(PyTypeObject *type, PyObject *paramflags)
PyObject *argtypes; PyObject *argtypes;
dict = PyType_stgdict((PyObject *)type); dict = PyType_stgdict((PyObject *)type);
assert(dict); /* Cannot be NULL. 'type' is a PyCFuncPtr type. */ if (!dict) {
PyErr_SetString(PyExc_TypeError,
"abstract class");
return 0;
}
argtypes = dict->argtypes; argtypes = dict->argtypes;
if (paramflags == NULL || dict->argtypes == NULL) if (paramflags == NULL || dict->argtypes == NULL)
...@@ -5090,7 +5107,7 @@ Pointer_ass_item(PyObject *_self, Py_ssize_t index, PyObject *value) ...@@ -5090,7 +5107,7 @@ Pointer_ass_item(PyObject *_self, Py_ssize_t index, PyObject *value)
} }
stgdict = PyObject_stgdict((PyObject *)self); stgdict = PyObject_stgdict((PyObject *)self);
assert(stgdict); /* Cannot be NULL fr pointer instances */ assert(stgdict); /* Cannot be NULL for pointer instances */
proto = stgdict->proto; proto = stgdict->proto;
assert(proto); assert(proto);
...@@ -5118,7 +5135,7 @@ Pointer_get_contents(CDataObject *self, void *closure) ...@@ -5118,7 +5135,7 @@ Pointer_get_contents(CDataObject *self, void *closure)
} }
stgdict = PyObject_stgdict((PyObject *)self); stgdict = PyObject_stgdict((PyObject *)self);
assert(stgdict); /* Cannot be NULL fr pointer instances */ assert(stgdict); /* Cannot be NULL for pointer instances */
return PyCData_FromBaseObj(stgdict->proto, return PyCData_FromBaseObj(stgdict->proto,
(PyObject *)self, 0, (PyObject *)self, 0,
*(void **)self->b_ptr); *(void **)self->b_ptr);
...@@ -5137,7 +5154,7 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure) ...@@ -5137,7 +5154,7 @@ Pointer_set_contents(CDataObject *self, PyObject *value, void *closure)
return -1; return -1;
} }
stgdict = PyObject_stgdict((PyObject *)self); stgdict = PyObject_stgdict((PyObject *)self);
assert(stgdict); /* Cannot be NULL fr pointer instances */ assert(stgdict); /* Cannot be NULL for pointer instances */
assert(stgdict->proto); assert(stgdict->proto);
if (!CDataObject_Check(value)) { if (!CDataObject_Check(value)) {
int res = PyObject_IsInstance(value, stgdict->proto); int res = PyObject_IsInstance(value, stgdict->proto);
......
...@@ -205,7 +205,11 @@ PyCField_set(CFieldObject *self, PyObject *inst, PyObject *value) ...@@ -205,7 +205,11 @@ PyCField_set(CFieldObject *self, PyObject *inst, PyObject *value)
{ {
CDataObject *dst; CDataObject *dst;
char *ptr; char *ptr;
assert(CDataObject_Check(inst)); if (!CDataObject_Check(inst)) {
PyErr_SetString(PyExc_TypeError,
"not a ctype instance");
return -1;
}
dst = (CDataObject *)inst; dst = (CDataObject *)inst;
ptr = dst->b_ptr + self->offset; ptr = dst->b_ptr + self->offset;
if (value == NULL) { if (value == NULL) {
...@@ -225,7 +229,11 @@ PyCField_get(CFieldObject *self, PyObject *inst, PyTypeObject *type) ...@@ -225,7 +229,11 @@ PyCField_get(CFieldObject *self, PyObject *inst, PyTypeObject *type)
Py_INCREF(self); Py_INCREF(self);
return (PyObject *)self; return (PyObject *)self;
} }
assert(CDataObject_Check(inst)); if (!CDataObject_Check(inst)) {
PyErr_SetString(PyExc_TypeError,
"not a ctype instance");
return NULL;
}
src = (CDataObject *)inst; src = (CDataObject *)inst;
return PyCData_get(self->proto, self->getfunc, inst, return PyCData_get(self->proto, self->getfunc, inst,
self->index, self->size, src->b_ptr + self->offset); self->index, self->size, src->b_ptr + self->offset);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment