Skip to content
20 changes: 20 additions & 0 deletions Lib/ctypes/test/test_parameters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest
from ctypes.test import need_symbol
import test.support

class SimpleTypesTestCase(unittest.TestCase):

Expand Down Expand Up @@ -180,6 +181,25 @@ def test_abstract(self):
self.assertRaises(TypeError, _Pointer.from_param, 42)
self.assertRaises(TypeError, _SimpleCData.from_param, 42)

@test.support.cpython_only
def test_issue31311(self):
# __setstate__ should neither raise a SystemError nor crash in case
# of a bad __dict__.
from ctypes import Structure

class BadStruct(Structure):
def __dict__(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to use a property. This doesn't work in any case, don't affect instance's dict, but at least looks more realistic.

pass
with self.assertRaises(TypeError):
BadStruct().__setstate__({}, b'foo')

class WorseStruct(Structure):
@property
def __dict__(self):
raise ArithmeticError
with self.assertRaises(ArithmeticError):
WorseStruct().__setstate__({}, b'foo')

################################################################

if __name__ == '__main__':
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash in the ``__setstate__()`` method of `ctypes._CData`, in case of
a bad ``__dict__``. Patch by Oren Milman.
9 changes: 9 additions & 0 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2671,6 +2671,15 @@ PyCData_setstate(PyObject *myself, PyObject *args)
len = self->b_size;
memmove(self->b_ptr, data, len);
mydict = PyObject_GetAttrString(myself, "__dict__");
if (mydict == NULL) {
return NULL;
}
if (!PyDict_Check(mydict)) {
PyErr_SetString(PyExc_TypeError,
"__dict__ must be a dictionary");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an actual type of __dict__. Add a type of myself.

Py_DECREF(mydict);
return NULL;
}
res = PyDict_Update(mydict, dict);
Py_DECREF(mydict);
if (res == -1)
Expand Down