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

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fixed bpo-29565: Corrected ctypes passing of large structs by value o…
…n 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.

(cherry picked from commit a86339b)
  • Loading branch information
vsajip committed Feb 21, 2017
commit b133b27579e310e0655f0b9dc1d87276bce495c4
11 changes: 11 additions & 0 deletions Lib/ctypes/test/test_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ def callback(a, b, c, d, e):
def test_callback_large_struct(self):
class Check: pass

# This should mirror the structure in Modules/_ctypes/_ctypes_test.c
class X(Structure):
_fields_ = [
('first', c_ulong),
Expand All @@ -255,6 +256,11 @@ def callback(check, s):
check.first = s.first
check.second = s.second
check.third = s.third
# See issue #29565.
# The structure should be passed by value, so
# any changes to it should not be reflected in
# the value passed
s.first = s.second = s.third = 0x0badf00d

check = Check()
s = X()
Expand All @@ -275,6 +281,11 @@ def callback(check, s):
self.assertEqual(check.first, 0xdeadbeef)
self.assertEqual(check.second, 0xcafebabe)
self.assertEqual(check.third, 0x0bad1dea)
# See issue #29565.
# Ensure that the original struct is unchanged.
self.assertEqual(s.first, check.first)
self.assertEqual(s.second, check.second)
self.assertEqual(s.third, check.third)

################################################################

Expand Down
23 changes: 23 additions & 0 deletions Lib/ctypes/test/test_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from ctypes.test import need_symbol
from struct import calcsize
import _testcapi
import _ctypes_test

class SubclassesTest(unittest.TestCase):
def test_subclass(self):
Expand Down Expand Up @@ -394,6 +395,28 @@ class Z(Y):
(1, 0, 0, 0, 0, 0))
self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7))

def test_pass_by_value(self):
# This should mirror the structure in Modules/_ctypes/_ctypes_test.c
class X(Structure):
_fields_ = [
('first', c_ulong),
('second', c_ulong),
('third', c_ulong),
]

s = X()
s.first = 0xdeadbeef
s.second = 0xcafebabe
s.third = 0x0bad1dea
dll = CDLL(_ctypes_test.__file__)
func = dll._testfunc_large_struct_update_value
func.argtypes = (X,)
func.restype = None
func(s)
self.assertEqual(s.first, 0xdeadbeef)
self.assertEqual(s.second, 0xcafebabe)
self.assertEqual(s.third, 0x0bad1dea)

class PointerMemberTestCase(unittest.TestCase):

def test(self):
Expand Down
13 changes: 13 additions & 0 deletions Modules/_ctypes/_ctypes_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ _testfunc_cbk_large_struct(Test in, void (*func)(Test))
func(in);
}

/*
* See issue 29565. Update a structure passed by value;
* the caller should not see any change.
*/

EXPORT(void)
_testfunc_large_struct_update_value(Test in)
{
in.first = 0x0badf00d;
in.second = 0x0badf00d;
in.third = 0x0badf00d;
}

EXPORT(void)testfunc_array(int values[4])
{
printf("testfunc_array %d %d %d %d\n",
Expand Down
10 changes: 10 additions & 0 deletions Modules/_ctypes/libffi_msvc/ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,16 @@ ffi_call(/*@dependent@*/ ffi_cif *cif,
break;
#else
case FFI_SYSV:
/* If a single argument takes more than 8 bytes,
then a copy is passed by reference. */
for (unsigned i = 0; i < cif->nargs; i++) {
size_t z = cif->arg_types[i]->size;
if (z > 8) {
void *temp = alloca(z);
memcpy(temp, avalue[i], z);
avalue[i] = temp;
}
}
/*@-usedef@*/
return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes,
cif->flags, ecif.rvalue, fn);
Expand Down