Skip to content

Commit 83c02fb

Browse files
committed
gh-161390: Reject non-ctypes objects assigned to pointer contents
1 parent 5aa2f70 commit 83c02fb

3 files changed

Lines changed: 23 additions & 10 deletions

File tree

‎Lib/test/test_ctypes/test_pointers.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,19 @@ def test_change_pointers(self):
9898
x.value = -99
9999
self.assertEqual(res.contents.value, -99)
100100

101+
def test_contents_rejects_spoofed_class(self):
102+
# gh-161390: __class__ must not make a non-ctypes object look valid.
103+
class Forge:
104+
__slots__ = ("a", "b", "c", "d")
105+
__class__ = c_int
106+
107+
def __init__(self):
108+
self.a = c_int(7)
109+
self.b = self.c = self.d = None
110+
111+
with self.assertRaises(TypeError):
112+
pointer(c_int(1)).contents = Forge()
113+
101114
def test_callbacks_with_pointers(self):
102115
# a function type receiving a pointer
103116
PROTOTYPE = CFUNCTYPE(c_int, POINTER(c_int))
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Assigning to the :attr:`contents` attribute of a :mod:`ctypes` pointer now
2+
raises :exc:`TypeError` when the value is not a ctypes object. A value
3+
whose ``__class__`` claimed to be a ctypes type could previously crash
4+
the interpreter.

‎Modules/_ctypes/_ctypes.c‎

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5785,16 +5785,12 @@ Pointer_set_contents_lock_held(PyObject *op, PyObject *value, void *closure)
57855785
assert(stginfo); /* Cannot be NULL for pointer instances */
57865786
assert(stginfo->proto);
57875787
if (!CDataObject_Check(st, value)) {
5788-
int res = PyObject_IsInstance(value, stginfo->proto);
5789-
if (res == -1)
5790-
return -1;
5791-
if (!res) {
5792-
PyErr_Format(PyExc_TypeError,
5793-
"expected %s instead of %s",
5794-
((PyTypeObject *)(stginfo->proto))->tp_name,
5795-
Py_TYPE(value)->tp_name);
5796-
return -1;
5797-
}
5788+
/* PyObject_IsInstance() follows a spoofed __class__ (gh-161390). */
5789+
PyErr_Format(PyExc_TypeError,
5790+
"expected %s instead of %s",
5791+
((PyTypeObject *)(stginfo->proto))->tp_name,
5792+
Py_TYPE(value)->tp_name);
5793+
return -1;
57985794
}
57995795

58005796
dst = (CDataObject *)value;

0 commit comments

Comments
 (0)