From d17521c769b59c602ec5028a57fe390f4a3da60a Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Fri, 16 Jan 2026 19:29:15 +0530 Subject: [PATCH 1/3] gh-143005: prevent incompatible __class__ reassignment for ctypes arrays --- Lib/test/test_ctypes/test_arrays.py | 8 ++++ Modules/_ctypes/_ctypes.c | 58 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/Lib/test/test_ctypes/test_arrays.py b/Lib/test/test_ctypes/test_arrays.py index 7f1f6cf58402c9..52eefcb4d0395b 100644 --- a/Lib/test/test_ctypes/test_arrays.py +++ b/Lib/test/test_ctypes/test_arrays.py @@ -102,6 +102,14 @@ def test_simple(self): # cannot delete items with self.assertRaises(TypeError): del ca[0] + + def test_ctypes_array_class_assignment_incompatible(self): + A = c_long * 3 + B = c_long * 5 + x = A(1, 2, 3) + + with self.assertRaises(TypeError): + x.__class__ = B def test_step_overflow(self): a = (c_int * 5)() diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index 563e95a762599b..58a639be308d8f 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -1097,6 +1097,28 @@ CDataType_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value) return Py_NewRef(value); } ctypes_state *st = get_module_state_by_class(cls); + + /* Disallow incompatible ctypes array __class__ reassignment */ + StgInfo *old_info = NULL; + StgInfo *new_info = NULL; + if (PyStgInfo_FromObject(st, value, &old_info) == 0 && + PyStgInfo_FromType(st, type, &new_info) == 0 && + old_info != NULL && + new_info != NULL && + old_info->length >= 0 && + new_info->length >= 0) + { + if (old_info->length != new_info->length || + old_info->size != new_info->size || + old_info->proto != new_info->proto) + { + PyErr_SetString( + PyExc_TypeError, + "cannot assign incompatible ctypes array type" + ); + return NULL; + } + } if (PyCArg_CheckExact(st, value)) { PyCArgObject *p = (PyCArgObject *)value; PyObject *ob = p->obj; @@ -4992,6 +5014,41 @@ Array_init(PyObject *self, PyObject *args, PyObject *kw) return 0; } +static int +PyCArray_setattro(PyObject *self, PyObject *key, PyObject *value) +{ + if (PyUnicode_Check(key) && + PyUnicode_CompareWithASCIIString(key, "__class__") == 0) + { + ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self))); + StgInfo *old_info; + StgInfo *new_info; + + if (PyStgInfo_FromObject(st, self, &old_info) < 0) { + return -1; + } + if (PyStgInfo_FromType(st, value, &new_info) < 0) { + return -1; + } + + /* Only care about array → array */ + if (old_info->length >= 0 && new_info->length >= 0) { + if (old_info->length != new_info->length || + old_info->size != new_info->size || + old_info->proto != new_info->proto) + { + PyErr_SetString( + PyExc_TypeError, + "cannot assign incompatible ctypes array type" + ); + return -1; + } + } + } + + return PyObject_GenericSetAttr(self, key, value); +} + static PyObject * Array_item_lock_held(PyObject *myself, Py_ssize_t index) { @@ -5310,6 +5367,7 @@ static PyType_Slot pycarray_slots[] = { {Py_mp_length, Array_length}, {Py_mp_subscript, Array_subscript}, {Py_mp_ass_subscript, Array_ass_subscript}, + {Py_tp_setattro, PyCArray_setattro}, {0, NULL}, }; From 670c7a17589e13d6eab19e980b3f84c21642d643 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Fri, 16 Jan 2026 19:38:55 +0530 Subject: [PATCH 2/3] gh-143005: trim trailing whitespace --- Lib/test/test_ctypes/test_arrays.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_ctypes/test_arrays.py b/Lib/test/test_ctypes/test_arrays.py index 52eefcb4d0395b..6002cf88a9a0f4 100644 --- a/Lib/test/test_ctypes/test_arrays.py +++ b/Lib/test/test_ctypes/test_arrays.py @@ -102,12 +102,12 @@ def test_simple(self): # cannot delete items with self.assertRaises(TypeError): del ca[0] - + def test_ctypes_array_class_assignment_incompatible(self): A = c_long * 3 B = c_long * 5 x = A(1, 2, 3) - + with self.assertRaises(TypeError): x.__class__ = B From 32fac412e380ea225ba91806eb244f71db344887 Mon Sep 17 00:00:00 2001 From: VanshAgarwal24036 Date: Sat, 17 Jan 2026 16:37:03 +0530 Subject: [PATCH 3/3] News added --- .../next/Library/2026-01-17-16-35-57.gh-issue-143005.WKf6GL.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-01-17-16-35-57.gh-issue-143005.WKf6GL.rst diff --git a/Misc/NEWS.d/next/Library/2026-01-17-16-35-57.gh-issue-143005.WKf6GL.rst b/Misc/NEWS.d/next/Library/2026-01-17-16-35-57.gh-issue-143005.WKf6GL.rst new file mode 100644 index 00000000000000..64895e225585fb --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-17-16-35-57.gh-issue-143005.WKf6GL.rst @@ -0,0 +1,2 @@ +Fix a memory safety issue in ctypes arrays by rejecting ``__class__`` +assignment to incompatible array types.