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;