Skip to content

Commit d17521c

Browse files
gh-143005: prevent incompatible __class__ reassignment for ctypes arrays
1 parent ae53da5 commit d17521c

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

Lib/test/test_ctypes/test_arrays.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ def test_simple(self):
102102
# cannot delete items
103103
with self.assertRaises(TypeError):
104104
del ca[0]
105+
106+
def test_ctypes_array_class_assignment_incompatible(self):
107+
A = c_long * 3
108+
B = c_long * 5
109+
x = A(1, 2, 3)
110+
111+
with self.assertRaises(TypeError):
112+
x.__class__ = B
105113

106114
def test_step_overflow(self):
107115
a = (c_int * 5)()

Modules/_ctypes/_ctypes.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,28 @@ CDataType_from_param_impl(PyObject *type, PyTypeObject *cls, PyObject *value)
10971097
return Py_NewRef(value);
10981098
}
10991099
ctypes_state *st = get_module_state_by_class(cls);
1100+
1101+
/* Disallow incompatible ctypes array __class__ reassignment */
1102+
StgInfo *old_info = NULL;
1103+
StgInfo *new_info = NULL;
1104+
if (PyStgInfo_FromObject(st, value, &old_info) == 0 &&
1105+
PyStgInfo_FromType(st, type, &new_info) == 0 &&
1106+
old_info != NULL &&
1107+
new_info != NULL &&
1108+
old_info->length >= 0 &&
1109+
new_info->length >= 0)
1110+
{
1111+
if (old_info->length != new_info->length ||
1112+
old_info->size != new_info->size ||
1113+
old_info->proto != new_info->proto)
1114+
{
1115+
PyErr_SetString(
1116+
PyExc_TypeError,
1117+
"cannot assign incompatible ctypes array type"
1118+
);
1119+
return NULL;
1120+
}
1121+
}
11001122
if (PyCArg_CheckExact(st, value)) {
11011123
PyCArgObject *p = (PyCArgObject *)value;
11021124
PyObject *ob = p->obj;
@@ -4992,6 +5014,41 @@ Array_init(PyObject *self, PyObject *args, PyObject *kw)
49925014
return 0;
49935015
}
49945016

5017+
static int
5018+
PyCArray_setattro(PyObject *self, PyObject *key, PyObject *value)
5019+
{
5020+
if (PyUnicode_Check(key) &&
5021+
PyUnicode_CompareWithASCIIString(key, "__class__") == 0)
5022+
{
5023+
ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self)));
5024+
StgInfo *old_info;
5025+
StgInfo *new_info;
5026+
5027+
if (PyStgInfo_FromObject(st, self, &old_info) < 0) {
5028+
return -1;
5029+
}
5030+
if (PyStgInfo_FromType(st, value, &new_info) < 0) {
5031+
return -1;
5032+
}
5033+
5034+
/* Only care about array → array */
5035+
if (old_info->length >= 0 && new_info->length >= 0) {
5036+
if (old_info->length != new_info->length ||
5037+
old_info->size != new_info->size ||
5038+
old_info->proto != new_info->proto)
5039+
{
5040+
PyErr_SetString(
5041+
PyExc_TypeError,
5042+
"cannot assign incompatible ctypes array type"
5043+
);
5044+
return -1;
5045+
}
5046+
}
5047+
}
5048+
5049+
return PyObject_GenericSetAttr(self, key, value);
5050+
}
5051+
49955052
static PyObject *
49965053
Array_item_lock_held(PyObject *myself, Py_ssize_t index)
49975054
{
@@ -5310,6 +5367,7 @@ static PyType_Slot pycarray_slots[] = {
53105367
{Py_mp_length, Array_length},
53115368
{Py_mp_subscript, Array_subscript},
53125369
{Py_mp_ass_subscript, Array_ass_subscript},
5370+
{Py_tp_setattro, PyCArray_setattro},
53135371
{0, NULL},
53145372
};
53155373

0 commit comments

Comments
 (0)