Uploaded image for project: 'Swift'
  1. Swift
  2. SR-12088

Cannot create pointer to 0-length C struct field in Swift



    • Type: Bug
    • Status: Open
    • Priority: Medium
    • Resolution: Unresolved
    • Component/s: None
    • Labels:
    • Environment:

      Apple Swift version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15)
      Target: x86_64-apple-darwin18.6.0


      Given a C struct with a trailing member that is a 0-length array*:

      typedef struct {
          size_t len;
          int8_t payload[0];
      } Foo;

      Swift cannot produce a valid pointer to the payload field.

      Note first,

      let offset = MemoryLayout<Foo>.offset(of: \Foo.payload)!
      assert(offset == 0)

      I have two helper functions in C to aid inspection (lldb seems to sometimes have trouble recognizing Swift pointer variables):

      // Just `printf("%p\n", p);`
      void whats_this_addr(void * p);
      // Wrapper around memcpy, allowing inspection of the addresses
      // and/or memory in the debugger
      void my_memcpy(void * dest, const void * src, size_t len);

      Then, working with a heap-allocated instance of the struct in Swift:

      typealias FooRef = UnsafeMutablePointer<Foo>
      let new: FooRef =  ... // Allocate via `UnsafeMutableRawPointer` and then `bindMemory`
      let payloadStart = UnsafeMutableRawPointer(&new.pointee.payload)
      whats_this_addr(payloadStart)    // Garbage value, consistently somewhere above `new`
      // Curiouser
      let payload: [Int8] = ...
      _ = payload.withUnsafeBytes {
          my_memcpy(payloadStart, $0.baseAddress!, payload.count)    // In a debug build, the first arg is 0x7ff... inside my_memcpy
      // And curiouser
      whats_this_addr(&new.pointee.payload)    // Garbage, _different_ from whats_this_addr(payloadStart) above -- consistently somewhere below `new`
      whats_this_addr(&new.pointee.payload)    // NULL
      whats_this_addr(payloadStart)            // NULL?!

      If the offset/pointer is calculated in C, Swift can use it perfectly well. The problem seems to be solely with Swift's calculation of the location of the field.

      The address range is different in a release build (tested with -fno-pie), but the behavior is effectively the same.

      Full running example attached, also available at https://github.com/woolsweater/flex-arr-mem , "bug-report" branch (there is a fuller example on master).

      *In lieu of a true flexible array member int8_t payload[], which isn't imported at all. (Swift does handle a member with declared length 1 correctly, however.)




            Unassigned Unassigned
            woolsweater Josh Caswell
            0 Vote for this issue
            4 Start watching this issue