[swift-users] Swift-C array interop with Swift 4/Xcode 9

Johannes Weiß johannesweiss at apple.com
Thu Sep 21 08:33:36 CDT 2017


Hi Chris,

Yes, he'll need to do

withUnsafeMutablePointer(to: &inReq.imageInfo.transformation) { transformationTuplePtr in
   withUnsafeMutablePointer(to: &inReq.imageInfo.projection) { projectionTuplePtr in
       transformationTuplePtr.withMemoryRebound(to: Float.self, capacity: 16) { transformationArrayPtr in
           projectionTuplePtr.withMemoryRebound(to: Float.self, capacity: 16) { projectionArrayPtr in
               ....imageReceived(..., transform: transformationArrayPtr, projection: projectionArrayPtr, ...)
           }
       }
   }
}

if you need access to 5 of them, you'll need to nest deeper, apologies.

However, there's talk on swift-evolution about getting fixed-size arrays into Swift which would help here. But there's no full proposal yet AFAIK.


-- Johannes

> On 21 Sep 2017, at 2:27 pm, Chris McIntyre <nothingwasdelivered at gmail.com> wrote:
> 
> Sorry to jump in here, and maybe I’m missing something obvious, but in your example Rick is two levels of closure deep and only has access to one of the arrays in his struct. In this case there is another array of floats he needs as an argument to his method call. Will he need to go through the same steps, going two levels of closure deeper, before he has access to both struct members? 
> 
> This doesn’t seem to scale very well. What about a struct with 5 arrays? 
> --
> Chris McIntyre
> 
> 
> 
> 
>> On Sep 21, 2017, at 6:05 AM, Johannes Weiß via swift-users <swift-users at swift.org> wrote:
>> 
>> Hi Rick,
>> 
>>> On 21 Sep 2017, at 1:03 am, Rick Mann via swift-users <swift-users at swift.org> wrote:
>>> 
>>> I've got Swift code wrapping a C API. One of the C structs the API uses looks like this:
>>> 
>>> typedef struct {
>>>   size_t size;
>>>   float transformation[16];
>>>   float projection[16];
>>>   uint32_t width;
>>>   uint32_t height;
>>> } image_info_t;
>>> 
>>> In Swift, the two array members are 16-tuples of floats. Eventually, the Swift code calls an Objective-C delegate, passing a tuple to an array:
>>> 
>>> self.delegate?
>>>   .imageReceived(self,
>>>                   index: inReq.imageIndex,
>>>                   data: data,
>>>                   width: width,
>>>                   height: height,
>>>                   transform: &inReq.imageInfo.transformation.0,
>>>                   projection: &inReq.imageInfo.projection.0)
>> 
>> these are illegal. You're getting a pointer to _only_ the first element (&inReq.imageInfo.transformation.0) but the library will then read 16 elements from there.
>> 
>> Something like this should work (and is legal):
>> 
>> withUnsafeMutablePointer(to: &inReq.imageInfo.transformation) { tupleOfFloatsPtr in
>>    tupleOfFloatsPtr.withMemoryRebound(to: Float.self, capacity: 16) { arrayOfFloatsPtr in
>>        ....imageReceived(..., transform: arrayOfFloatsPtr, ...)
>>    }
>> }
>> 
>> that's legal because now we're capturing a pointer to the whole tuple and therefore Swift guarantees the memory to be in C standard layout.
>> 
>> 
>> 
>>> 
>>> This seems to have been working fine for a long time, but we're running into a crash in some cases, and so I've been digging into it. I turned on the address sanitizer, and I consistently get it to trip in some C++ code that eventually gets called with a float* to that transform tuple.
>>> 
>>> Now, in Swift, the values of the tuple look great:
>>> 
>>> ▿ 16 elements
>>> - .0 : 0.548282921
>>> - .1 : 0.821425974
>>> - .2 : -0.156990349
>>> - .3 : 0.0
>>> - .4 : -0.277842015
>>> - .5 : 0.00185899995
>>> - .6 : -0.960625231
>>> - .7 : 0.0
>>> - .8 : -0.788789928
>>> - .9 : 0.570312977
>>> - .10 : 0.229245573
>>> - .11 : 0.0
>>> - .12 : 0.0475889929
>>> - .13 : -0.0323969983
>>> - .14 : -0.0113521963
>>> - .15 : 1.0
>>> 
>>> I can see this by clicking up the stack a couple frames to the Swift code. But the Objective-C(++) delegate code, that gets passed a float*, sees this (BTW, I can't figure out how to get the debugger, Xcode or command line, to display inTransform as an array of 16 floats). It's clearly not the same array.
>>> 
>>> 
>>> (lldb) po inTransform[0]
>>> 0.548282921
>>> 
>>> (lldb) po inTransform[1]
>>> 0.0343905278
>>> 
>>> (lldb) po inTransform[2]
>>> -0
>>> 
>>> (lldb) po inTransform[3]
>>> 0
>>> 
>>> (lldb) po inTransform[4]
>>> 0
>>> 
>>> (lldb) po inTransform[5]
>>> 0
>>> 
>>> (lldb) po inTransform[6]
>>> 95488384
>>> 
>>> (lldb) po inTransform[14]
>>> 0.00000000000000000000000000000000000000000000560519386
>>> 
>>> (lldb) po inTransform[15]
>>> 0
>>> 
>>> The address sanitizer spits out this:
>>> 
>>> =================================================================
>>> ==1358==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x00014d3ea224 at pc 0x000100eb9a84 bp 0x00016fd84bd0 sp 0x00016fd84bc8
>>> READ of size 4 at 0x00014d3ea224 thread T0
>>>   #0 0x100eb9a83 in <redacted>::addImage(void const*, int, int, int, float const*, float const*) (<redacted>:arm64+0x100e41a83)
>>>   #1 0x10040b72b in -[<redacted> imageReceived:index:data:width:height:transform:projection:] (<redacted>:arm64+0x10039372b)
>>>   #2 0x10045d037 in function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Owned To Guaranteed, Arg[2] = Owned To Guaranteed and Exploded> of closure #1 () -> () in <redacted>.imageDownloaded(<redacted>.DownloadImageRequest, index: Swift.Int) -> () (<redacted>:arm64+0x1003e5037)
>>>   #3 0x100442bbb in closure #1 () -> () in <redacted>.imageDownloaded(<redacted>.DownloadImageRequest, index: Swift.Int) -> () (<redacted>:arm64+0x1003cabbb)
>>>   #4 0x10043e7fb in reabstraction thunk helper from @callee_owned () -> () to @callee_unowned @convention(block) () -> () (<redacted>:arm64+0x1003c67fb)
>>>   #5 0x103ba9d5b in __wrap_dispatch_async_block_invoke (/var/containers/Bundle/Application/B3E557CA-5ED9-4673-8B07-37A0F2DA472B/<redacted>.app/Frameworks/libclang_rt.asan_ios_dynamic.dylib:arm64+0x4dd5b)
>>>   #6 0x105f1549b in _dispatch_call_block_and_release (/usr/lib/system/introspection/libdispatch.dylib:arm64+0x149b)
>>>   #7 0x105f1545b in _dispatch_client_callout (/usr/lib/system/introspection/libdispatch.dylib:arm64+0x145b)
>>>   #8 0x105f1a04f in _dispatch_main_queue_callback_4CF (/usr/lib/system/introspection/libdispatch.dylib:arm64+0x604f)
>>>   #9 0x181d43f1f in <redacted> (/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation:arm64+0xe9f1f)
>>>   #10 0x181d41afb in <redacted> (/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation:arm64+0xe7afb)
>>>   #11 0x181c622d7 in CFRunLoopRunSpecific (/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation:arm64+0x82d7)
>>>   #12 0x183af3f83 in GSEventRunModal (/System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices:arm64+0xaf83)
>>>   #13 0x18b20e87f in UIApplicationMain (/System/Library/Frameworks/UIKit.framework/UIKit:arm64+0x7387f)
>>>   #14 0x100122dc3 in main (<redacted>:arm64+0x1000aadc3)
>>>   #15 0x18178656b in <redacted> (/usr/lib/system/libdyld.dylib:arm64+0x156b)
>>> 
>>> Address 0x00014d3ea224 is located in stack of thread T0 at offset 36 in frame
>>>   #0 0x10045cd57 in function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Owned To Guaranteed, Arg[2] = Owned To Guaranteed and Exploded> of closure #1 () -> () in <redacted>.imageDownloaded(<redacted>.DownloadImageRequest, index: Swift.Int) -> () (<redacted>:arm64+0x1003e4d57)
>>> 
>>> This frame has 4 object(s):
>>>   [32, 36) '' <== Memory access at offset 36 overflows this variable
>>>   [48, 208) ''
>>>   [272, 276) ''
>>>   [288, 448) ''
>>> HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
>>>     (longjmp and C++ exceptions *are* supported)
>>> SUMMARY: AddressSanitizer: stack-buffer-overflow (<redacted>:arm64+0x100e41a83) in <redacted>::addImage(void const*, int, int, int, float const*, float const*)
>>> Shadow bytes around the buggy address:
>>> 0x0001302dd3f0: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
>>> 0x0001302dd400: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
>>> 0x0001302dd410: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
>>> 0x0001302dd420: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
>>> 0x0001302dd430: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
>>> =>0x0001302dd440: f1 f1 f1 f1[04]f2 00 00 00 00 00 00 00 00 00 00
>>> 0x0001302dd450: 00 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2 f2 f2
>>> 0x0001302dd460: f2 f2 04 f2 00 00 00 00 00 00 00 00 00 00 00 00
>>> 0x0001302dd470: 00 00 00 00 00 00 00 00 f3 f3 f3 f3 f3 f3 f3 f3
>>> 0x0001302dd480: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
>>> 0x0001302dd490: f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5 f5
>>> Shadow byte legend (one shadow byte represents 8 application bytes):
>>> Addressable:           00
>>> Partially addressable: 01 02 03 04 05 06 07 
>>> Heap left redzone:       fa
>>> Freed heap region:       fd
>>> Stack left redzone:      f1
>>> Stack mid redzone:       f2
>>> Stack right redzone:     f3
>>> Stack after return:      f5
>>> Stack use after scope:   f8
>>> Global redzone:          f9
>>> Global init order:       f6
>>> Poisoned by user:        f7
>>> Container overflow:      fc
>>> Array cookie:            ac
>>> Intra object redzone:    bb
>>> ASan internal:           fe
>>> Left alloca redzone:     ca
>>> Right alloca redzone:    cb
>>> 
>>> 
>>> 
>>> So, how do I properly go back and forth between C and Swift code with pointers to arrays of floats?
>>> 
>>> Thanks,
>>> 
>>> -- 
>>> Rick Mann
>>> rmann at latencyzero.com
>>> 
>>> 
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-users
>> 
>> _______________________________________________
>> swift-users mailing list
>> swift-users at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-users
> 



More information about the swift-users mailing list