Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a86339b

Browse files
authored
Fixed bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64. (#168)
* Fixed bpo-29565: Corrected ctypes passing of large structs by value. Added code and test to check that when a structure passed by value is large enough to need to be passed by reference, a copy of the original structure is passed. The callee updates the passed-in value, and the test verifies that the caller's copy is unchanged. A similar change was also added to the test added for bpo-20160 (that test was passing, but the changes should guard against regressions). * Reverted unintended whitespace changes.
1 parent 3eea8c6 commit a86339b

File tree

4 files changed

+57
-0
lines changed

4 files changed

+57
-0
lines changed

Lib/ctypes/test/test_callbacks.py

+11
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ def callback(a, b, c, d, e):
244244
def test_callback_large_struct(self):
245245
class Check: pass
246246

247+
# This should mirror the structure in Modules/_ctypes/_ctypes_test.c
247248
class X(Structure):
248249
_fields_ = [
249250
('first', c_ulong),
@@ -255,6 +256,11 @@ def callback(check, s):
255256
check.first = s.first
256257
check.second = s.second
257258
check.third = s.third
259+
# See issue #29565.
260+
# The structure should be passed by value, so
261+
# any changes to it should not be reflected in
262+
# the value passed
263+
s.first = s.second = s.third = 0x0badf00d
258264

259265
check = Check()
260266
s = X()
@@ -275,6 +281,11 @@ def callback(check, s):
275281
self.assertEqual(check.first, 0xdeadbeef)
276282
self.assertEqual(check.second, 0xcafebabe)
277283
self.assertEqual(check.third, 0x0bad1dea)
284+
# See issue #29565.
285+
# Ensure that the original struct is unchanged.
286+
self.assertEqual(s.first, check.first)
287+
self.assertEqual(s.second, check.second)
288+
self.assertEqual(s.third, check.third)
278289

279290
################################################################
280291

Lib/ctypes/test/test_structures.py

+23
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from ctypes.test import need_symbol
44
from struct import calcsize
55
import _testcapi
6+
import _ctypes_test
67

78
class SubclassesTest(unittest.TestCase):
89
def test_subclass(self):
@@ -391,6 +392,28 @@ class Z(Y):
391392
(1, 0, 0, 0, 0, 0))
392393
self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7))
393394

395+
def test_pass_by_value(self):
396+
# This should mirror the structure in Modules/_ctypes/_ctypes_test.c
397+
class X(Structure):
398+
_fields_ = [
399+
('first', c_ulong),
400+
('second', c_ulong),
401+
('third', c_ulong),
402+
]
403+
404+
s = X()
405+
s.first = 0xdeadbeef
406+
s.second = 0xcafebabe
407+
s.third = 0x0bad1dea
408+
dll = CDLL(_ctypes_test.__file__)
409+
func = dll._testfunc_large_struct_update_value
410+
func.argtypes = (X,)
411+
func.restype = None
412+
func(s)
413+
self.assertEqual(s.first, 0xdeadbeef)
414+
self.assertEqual(s.second, 0xcafebabe)
415+
self.assertEqual(s.third, 0x0bad1dea)
416+
394417
class PointerMemberTestCase(unittest.TestCase):
395418

396419
def test(self):

Modules/_ctypes/_ctypes_test.c

+13
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,19 @@ _testfunc_cbk_large_struct(Test in, void (*func)(Test))
4444
func(in);
4545
}
4646

47+
/*
48+
* See issue 29565. Update a structure passed by value;
49+
* the caller should not see any change.
50+
*/
51+
52+
EXPORT(void)
53+
_testfunc_large_struct_update_value(Test in)
54+
{
55+
in.first = 0x0badf00d;
56+
in.second = 0x0badf00d;
57+
in.third = 0x0badf00d;
58+
}
59+
4760
EXPORT(void)testfunc_array(int values[4])
4861
{
4962
printf("testfunc_array %d %d %d %d\n",

Modules/_ctypes/libffi_msvc/ffi.c

+10
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,16 @@ ffi_call(/*@dependent@*/ ffi_cif *cif,
239239
break;
240240
#else
241241
case FFI_SYSV:
242+
/* If a single argument takes more than 8 bytes,
243+
then a copy is passed by reference. */
244+
for (unsigned i = 0; i < cif->nargs; i++) {
245+
size_t z = cif->arg_types[i]->size;
246+
if (z > 8) {
247+
void *temp = alloca(z);
248+
memcpy(temp, avalue[i], z);
249+
avalue[i] = temp;
250+
}
251+
}
242252
/*@-usedef@*/
243253
return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes,
244254
cif->flags, ecif.rvalue, fn);

0 commit comments

Comments
 (0)