Type Pun in Test Suite

Just stumbled across this mock NSDictionary implementation in the test suite:

It appears to be violating strict aliasing, but I’d just like to verify it with y’all since it’s a classic example of type punning that the average programmer expects to work, and will generally work in practice™. Good learning opportunity!

Notable lines:

  struct Keys {
    var key0: AnyObject = TestObjCKeyTy(10)
    var key1: AnyObject = TestObjCKeyTy(20)
    var key2: AnyObject = TestObjCKeyTy(30)
    var key3: AnyObject = TestObjCKeyTy(40)
  }

  var keys = [ Keys() ]

    if theState.state == 0 {
      theState.state = 1
      theState.itemsPtr = AutoreleasingUnsafeMutablePointer(keys._baseAddressIfContiguous)
      theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
      state.pointee = theState
      return 4
    }

This appears to be casting a pointer to a struct Keys (containing 4 AnyObjects) to a pointer to AnyObject, declaring that it’s an array of 4 AnyObjects. So this is broken in three ways:

* Strict aliasing violation; just plain UB.

* There’s nothing that actually guarantees an instance of Keys is allocated inline like this, right? The compiler could decide it’s profitable for all instances of this type to be boxed. Or even random fields to be boxed?

* It could also choose to reorder the fields and insert padding however it pleases.

I agree with all three lines. Good catch. Can we get Andy’s Swift Type Sanitizer online? :-)

Jordan

···

On Nov 9, 2016, at 17:30, Alexis via swift-dev <swift-dev@swift.org> wrote:

Just stumbled across this mock NSDictionary implementation in the test suite:

https://github.com/apple/swift/blob/a1dbe066adf826b27dd8e71234ba7e8ce2b26b73/validation-test/stdlib/Dictionary.swift#L1260-L1306

It appears to be violating strict aliasing, but I’d just like to verify it with y’all since it’s a classic example of type punning that the average programmer expects to work, and will generally work in practice™. Good learning opportunity!

Notable lines:

  struct Keys {
    var key0: AnyObject = TestObjCKeyTy(10)
    var key1: AnyObject = TestObjCKeyTy(20)
    var key2: AnyObject = TestObjCKeyTy(30)
    var key3: AnyObject = TestObjCKeyTy(40)
  }

  var keys = [ Keys() ]

    if theState.state == 0 {
      theState.state = 1
      theState.itemsPtr = AutoreleasingUnsafeMutablePointer(keys._baseAddressIfContiguous)
      theState.mutationsPtr = _fastEnumerationStorageMutationsPtr
      state.pointee = theState
      return 4
    }

This appears to be casting a pointer to a struct Keys (containing 4 AnyObjects) to a pointer to AnyObject, declaring that it’s an array of 4 AnyObjects. So this is broken in three ways:

* Strict aliasing violation; just plain UB.

* There’s nothing that actually guarantees an instance of Keys is allocated inline like this, right? The compiler could decide it’s profitable for all instances of this type to be boxed. Or even random fields to be boxed?

* It could also choose to reorder the fields and insert padding however it pleases.