Skip to content

Commit

Permalink
bpo-16576: Add checks for bitfields passed by value to functions. (GH…
Browse files Browse the repository at this point in the history
…-17097) (GH-17223)

(cherry picked from commit 1062715)
  • Loading branch information
miss-islington authored and vsajip committed Nov 18, 2019
1 parent 21eb731 commit bef2815
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 3 deletions.
81 changes: 81 additions & 0 deletions Lib/ctypes/test/test_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,87 @@ class Test5(Structure):
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):
Expand Down
13 changes: 10 additions & 3 deletions Modules/_ctypes/_ctypes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
50 changes: 50 additions & 0 deletions Modules/_ctypes/_ctypes_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,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",
Expand Down

0 comments on commit bef2815

Please sign in to comment.