This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients amaury.forgeotdarc, belopolsky, eryksun, itsgk92, meador.inge, paul.moore, steve.dower, tim.golden, zach.ware
Date 2020-06-20.05:43:53
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1592631833.98.0.858216756882.issue41021@roundup.psfhosted.org>
In-reply-to
Content
I can reproduce a crash with an x86 build that uses a stdcall callback with a 32-bit argument followed by any ffi_type_sint64 argument (FILETIME, long long, etc). Apparently this is a bug in libffi's ffi_prep_cif. Given the 2nd argument has a size of 8 bytes and an alignment of 8 bytes, it sets the CIF (call interface) to 16 bytes instead of the correct value of 12 bytes. 

source:

    import ctypes

    @ctypes.WINFUNCTYPE(None, ctypes.c_int, ctypes.c_int64)
    def test(i, j):
        print(f"callback {i} {j}")

debugger:

    0:000:x86> k 2
    ChildEBP RetAddr
    00bef6a0 6f1873bd _ctypes_d!_ctypes_alloc_callback+0x24c
    00bef6dc 6e7ad81b _ctypes_d!PyCFuncPtr_new+0x32d

The `bytes` field should be 12, not 16:

    0:000:x86> ?? p->cif
    struct ffi_cif
       +0x000 abi              : 2 ( FFI_STDCALL )
       +0x004 nargs            : 2
       +0x008 arg_types        : 0x02dc43bc  -> 0x00742e5c _ffi_type
       +0x00c rtype            : 0x6f1accec _ffi_type
       +0x010 bytes            : 0x10
       +0x014 flags            : 8

    0:000:x86> ?? p->cif.arg_types[0]
    struct _ffi_type * 0x00742e5c
       +0x000 size             : 4
       +0x004 alignment        : 4
       +0x006 type             : 0xa
       +0x008 elements         : (null)

    0:000:x86> ?? p->cif.arg_types[1]
    struct _ffi_type * 0x02dd635c
       +0x000 size             : 8
       +0x004 alignment        : 8
       +0x006 type             : 0xc
       +0x008 elements         : (null)

When the callback returns, it cleans 16 bytes from stack instead of the expected 12. With stack-allocated `systime` in the sample code, eventually GetSystemTimeAsFileTime(&systime) ends up overwriting the return address, and it tries to return to an arbitrary address. If you're lucky, this address isn't executable memory, and it immediately fails on an access violation.

The callback in Aravindhan's sample code works fine if I modify the CIF to use 12 bytes instead of 16:

    _ctypes_d!_ctypes_alloc_callback+0x24c:
    6e9f36ec 83c414          add     esp,14h
    0:000:x86> ?? p->cif.bytes = 12
    unsigned int 0xc

    0:000:x86> bd 0; g
    callback 1 477180670 30820036
    callback 2 487502671 30820036
    callback 3 497655140 30820036
    callback 4 507801994 30820036
    callback 5 517818512 30820036
    callback 6 527961670 30820036

I suppose this issue should be closed as third party, with a bug report pushed upstream -- unless for now it would be better if ctypes implements a workaround.
History
Date User Action Args
2020-06-20 05:43:54eryksunsetrecipients: + eryksun, paul.moore, amaury.forgeotdarc, belopolsky, tim.golden, meador.inge, zach.ware, steve.dower, itsgk92
2020-06-20 05:43:53eryksunsetmessageid: <1592631833.98.0.858216756882.issue41021@roundup.psfhosted.org>
2020-06-20 05:43:53eryksunlinkissue41021 messages
2020-06-20 05:43:53eryksuncreate