8000 bpo-16575: Add checks for unions passed by value to functions. (GH-16… · python/cpython@79d4ed1 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit 79d4ed1

Browse files
authored
bpo-16575: Add checks for unions passed by value to functions. (GH-16799)
1 parent bdac32e commit 79d4ed1

File tree

5 files changed

+178
-0
lines changed

5 files changed

+178
-0
lines changed

Lib/ctypes/test/test_structures.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,86 @@ class U(Union):
576576
self.assertEqual(f2, [0x4567, 0x0123, 0xcdef, 0x89ab,
577577
0x3210, 0x7654, 0xba98, 0xfedc])
578578

579+
def test_union_by_value(self):
580+
# See bpo-16575
581+
582+
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
583+
584+
class Nested1(Structure):
585+
_fields_ = [
586+
('an_int', c_int),
587+
('another_int', c_int),
588+
]
589+
590+
class Test4(Union):
591+
_fields_ = [
592+
('a_long', c_long),
593+
('a_struct', Nested1),
594+
]
595+
596+
class Nested2(Structure):
597+
_fields_ = [
598+
('an_int', c_int),
599+
('a_union', Test4),
600+
]
601+
602+
class Test5(Structure):
603+
_fields_ = [
604+
('an_int', c_int),
605+
('nested', Nested2),
606+
('another_int', c_int),
607+
]
608+
609+
test4 = Test4()
610+
dll = CDLL(_ctypes_test.__file__)
611+
with self.assertRaises(TypeError) as ctx:
612+
func = dll._testfunc_union_by_value1
613+
func.restype = c_long
614+
func.argtypes = (Test4,)
615+
result = func(test4)
616+
self.assertEqual(ctx.exception.args[0], 'item 1 in _argtypes_ passes '
617+
'a union by value, which is unsupported.')
618+
test5 = Test5()
619+
with self.assertRaises(TypeError) as ctx:
620+
func = dll._testfunc_union_by_value2
621+
func.restype = c_long
622+
func.argtypes = (Test5,)
623+
result = func(test5)
624+
self.assertEqual(ctx.exception.args[0], 'item 1 in _argtypes_ passes '
625+
'a union by value, which is unsupported.')
626+
627+
# passing by reference should be OK
628+
test4.a_long = 12345;
629+
func = dll._testfunc_union_by_reference1
630+
func.restype = c_long
631+
func.argtypes = (POINTER(Test4),)
632+
result = func(byref(test4))
633+
self.assertEqual(result, 12345)
634+
self.assertEqual(test4.a_long, 0)
635+
self.assertEqual(test4.a_struct.an_int, 0)
636+
self.assertEqual(test4.a_struct.another_int, 0)
637+
test4.a_struct.an_int = 0x12340000
638+
test4.a_struct.another_int = 0x5678
639+
func = dll._testfunc_union_by_reference2
640+
func.restype = c_long
641+
func.argtypes = (POINTER(Test4),)
642+
result = func(byref(test4))
643+
self.assertEqual(result, 0x12345678)
644+
self.assertEqual(test4.a_long, 0)
645+
self.assertEqual(test4.a_struct.an_int, 0)
646+
self.assertEqual(test4.a_struct.another_int, 0)
647+
test5.an_int = 0x12000000
648+
test5.nested.an_int = 0x345600
649+
test5.another_int = 0x78
650+
func = dll._testfunc_union_by_reference3
651+
func.restype = c_long
652+
func.argtypes = (POINTER(Test5),)
653+
result = func(byref(test5))
654+
self.assertEqual(result, 0x12345678)
655+
self.assertEqual(test5.an_int, 0)
656+
self.assertEqual(test5.nested.an_int, 0)
657+
self.assertEqual(test5.another_int, 0)
658+
579659
class PointerMemberTestCase(unittest.TestCase):
580660

581661
def test(self):

Modules/_ctypes/_ctypes.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,9 @@ StructUnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds, int isSt
504504
Py_DECREF(result);
505505
return NULL;
506506
}
507+
if 2364 (!isStruct) {
508+
dict->flags |= TYPEFLAG_HASUNION;
509+
}
507510
/* replace the class dict by our updated stgdict, which holds info
508511
about storage requirements of the instances */
509512
if (-1 == PyDict_Update((PyObject *)dict, result->tp_dict)) {
@@ -2383,6 +2386,27 @@ converters_from_argtypes(PyObject *ob)
23832386
for (i = 0; i < nArgs; ++i) {
23842387
PyObject *cnv;
23852388
PyObject *tp = PyTuple_GET_ITEM(ob, i);
2389+
StgDictObject *stgdict = PyType_stgdict(tp);
2390+
2391+
if (stgdict != NULL) {
2392+
if (stgdict->flags & TYPEFLAG_HASUNION) {
2393+
Py_DECREF(converters);
2394+
Py_DECREF(ob);
2395+
if (!PyErr_Occurred()) {
2396+
PyErr_Format(PyExc_TypeError,
2397+
"item %zd in _argtypes_ passes a union by "
2398+
"value, which is unsupported.",
2399+
i + 1);
2400+
}
2401+
return NULL;
2402+
}
2403+
/*
2404+
if (stgdict->flags & TYPEFLAG_HASBITFIELD) {
2405+
printf("found stgdict with bitfield\n");
2406+
}
2407+
*/
2408+
}
2409+
23862410
if (_PyObject_LookupAttrId(tp, &PyId_from_param, &cnv) <= 0) {
23872411
Py_DECREF(converters);
23882412
Py_DECREF(ob);

Modules/_ctypes/_ctypes_test.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,69 @@ _testfunc_array_in_struct2a(Test3B in)
131131
return result;
132132
}
133133

134+
typedef union {
135+
long a_long;
136+
struct {
137+
int an_int;
138+
int another_int;
139+
} a_struct;
140+
} Test4;
141+
142+
typedef struct {
143+
int an_int;
144+
struct {
145+
int an_int;
146+
Test4 a_union;
147+
} nested;
148+
int another_int;
149+
} Test5;
150+
151+
EXPORT(long)
152+
_testfunc_union_by_value1(Test4 in) {
153+
long result = in.a_long + in.a_struct.an_int + in.a_struct.another_int;
154+
155+
/* As the union/struct are passed by value, changes to them shouldn't be
156+
* reflected in the caller.
157+
*/
158+
memset(&in, 0, sizeof(in));
159+
return result;
160+
}
161+
162+
EXPORT(long)
163+
_testfunc_union_by_value2(Test5 in) {
164+
long result = in.an_int + in.nested.an_int;
165+
166+
/* As the union/struct are passed by value, changes to them shouldn't be
167+
* reflected in the caller.
168+
*/
169+
memset(&in, 0, sizeof(in));
170+
return result;
171+
}
172+
173+
EXPORT(long)
174+
_testfunc_union_by_reference1(Test4 *in) {
175+
long result = in->a_long;
176+
177+
memset(in, 0, sizeof(Test4));
178+
return result;
179+
}
180+
181+
EXPORT(long)
182+
_testfunc_union_by_reference2(Test4 *in) {
183+
long result = in->a_struct.an_int + in->a_struct.another_int;
184+
185+
memset(in, 0, sizeof(Test4));
186+
return result;
187+
}
188+
189+
EXPORT(long)
190+
_testfunc_union_by_reference3(Test5 *in) {
191+
long result = in->an_int + in->nested.an_int + in->another_int;
192+
193+
memset(in, 0, sizeof(Test5));
194+
return result;
195+
}
196+
134197
EXPORT(void)testfunc_array(int values[4])
135198
{
136199
printf("testfunc_array %d %d %d %d\n",

Modules/_ctypes/ctypes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ PyObject *_ctypes_callproc(PPROC pProc,
288288

289289
#define TYPEFLAG_ISPOINTER 0x100
290290
#define TYPEFLAG_HASPOINTER 0x200
291+
#define TYPEFLAG_HASUNION 0x400
292+
#define TYPEFLAG_HASBITFIELD 0x800
291293

292294
#define DICTFLAG_FINAL 0x1000
293295

Modules/_ctypes/stgdict.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,13 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
440440
PyMem_Free(stgdict->ffi_type_pointer.elements);
441441

442442
basedict = PyType_stgdict((PyObject *)((PyTypeObject *)type)->tp_base);
443+
if (basedict) {
444+
stgdict->flags |= (basedict->flags &
445+
(TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD));
446+
}
447+
if (!isStruct) {
448+
stgdict->flags |= TYPEFLAG_HASUNION;
449+
}
443450
if (basedict && !use_broken_old_ctypes_semantics) {
444451
size = offset = basedict->size;
445452
align = basedict->align;
@@ -515,8 +522,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
515522
stgdict->ffi_type_pointer.elements[ffi_ofs + i] = &dict->ffi_type_pointer;
516523
if (dict->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
517524
stgdict->flags |= TYPEFLAG_HASPOINTER;
525+
stgdict->flags |= dict->flags & (TYPEFLAG_HASUNION | TYPEFLAG_HASBITFIELD);
518526
dict->flags |= DICTFLAG_FINAL; /* mark field type final */
519527
if (PyTuple_Size(pair) == 3) { /* bits specified */
528+
stgdict->flags |= TYPEFLAG_HASBITFIELD;
520529
switch(dict->ffi_type_pointer.type) {
521530
case FFI_TYPE_UINT8:
522531
case FFI_TYPE_UINT16:

0 commit comments

Comments
 (0)
You can’t perform that action at this time.
0