From 1087560ba56e234021d07f5427a05db0839a3f4f Mon Sep 17 00:00:00 2001 From: Pavel Nekrasov Date: Thu, 5 Nov 2026 15:10:57 +0300 Subject: [PATCH] Fix crash when subscripting a ctypes pointer without _type_ Check proto and the PyStgInfo_FromType() result instead of relying on assert() in Pointer_item_impl(), Pointer_ass_item_impl() and Pointer_subscript(). --- Lib/test/test_ctypes/test_pointers.py | 14 ++++++++++ Modules/_ctypes/_ctypes.c | 38 +++++++++++++++++++++------ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_ctypes/test_pointers.py b/Lib/test/test_ctypes/test_pointers.py index 78480af34474a54..5137f081482716f 100644 --- a/Lib/test/test_ctypes/test_pointers.py +++ b/Lib/test/test_ctypes/test_pointers.py @@ -414,6 +414,20 @@ class BadType(ctypes._Pointer): with self.assertRaises(ctypes.ArgumentError): func(object()) + def test_incomplete_pointer_subscript(self): + class BadType(ctypes._Pointer): + pass + + buf = bytearray(8) + buf[0] = 1 + obj = BadType.from_buffer(buf) + with self.assertRaisesRegex(TypeError, "has no _type_"): + obj[0:1] + with self.assertRaisesRegex(TypeError, "has no _type_"): + obj[0] + with self.assertRaisesRegex(TypeError, "has no _type_"): + obj[0] = 0 + class PointerTypeCacheTestCase(unittest.TestCase): # dummy tests to check warnings and base behavior def tearDown(self): diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 56d39e29f140dd0..209d0897c50ec5b 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -5665,14 +5665,21 @@ Pointer_item_impl(PyObject *myself, Py_ssize_t index) assert(stginfo); /* Cannot be NULL for pointer object instances */ proto = stginfo->proto; - assert(proto); + if (proto == NULL) { + PyErr_SetString(PyExc_TypeError, + "Cannot access items: has no _type_"); + return NULL; + } StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { return NULL; } - assert(iteminfo); /* proto is the item type of the pointer, a ctypes - type, so this cannot be NULL */ + if (iteminfo == NULL) { + PyErr_SetString(PyExc_SystemError, + "pointer _type_ has no storage info"); + return NULL; + } size = iteminfo->size; offset = index * iteminfo->size; @@ -5722,14 +5729,21 @@ Pointer_ass_item_impl(PyObject *myself, Py_ssize_t index, PyObject *value) assert(stginfo); /* Cannot be NULL for pointer instances */ proto = stginfo->proto; - assert(proto); + if (proto == NULL) { + PyErr_SetString(PyExc_TypeError, + "Cannot access items: has no _type_"); + return -1; + } StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { return -1; } - assert(iteminfo); /* Cannot be NULL because the itemtype of a pointer - is always a ctypes type */ + if (iteminfo == NULL) { + PyErr_SetString(PyExc_SystemError, + "pointer _type_ has no storage info"); + return -1; + } size = iteminfo->size; offset = index * iteminfo->size; @@ -5957,12 +5971,20 @@ Pointer_subscript(PyObject *myself, PyObject *item) } assert(stginfo); /* Cannot be NULL for pointer instances */ proto = stginfo->proto; - assert(proto); + if (proto == NULL) { + PyErr_SetString(PyExc_TypeError, + "Cannot access items: has no _type_"); + return NULL; + } StgInfo *iteminfo; if (PyStgInfo_FromType(st, proto, &iteminfo) < 0) { return NULL; } - assert(iteminfo); + if (iteminfo == NULL) { + PyErr_SetString(PyExc_SystemError, + "pointer _type_ has no storage info"); + return NULL; + } if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) { char *dest;