Swift-C array interop with Swift 4/Xcode 9

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)

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@latencyzero.com

Adding: it seems that while passing &inReq.imageInfo.transformation.0 to an UnsafePointer<Float> parameter worked before (passed a pointer to a contiguous block of 16 floats), now it's making a copy of just the first tuple element, and passing a smaller memory block. I think I'm interpreting these results correctly like that.

···

On Sep 20, 2017, at 17:03 , Rick Mann via swift-users <swift-users@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)

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@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

--
Rick Mann
rmann@latencyzero.com

Hi Rick,

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.

···

On 21 Sep 2017, at 1:03 am, Rick Mann via swift-users <swift-users@swift.org> wrote:

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@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

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@swift.org> wrote:

Hi Rick,

On 21 Sep 2017, at 1:03 am, Rick Mann via swift-users <swift-users@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@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

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@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@swift.org> wrote:

Hi Rick,

On 21 Sep 2017, at 1:03 am, Rick Mann via swift-users <swift-users@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@latencyzero.com

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Hey Chris,

So I work with a C library and Swift as well and have a couple of ideas for
you depending on how you work with the array. Here are some tricks I have
used when passing around arrays from C to Swift to ObjC and never had
issues with it. Also, while passing in the first element of the tuple is
fine since the first element has the same address as the entire array in C,
I would just use the base tuple instead as it is more Swifty.

You will have to specify the result type of the functions but it is better
than using several closures to get to where you want to work with stuff.
Removing closures will also help you debug it and see if something else is
going on because if you are passing in the first element all the way down
and not modifying the pointer your logic should be correct.

If you don't mind making a deep copy into a native Swift array you can use
a function like this to work with any C array:

func tupleValuesToArray<T, U>(_ tuple: T) -> U {
    var toReturn: [U] = []
    for (_, value) in Mirror(reflecting: tuple).children {
        if let value = value as? U {
            toReturn.append(value)
        } else {
            fatalError("Failed to convert \(tuple) into an array")
        }
    }
    return toReturn
}

let swiftArray: [Float] = tupleValuesToArray(comeCArray)

If you need to mutate the array or if you just don't want to copy it and/or
want to access it as a sequence you should use an
Unsafe[Mutable]BufferPointer. You can convert to one with this function:

func tupleToUnsafe[Mutable]Buffer<T, U>(_ tuple: Unsafe[Mutable]Pointer<T>
-> Unsafe[Mutable]BufferPointer<U> {
    let mirror = Mirror(reflecting: tuple.poinee)
    return Unsafe[Mutable]BufferPointer<U>(start:
Unsafe[Mutable]Pointer(OpaquePointer(tuple)), count:
Int(mirror.children.count))
}

let buffer: UnsafeMutableBufferPointer<Float> =
tupleToUnsafeMutableBuffer(&cArrayAsTuple)

So there are a few things to point out with this function. You have to get
around type checking by converting to an OpaquePointer and back in order to
cast from T -> U.. There are other solutions to get around the type checks
but this is the one I often use even though I dislike it...

Anyways, once you have a buffer you should then be able to pass in the
baseAddress (and count) of the buffer to ObjC and have it work fine so long
as no pointer math is messing it up down inside the other parts of your
code.

···

On Thu, Sep 21, 2017 at 7:33 AM, Johannes Weiß via swift-users < swift-users@swift.org> wrote:

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@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@swift.org> wrote:
>>
>> Hi Rick,
>>
>>> On 21 Sep 2017, at 1:03 am, Rick Mann via swift-users < > swift-users@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(<
>.DownloadImageRequest, index: Swift.Int) -> ()
(<redacted>:arm64+0x1003e5037)
>>> #3 0x100442bbb in closure #1 () -> () in <redacted>.imageDownloaded(<
>.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/<
>.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(<
>.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@latencyzero.com
>>>
>>>
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users@swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-users
>>
>> _______________________________________________
>> swift-users mailing list
>> swift-users@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-users
>

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users