bpo-16576: Add checks for bitfields passed by value to functions. (GH-17097)
This commit is contained in:
parent
c8b53dc3d8
commit
106271568c
|
@ -656,6 +656,87 @@ class StructureTestCase(unittest.TestCase):
|
|||
self.assertEqual(test5.nested.an_int, 0)
|
||||
self.assertEqual(test5.another_int, 0)
|
||||
|
||||
#@unittest.skipIf('s390' in MACHINE, 'Test causes segfault on S390')
|
||||
def test_bitfield_by_value(self):
|
||||
# See bpo-16576
|
||||
|
||||
# These should mirror the structures in Modules/_ctypes/_ctypes_test.c
|
||||
|
||||
class Test6(Structure):
|
||||
_fields_ = [
|
||||
('A', c_int, 1),
|
||||
('B', c_int, 2),
|
||||
('C', c_int, 3),
|
||||
('D', c_int, 2),
|
||||
]
|
||||
|
||||
test6 = Test6()
|
||||
# As these are signed int fields, all are logically -1 due to sign
|
||||
# extension.
|
||||
test6.A = 1
|
||||
test6.B = 3
|
||||
test6.C = 7
|
||||
test6.D = 3
|
||||
dll = CDLL(_ctypes_test.__file__)
|
||||
with self.assertRaises(TypeError) as ctx:
|
||||
func = dll._testfunc_bitfield_by_value1
|
||||
func.restype = c_long
|
||||
func.argtypes = (Test6,)
|
||||
result = func(test6)
|
||||
self.assertEqual(ctx.exception.args[0], 'item 1 in _argtypes_ passes '
|
||||
'a struct/union with a bitfield by value, which is '
|
||||
'unsupported.')
|
||||
# passing by reference should be OK
|
||||
func = dll._testfunc_bitfield_by_reference1
|
||||
func.restype = c_long
|
||||
func.argtypes = (POINTER(Test6),)
|
||||
result = func(byref(test6))
|
||||
self.assertEqual(result, -4)
|
||||
self.assertEqual(test6.A, 0)
|
||||
self.assertEqual(test6.B, 0)
|
||||
self.assertEqual(test6.C, 0)
|
||||
self.assertEqual(test6.D, 0)
|
||||
|
||||
class Test7(Structure):
|
||||
_fields_ = [
|
||||
('A', c_uint, 1),
|
||||
('B', c_uint, 2),
|
||||
('C', c_uint, 3),
|
||||
('D', c_uint, 2),
|
||||
]
|
||||
test7 = Test7()
|
||||
test7.A = 1
|
||||
test7.B = 3
|
||||
test7.C = 7
|
||||
test7.D = 3
|
||||
func = dll._testfunc_bitfield_by_reference2
|
||||
func.restype = c_long
|
||||
func.argtypes = (POINTER(Test7),)
|
||||
result = func(byref(test7))
|
||||
self.assertEqual(result, 14)
|
||||
self.assertEqual(test7.A, 0)
|
||||
self.assertEqual(test7.B, 0)
|
||||
self.assertEqual(test7.C, 0)
|
||||
self.assertEqual(test7.D, 0)
|
||||
|
||||
# for a union with bitfields, the union check happens first
|
||||
class Test8(Union):
|
||||
_fields_ = [
|
||||
('A', c_int, 1),
|
||||
('B', c_int, 2),
|
||||
('C', c_int, 3),
|
||||
('D', c_int, 2),
|
||||
]
|
||||
|
||||
test8 = Test8()
|
||||
with self.assertRaises(TypeError) as ctx:
|
||||
func = dll._testfunc_bitfield_by_value2
|
||||
func.restype = c_long
|
||||
func.argtypes = (Test8,)
|
||||
result = func(test8)
|
||||
self.assertEqual(ctx.exception.args[0], 'item 1 in _argtypes_ passes '
|
||||
'a union by value, which is unsupported.')
|
||||
|
||||
class PointerMemberTestCase(unittest.TestCase):
|
||||
|
||||
def test(self):
|
||||
|
|
|
@ -2400,11 +2400,18 @@ converters_from_argtypes(PyObject *ob)
|
|||
}
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
if (stgdict->flags & TYPEFLAG_HASBITFIELD) {
|
||||
printf("found stgdict with bitfield\n");
|
||||
Py_DECREF(converters);
|
||||
Py_DECREF(ob);
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"item %zd in _argtypes_ passes a struct/"
|
||||
"union with a bitfield by value, which is "
|
||||
"unsupported.",
|
||||
i + 1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (_PyObject_LookupAttrId(tp, &PyId_from_param, &cnv) <= 0) {
|
||||
|
|
|
@ -194,6 +194,56 @@ _testfunc_union_by_reference3(Test5 *in) {
|
|||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
signed int A: 1, B:2, C:3, D:2;
|
||||
} Test6;
|
||||
|
||||
EXPORT(long)
|
||||
_testfunc_bitfield_by_value1(Test6 in) {
|
||||
long result = in.A + in.B + in.C + in.D;
|
||||
|
||||
/* As the struct is passed by value, changes to it shouldn't be
|
||||
* reflected in the caller.
|
||||
*/
|
||||
memset(&in, 0, sizeof(in));
|
||||
return result;
|
||||
}
|
||||
|
||||
EXPORT(long)
|
||||
_testfunc_bitfield_by_reference1(Test6 *in) {
|
||||
long result = in->A + in->B + in->C + in->D;
|
||||
|
||||
memset(in, 0, sizeof(Test6));
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned int A: 1, B:2, C:3, D:2;
|
||||
} Test7;
|
||||
|
||||
EXPORT(long)
|
||||
_testfunc_bitfield_by_reference2(Test7 *in) {
|
||||
long result = in->A + in->B + in->C + in->D;
|
||||
|
||||
memset(in, 0, sizeof(Test7));
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef union {
|
||||
signed int A: 1, B:2, C:3, D:2;
|
||||
} Test8;
|
||||
|
||||
EXPORT(long)
|
||||
_testfunc_bitfield_by_value2(Test8 in) {
|
||||
long result = in.A + in.B + in.C + in.D;
|
||||
|
||||
/* As the struct is passed by value, changes to it shouldn't be
|
||||
* reflected in the caller.
|
||||
*/
|
||||
memset(&in, 0, sizeof(in));
|
||||
return result;
|
||||
}
|
||||
|
||||
EXPORT(void)testfunc_array(int values[4])
|
||||
{
|
||||
printf("testfunc_array %d %d %d %d\n",
|
||||
|
|
Loading…
Reference in New Issue