The RawSpan and MutableRawSpan APIs make it possible to arbitrarily change the bit pattern of a BitwiseCopyable value without using unsafe code. For example, the following function returns a Bool value with an invalid bit pattern:
func createInvalidBool() -> Bool {
var dst: [1 of Bool] = [false]
var dstSpan = dst.mutableSpan
var dstBytes = dstSpan.mutableBytes
dstBytes.storeBytes(of: 2, toByteOffset: 0, as: UInt8.self)
return dst[0]
}
I've also noticed that the RawSpan and MutableRawSpan APIs make it possible to read uninitialized memory by reading the padding bytes of BitwiseCopyable values, without using unsafe code. For example, the following function returns the contents of uninitialized memory on the stack:
func readUninitializedBytesFromStack() -> [UInt8] {
let src: [100 of Void] = InlineArray(repeating: ())
var dst = Array(repeating: 0 as UInt8, count: 100)
var dstSpan = dst.mutableSpan
var dstBytes = dstSpan.mutableBytes
dstBytes.storeBytes(of: src, toByteOffset: 0, as: [100 of Void].self)
return dst
}
I'm not sure if this is undefined behavior, or if this has "freeze" semantics (where the returned values are indeterminately chosen but self-consistent, and there is no undefined behavior). Even if it has "freeze" semantics, it still arguably breaks memory safety, because exposing uninitialized memory like this can lead to the same kinds of security vulnerabilities associated with memory-unsafe code in languages like C.