Skip to content

Commit 217d5a1

Browse files
vsajipshihai1991
authored andcommitted
bpo-16576: Add checks for bitfields passed by value to functions. (pythonGH-17097)
1 parent d486739 commit 217d5a1

File tree

3 files changed

+141
-3
lines changed

3 files changed

+141
-3
lines changed

Lib/ctypes/test/test_structures.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,87 @@ class Test5(Structure):
656656
self.assertEqual(test5.nested.an_int, 0)
657657
self.assertEqual(test5.another_int, 0)
658658

659+
#@unittest.skipIf('s390' in MACHINE, 'Test causes segfault on S390')
660+
def test_bitfield_by_value(self):
661+
# See bpo-16576
662+
663+
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
664+
665+
class Test6(Structure):
666+
_fields_ = [
667+
('A', c_int, 1),
668+
('B', c_int, 2),
669+
('C', c_int, 3),
670+
('D', c_int, 2),
671+
]
672+
673+
test6 = Test6()
674+
# As these are signed int fields, all are logically -1 due to sign
675+
# extension.
676+
test6.A = 1
677+
test6.B = 3
678+
test6.C = 7
679+
test6.D = 3
680+
dll = CDLL(_ctypes_test.__file__)
681+
with self.assertRaises(TypeError) as ctx:
682+
func = dll._testfunc_bitfield_by_value1
683+
func.restype = c_long
684+
func.argtypes = (Test6,)
685+
result = func(test6)
686+
self.assertEqual(ctx.exception.args[0], 'item 1 in _argtypes_ passes '
687+
'a struct/union with a bitfield by value, which is '
688+
'unsupported.')
689+
# passing by reference should be OK
690+
func = dll._testfunc_bitfield_by_reference1
691+
func.restype = c_long
692+
func.argtypes = (POINTER(Test6),)
693+
result = func(byref(test6))
694+
self.assertEqual(result, -4)
695+
self.assertEqual(test6.A, 0)
696+
self.assertEqual(test6.B, 0)
697+
self.assertEqual(test6.C, 0)
698+
self.assertEqual(test6.D, 0)
699+
700+
class Test7(Structure):
701+
_fields_ = [
702+
('A', c_uint, 1),
703+
('B', c_uint, 2),
704+
('C', c_uint, 3),
705+
('D', c_uint, 2),
706+
]
707+
test7 = Test7()
708+
test7.A = 1
709+
test7.B = 3
710+
test7.C = 7
711+
test7.D = 3
712+
func = dll._testfunc_bitfield_by_reference2
713+
func.restype = c_long
714+
func.argtypes = (POINTER(Test7),)
715+
result = func(byref(test7))
716+
self.assertEqual(result, 14)
717+
self.assertEqual(test7.A, 0)
718+
self.assertEqual(test7.B, 0)
719+
self.assertEqual(test7.C, 0)
720+
self.assertEqual(test7.D, 0)
721+
722+
# for a union with bitfields, the union check happens first
723+
class Test8(Union):
724+
_fields_ = [
725+
('A', c_int, 1),
726+
('B', c_int, 2),
727+
('C', c_int, 3),
728+
('D', c_int, 2),
729+
]
730+
731+
test8 = Test8()
732+
with self.assertRaises(TypeError) as ctx:
733+
func = dll._testfunc_bitfield_by_value2
734+
func.restype = c_long
735+
func.argtypes = (Test8,)
736+
result = func(test8)
737+
self.assertEqual(ctx.exception.args[0], 'item 1 in _argtypes_ passes '
738+
'a union by value, which is unsupported.')
739+
659740
class PointerMemberTestCase(unittest.TestCase):
660741

661742
def test(self):

Modules/_ctypes/_ctypes.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2400,11 +2400,18 @@ converters_from_argtypes(PyObject *ob)
24002400
}
24012401
return NULL;
24022402
}
2403-
/*
24042403
if (stgdict->flags & TYPEFLAG_HASBITFIELD) {
2405-
printf("found stgdict with bitfield\n");
2404+
Py_DECREF(converters);
2405+
Py_DECREF(ob);
2406+
if (!PyErr_Occurred()) {
2407+
PyErr_Format(PyExc_TypeError,
2408+
"item %zd in _argtypes_ passes a struct/"
2409+
"union with a bitfield by value, which is "
2410+
"unsupported.",
2411+
i + 1);
2412+
}
2413+
return NULL;
24062414
}
2407-
*/
24082415
}
24092416

24102417
if (_PyObject_LookupAttrId(tp, &PyId_from_param, &cnv) <= 0) {

Modules/_ctypes/_ctypes_test.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,56 @@ _testfunc_union_by_reference3(Test5 *in) {
194194
return result;
195195
}
196196

197+
typedef struct {
198+
signed int A: 1, B:2, C:3, D:2;
199+
} Test6;
200+
201+
EXPORT(long)
202+
_testfunc_bitfield_by_value1(Test6 in) {
203+
long result = in.A + in.B + in.C + in.D;
204+
205+
/* As the struct is passed by value, changes to it shouldn't be
206+
* reflected in the caller.
207+
*/
208+
memset(&in, 0, sizeof(in));
209+
return result;
210+
}
211+
212+
EXPORT(long)
213+
_testfunc_bitfield_by_reference1(Test6 *in) {
214+
long result = in->A + in->B + in->C + in->D;
215+
216+
memset(in, 0, sizeof(Test6));
217+
return result;
218+
}
219+
220+
typedef struct {
221+
unsigned int A: 1, B:2, C:3, D:2;
222+
} Test7;
223+
224+
EXPORT(long)
225+
_testfunc_bitfield_by_reference2(Test7 *in) {
226+
long result = in->A + in->B + in->C + in->D;
227+
228+
memset(in, 0, sizeof(Test7));
229+
return result;
230+
}
231+
232+
typedef union {
233+
signed int A: 1, B:2, C:3, D:2;
234+
} Test8;
235+
236+
EXPORT(long)
237+
_testfunc_bitfield_by_value2(Test8 in) {
238+
long result = in.A + in.B + in.C + in.D;
239+
240+
/* As the struct is passed by value, changes to it shouldn't be
241+
* reflected in the caller.
242+
*/
243+
memset(&in, 0, sizeof(in));
244+
return result;
245+
}
246+
197247
EXPORT(void)testfunc_array(int values[4])
198248
{
199249
printf("testfunc_array %d %d %d %d\n",

0 commit comments

Comments
 (0)