Skip to content

Commit 56903c5

Browse files
gh-143005: disallow incompatible __class__ reassignment for ctypes arrays
1 parent abfe97e commit 56903c5

2 files changed

Lines changed: 54 additions & 5 deletions

File tree

Lib/test/test_ctypes/test_arrays.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from ctypes import (Structure, Array, ARRAY, sizeof, addressof,
55
create_string_buffer, create_unicode_buffer,
66
c_char, c_wchar, c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint,
7-
c_long, c_ulonglong, c_float, c_double, c_longdouble)
7+
c_long, c_ulonglong, c_float, c_double, c_longdouble, POINTER)
88
from test.support import bigmemtest, _2G, threading_helper, Py_GIL_DISABLED
99
from ._support import (_CData, PyCArrayType, Py_TPFLAGS_DISALLOW_INSTANTIATION,
1010
Py_TPFLAGS_IMMUTABLETYPE)
@@ -111,6 +111,46 @@ def test_ctypes_array_class_assignment_incompatible(self):
111111
with self.assertRaises(TypeError):
112112
x.__class__ = B
113113

114+
def test_ctypes_array_class_assignment_abstract_target(self):
115+
class AbstractArray(Array):
116+
pass
117+
A = c_int * 3
118+
a = A()
119+
120+
with self.assertRaises(TypeError):
121+
a.__class__ = AbstractArray
122+
123+
def test_ctypes_array_class_assignment_non_array_instance(self):
124+
p = POINTER(c_int)()
125+
A = c_int * 3
126+
127+
with self.assertRaises(TypeError):
128+
p.__class__ = A
129+
130+
def test_ctypes_array_class_assignment_zero_length(self):
131+
A = c_long * 0
132+
B = c_long * 1
133+
a = A()
134+
135+
with self.assertRaises(TypeError):
136+
a.__class__ = B
137+
138+
def test_ctypes_array_class_assignment_incompatible_element_type(self):
139+
A = c_int * 3
140+
B = c_double * 3
141+
a = A()
142+
143+
with self.assertRaises(TypeError):
144+
a.__class__ = B
145+
146+
def test_ctypes_array_class_assignment_signed_unsigned(self):
147+
A = c_long * 3
148+
B = c_ulonglong * 3
149+
a = A()
150+
151+
with self.assertRaises(TypeError):
152+
a.__class__ = B
153+
114154
def test_step_overflow(self):
115155
a = (c_int * 5)()
116156
a[3::sys.maxsize] = (1,)

Modules/_ctypes/_ctypes.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5018,11 +5018,11 @@ static int
50185018
PyCArray_setattro(PyObject *self, PyObject *key, PyObject *value)
50195019
{
50205020
if (PyUnicode_Check(key) &&
5021-
PyUnicode_CompareWithASCIIString(key, "__class__") == 0)
5021+
_PyUnicode_EqualToASCIIString(key, "__class__"))
50225022
{
50235023
ctypes_state *st = get_module_state_by_def(Py_TYPE(Py_TYPE(self)));
5024-
StgInfo *old_info;
5025-
StgInfo *new_info;
5024+
StgInfo *old_info = NULL;
5025+
StgInfo *new_info = NULL;
50265026

50275027
if (PyStgInfo_FromObject(st, self, &old_info) < 0) {
50285028
return -1;
@@ -5031,7 +5031,16 @@ PyCArray_setattro(PyObject *self, PyObject *key, PyObject *value)
50315031
return -1;
50325032
}
50335033

5034-
/* Only care about array → array */
5034+
/* If one side has no storage info, disallow */
5035+
if (old_info == NULL || new_info == NULL) {
5036+
PyErr_SetString(
5037+
PyExc_TypeError,
5038+
"cannot assign incompatible ctypes array type"
5039+
);
5040+
return -1;
5041+
}
5042+
5043+
/* Only care about array to array */
50355044
if (old_info->length >= 0 && new_info->length >= 0) {
50365045
if (old_info->length != new_info->length ||
50375046
old_info->size != new_info->size ||

0 commit comments

Comments
 (0)