You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Still broken ctypes calling convention on MSVC / 64-bit Windows (large structs) #73751
Our support of passing structs and unions by value is really bad. Passing this particular struct by value is also broken on 64-bit Unix, but instead because it's small. It crashes Python due to an abort(). See bpo-22273.
Just trying to confirm my understanding - in both this issue and bpo-22273, the example struct has a single array member. Is the problem confined just to how ctypes describes arrays in structs/unions to libffi? The comment on bpo-22273 seems to suggest so.
The contents of the struct is just being big enough to reach the point where it is passed by reference rather than by value.
This issue is pointing out that it should be a reference to a temporary copy of the struct, so that if the called function modifies the argument then it doesn't affect the caller.
This issue is more straight-forward than bpo-22273. ISTM, we just have to copy large values. For example, in ffi_call, we'd iterate over the arguments and alloca and memcpy the large values prior to calling ffi_call_AMD64:
caseFFI_SYSV:
/* If a single argument takes more than 8 bytes, then a copy is passed by reference. */for (unsignedi=0; i<cif->nargs; i++) {
size_tz=cif->arg_types[i]->size;
if (z>8) {
void*temp=alloca(z);
memcpy(temp, avalue[i], z);
avalue[i] =temp;
}
}
/*@-usedef@*/returnffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes,
cif->flags, ecif.rvalue, fn);
/*@=usedef@*/break;
When adding the fix for bpo-20160 I had added a test involving passing a struct by value to a Python callback. I modified that test to update the struct in the callback, and check that the passed-in struct hadn't changed.
deftest_callback_large_struct(self):
classCheck: passclassX(Structure):
_fields_= [
('first', c_ulong),
('second', c_ulong),
('third', c_ulong),
]
defcallback(check, s):
check.first=s.firstcheck.second=s.secondcheck.third=s.third# See issue python/cpython#73751.# The structure should be passed by value, so# any changes to it should not be reflected in# the value passeds.first=s.second=s.third=0x0badf00dcheck=Check()
s=X()
s.first=0xdeadbeefs.second=0xcafebabes.third=0x0bad1deaCALLBACK=CFUNCTYPE(None, X)
dll=CDLL(_ctypes_test.__file__)
func=dll._testfunc_cbk_large_structfunc.argtypes= (X, CALLBACK)
func.restype=None# the function just calls the callback with the passed structurefunc(s, CALLBACK(functools.partial(callback, check)))
self.assertEqual(check.first, s.first)
self.assertEqual(check.second, s.second)
self.assertEqual(check.third, s.third)
self.assertEqual(check.first, 0xdeadbeef)
self.assertEqual(check.second, 0xcafebabe)
self.assertEqual(check.third, 0x0bad1dea)
# See issue python/cpython#73751.# 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)
The changed test still passes, so it looks like ctypes makes a copy when passing from C to a Python callback, but not when passing from Python to C.
New changeset a86339b by GitHub in branch 'master':
Fixed bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64. (#168) a86339b
New changeset 8fa7e22 by GitHub in branch '3.5':
Fixed bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64. (#168) (#221) 8fa7e22
New changeset 3cc5817 by GitHub in branch '3.6':
Fixed bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64. (#168) (#220) 3cc5817
/home/serhiy/py/cpython/Modules/_ctypes/_ctypes_test.c: In function ‘_testfunc_large_struct_update_value’:
/home/serhiy/py/cpython/Modules/_ctypes/_ctypes_test.c:53:42: warning: parameter ‘in’ set but not used [-Wunused-but-set-parameter]
_testfunc_large_struct_update_value(Test in)
^
New changeset 3243f8c by Victor Stinner in branch '2.7': bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64 (GH-168) (GH-8625) 3243f8c
It seems like this buildbot uses Visual Studio 2008 ("VS 9.0")... but it seems like AppVeyor also uses Visual Studio 2008. No idea why AppVeyor missed the compilation error :-(
Activity
IgorKudrin commentedon Feb 15, 2017
It looks like resolving https://bugs.python.org/issue20160 didn't fix all the issues. When a large struct is used as a parameter, a reference to the object itself is passed to the function, not a reference to a temporary copy, as it is required in https://msdn.microsoft.com/en-us/library/zthk2dkh.aspx.
Here is the reproduction sample:
=== Sample.c: ===
__declspec(dllexport) void Cripple(Data data)
{
data.v[0] = 0;
}
=== Sample.py ===
=== repro.cmd ===
call "%VS140COMNTOOLS%/../../VC/vcvarsall.bat" x86_amd64
cl /LD Sample.c
python Sample.py
eryksun commentedon Feb 15, 2017
Our support of passing structs and unions by value is really bad. Passing this particular struct by value is also broken on 64-bit Unix, but instead because it's small. It crashes Python due to an abort(). See bpo-22273.
vsajip commentedon Feb 16, 2017
Just trying to confirm my understanding - in both this issue and bpo-22273, the example struct has a single array member. Is the problem confined just to how ctypes describes arrays in structs/unions to libffi? The comment on bpo-22273 seems to suggest so.
zooba commentedon Feb 16, 2017
The contents of the struct is just being big enough to reach the point where it is passed by reference rather than by value.
This issue is pointing out that it should be a reference to a temporary copy of the struct, so that if the called function modifies the argument then it doesn't affect the caller.
eryksun commentedon Feb 17, 2017
This issue is more straight-forward than bpo-22273. ISTM, we just have to copy large values. For example, in ffi_call, we'd iterate over the arguments and alloca and memcpy the large values prior to calling ffi_call_AMD64:
vsajip commentedon Feb 19, 2017
When adding the fix for bpo-20160 I had added a test involving passing a struct by value to a Python callback. I modified that test to update the struct in the callback, and check that the passed-in struct hadn't changed.
The changed test still passes, so it looks like ctypes makes a copy when passing from C to a Python callback, but not when passing from Python to C.
vsajip commentedon Feb 19, 2017
PR submitted.
vsajip commentedon Feb 20, 2017
New changeset a86339b by GitHub in branch 'master':
Fixed bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64. (#168)
a86339b
zooba commentedon Feb 21, 2017
I approved the two backports. Thanks Vinay!
vsajip commentedon Feb 22, 2017
New changeset 8fa7e22 by GitHub in branch '3.5':
Fixed bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64. (#168) (#221)
8fa7e22
vsajip commentedon Feb 22, 2017
New changeset 3cc5817 by GitHub in branch '3.6':
Fixed bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64. (#168) (#220)
3cc5817
serhiy-storchaka commentedon Mar 6, 2017
New test causes a compiler warning:
/home/serhiy/py/cpython/Modules/_ctypes/_ctypes_test.c: In function ‘_testfunc_large_struct_update_value’:
/home/serhiy/py/cpython/Modules/_ctypes/_ctypes_test.c:53:42: warning: parameter ‘in’ set but not used [-Wunused-but-set-parameter]
_testfunc_large_struct_update_value(Test in)
^
Could it be silenced?
vstinner commentedon Aug 2, 2018
New changeset 3243f8c by Victor Stinner in branch '2.7':
bpo-29565: Corrected ctypes passing of large structs by value on Windows AMD64 (GH-168) (GH-8625)
3243f8c
vstinner commentedon Aug 2, 2018
Hum, the compilation succeeded on AppVeyor:
https://ci.appveyor.com/project/python/cpython/build/2.7build20258
But it fails on AMD64 Windows8 2.7:
https://buildbot.python.org/all/#/builders/74/builds/194
It seems like this buildbot uses Visual Studio 2008 ("VS 9.0")... but it seems like AppVeyor also uses Visual Studio 2008. No idea why AppVeyor missed the compilation error :-(
Anyway, I proposed PR 8626 to fix it.
vstinner commentedon Aug 2, 2018
New changeset 6a6b248 by Victor Stinner in branch '2.7':
bpo-29565: Fix compilation for C89 (GH-8626)
6a6b248