From 83c02fbab58ff09168912fc97dff2de91f437118 Mon Sep 17 00:00:00 2001 From: Rinku Rajole Date: Thu, 5 Nov 2026 15:15:42 +0530 Subject: [PATCH 1/2] gh-161390: Reject non-ctypes objects assigned to pointer contents --- Lib/test/test_ctypes/test_pointers.py | 13 +++++++++++++ ...026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst | 4 ++++ Modules/_ctypes/_ctypes.c | 16 ++++++---------- 3 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst diff --git a/Lib/test/test_ctypes/test_pointers.py b/Lib/test/test_ctypes/test_pointers.py index 78480af34474a54..eb3a5fdaa188c1e 100644 --- a/Lib/test/test_ctypes/test_pointers.py +++ b/Lib/test/test_ctypes/test_pointers.py @@ -98,6 +98,19 @@ def test_change_pointers(self): x.value = -99 self.assertEqual(res.contents.value, -99) + def test_contents_rejects_spoofed_class(self): + # gh-161390: __class__ must not make a non-ctypes object look valid. + class Forge: + __slots__ = ("a", "b", "c", "d") + __class__ = c_int + + def __init__(self): + self.a = c_int(7) + self.b = self.c = self.d = None + + with self.assertRaises(TypeError): + pointer(c_int(1)).contents = Forge() + def test_callbacks_with_pointers(self): # a function type receiving a pointer PROTOTYPE = CFUNCTYPE(c_int, POINTER(c_int)) diff --git a/Misc/NEWS.d/next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst b/Misc/NEWS.d/next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst new file mode 100644 index 000000000000000..226b9530a4bf726 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst @@ -0,0 +1,4 @@ +Assigning to the :attr:`contents` attribute of a :mod:`ctypes` pointer now +raises :exc:`TypeError` when the value is not a ctypes object. A value +whose ``__class__`` claimed to be a ctypes type could previously crash +the interpreter. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 56d39e29f140dd0..ce53a7a70ad7522 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5785,16 +5785,12 @@ Pointer_set_contents_lock_held(PyObject *op, PyObject *value, void *closure) assert(stginfo); /* Cannot be NULL for pointer instances */ assert(stginfo->proto); if (!CDataObject_Check(st, value)) { - int res = PyObject_IsInstance(value, stginfo->proto); - if (res == -1) - return -1; - if (!res) { - PyErr_Format(PyExc_TypeError, - "expected %s instead of %s", - ((PyTypeObject *)(stginfo->proto))->tp_name, - Py_TYPE(value)->tp_name); - return -1; - } + /* PyObject_IsInstance() follows a spoofed __class__ (gh-161390). */ + PyErr_Format(PyExc_TypeError, + "expected %s instead of %s", + ((PyTypeObject *)(stginfo->proto))->tp_name, + Py_TYPE(value)->tp_name); + return -1; } dst = (CDataObject *)value; From 9815024cdd6dc69daef60e90c26bef3b1e147311 Mon Sep 17 00:00:00 2001 From: Rinku Rajole Date: Thu, 5 Nov 2026 17:43:11 +0530 Subject: [PATCH 2/2] gh-161390: Avoid an unresolved contents reference in the NEWS entry --- .../next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst b/Misc/NEWS.d/next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst index 226b9530a4bf726..d1f9c18253a5755 100644 --- a/Misc/NEWS.d/next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst +++ b/Misc/NEWS.d/next/Library/2026-11-05-15-20-00.gh-issue-161390.p8Kq2m.rst @@ -1,4 +1,4 @@ -Assigning to the :attr:`contents` attribute of a :mod:`ctypes` pointer now +Assigning to the ``contents`` attribute of a :mod:`ctypes` pointer now raises :exc:`TypeError` when the value is not a ctypes object. A value whose ``__class__`` claimed to be a ctypes type could previously crash the interpreter.