diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index d1ea43bc7e3b6f..2e0802e1fc3eaa 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -4,6 +4,7 @@ from struct import calcsize import _ctypes_test import test.support +import sys class SubclassesTest(unittest.TestCase): def test_subclass(self): @@ -438,6 +439,38 @@ class X(Structure): self.assertEqual(s.first, got.first) self.assertEqual(s.second, got.second) + # bpo-37140: ctypes change made clang fail to build + def test_pass_by_value_string_buffer(self): + dll = CDLL(_ctypes_test.__file__) + dll.my_strdup.restype = POINTER(c_char) + dll.my_free.restype = None + + # This should mirror the structure in Modules/_ctypes/_ctypes_test.c + class X(Structure): + _fields_ = [ + ('first', c_ulong), + ('second', c_ulong), + ('third', POINTER(c_char)), + ] + def __del__(self): + # Windows throws 'Windows fatal exception: access violation' + # on second call to __del__ + # causing this test to fail + dll.my_free(self.third) + + hello = b"Hello" + s = X() + s.first = 0xdeadbeef + s.second = 0xcafebabe + s.third = dll.my_strdup(hello) + func = dll._testfunc_large_struct_update_value_string_buffer + func.argtypes = (X,) + func.restype = None + func(s) + self.assertEqual(s.first, 0xdeadbeef) + self.assertEqual(s.second, 0xcafebabe) + # string is already freed by pass-by-value copy of struct 's' + #self.assertEqual(s.third[0], b'J') class PointerMemberTestCase(unittest.TestCase): diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index f7513a3d74c4bc..d9497f6557a3e2 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -431,7 +431,7 @@ StructUnionType_paramfunc(CDataObject *self) parg->pffi_type = &stgdict->ffi_type_pointer; parg->value.p = copied_self->b_ptr; parg->size = copied_self->b_size; - parg->obj = (PyObject *)copied_self; + parg->obj = PyTuple_Pack(2, self, copied_self); return parg; } diff --git a/Modules/_ctypes/_ctypes_test.c b/Modules/_ctypes/_ctypes_test.c index bae4976a08d31b..4316a0632535cf 100644 --- a/Modules/_ctypes/_ctypes_test.c +++ b/Modules/_ctypes/_ctypes_test.c @@ -57,6 +57,26 @@ _testfunc_large_struct_update_value(Test in) ((volatile Test *)&in)->third = 0x0badf00d; } +typedef struct { + unsigned long first; + unsigned long second; + char *third; +} TestStringBuffer; + +/* + * See issue 37140. Update a structure passed by value + * that contains a pointer to a string and a destructor; + * the pointer to the string should not be double-freed. + */ + +EXPORT(void) +_testfunc_large_struct_update_value_string_buffer(TestStringBuffer in) +{ + ((volatile TestStringBuffer *)&in)->first = 0x0badf00d; + ((volatile TestStringBuffer *)&in)->second = 0x0badf00d; + ((volatile TestStringBuffer *)&in)->third[0] = 'J'; +} + typedef struct { unsigned int first; unsigned int second;