Skip to content

Commit

Permalink
Fixed rare crashes due to improper memory alignment
Browse files Browse the repository at this point in the history
  • Loading branch information
orchetect committed Feb 2, 2021
1 parent a13710f commit d332d98
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 6 deletions.
53 changes: 47 additions & 6 deletions Sources/OTCore/Extensions/Foundation/Data.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,27 @@ extension Data {

// define conversions

let number = { self.withUnsafeBytes { $0.load(as: Float32.self) } }()
// this crashes if Data alignment isn't correct
//let number = { self.withUnsafeBytes { $0.load(as: Float32.self) } }()

// since .load(as:) is not memory alignment safe, memcpy is the current workaround (as of Swift 5.3)
// see for more info: https://bugs.swift.org/browse/SR-10273
let number: Float32 = { self.withUnsafeBytes {
var value = Float32()
memcpy(&value, $0.baseAddress!, 4)
return value
} }()

// float twiddling

let numberSwapped: Float32 = {
var floatsw = CFConvertFloat32HostToSwapped(Float32())
floatsw = self.withUnsafeBytes { $0.load(as: CFSwappedFloat32.self) }
floatsw = self.withUnsafeBytes {
//$0.load(as: CFSwappedFloat32.self)
var value = CFSwappedFloat32()
memcpy(&value, $0.baseAddress!, 4)
return value
}
return CFConvertFloat32SwappedToHost(floatsw)
}()

Expand Down Expand Up @@ -350,11 +366,27 @@ extension Data {

// define conversions

let number: Double = { self.withUnsafeBytes { $0.load(as: Double.self) } }()
// this crashes if Data alignment isn't correct
//let number: Double = { self.withUnsafeBytes { $0.load(as: Double.self) } }()

// since .load(as:) is not memory alignment safe, memcpy is the current workaround (as of Swift 5.3)
// see for more info: https://bugs.swift.org/browse/SR-10273
let number: Double = { self.withUnsafeBytes {
var value = Double()
memcpy(&value, $0.baseAddress!, 8)
return value
} }()

// double twiddling

let numberSwapped: Double = {
var floatsw = CFConvertDoubleHostToSwapped(Double())
floatsw = self.withUnsafeBytes { $0.load(as: CFSwappedFloat64.self) }
floatsw = self.withUnsafeBytes {
//$0.load(as: CFSwappedFloat64.self)
var value = CFSwappedFloat64()
memcpy(&value, $0.baseAddress!, 8)
return value
}
return CFConvertDoubleSwappedToHost(floatsw)
}()

Expand Down Expand Up @@ -424,13 +456,22 @@ extension FixedWidthInteger {
extension Data {

/// Internal use.
private func toNumber<T: FixedWidthInteger>(from endianness: NumberEndianness = .platformDefault, toType: T.Type) -> T? {
internal func toNumber<T: FixedWidthInteger>(from endianness: NumberEndianness = .platformDefault, toType: T.Type) -> T? {

guard self.count == MemoryLayout<T>.size else { return nil }

// define conversion

let int = { self.withUnsafeBytes { $0.load(as: T.self) } }()
// this crashes if Data alignment isn't correct
//let int: T = { self.withUnsafeBytes { $0.load(as: T.self) } }()

// since .load(as:) is not memory alignment safe, memcpy is the current workaround (as of Swift 5.3)
// see for more info: https://bugs.swift.org/browse/SR-10273
let int: T = { self.withUnsafeBytes {
var value = T()
memcpy(&value, $0.baseAddress!, MemoryLayout<T>.size)
return value
} }()

// determine which conversion is needed

Expand Down
49 changes: 49 additions & 0 deletions Tests/OTCoreTests/Extensions/Foundation/Data Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,55 @@ class Extensions_Foundation_Data_Tests: XCTestCase {

}

func testMemoryAlignment() {

// test for misaligned raw pointer (memory alignment)

// if the underlying Data -> T:FixedWidthInteger method is not properly aligned, this test will trigger a runtime exception

// cycle through 8 memory offset positions regardless of the type we're testing
for offset in 1...8 {

let offSetBytes = Data([UInt8](repeating: 0x00, count: offset))

// integers

_ = (offSetBytes + Int8(1).toData(.bigEndian))[offset...offset]
.toInt8()

_ = (offSetBytes + UInt8(1).toData(.bigEndian))[offset...offset]
.toUInt8()

_ = (offSetBytes + Int16(1).toData(.bigEndian))[offset...offset + 1]
.toInt16(from: .bigEndian)

_ = (offSetBytes + UInt16(1).toData(.bigEndian))[offset...offset + 1]
.toUInt16(from: .bigEndian)

_ = (offSetBytes + Int32(1).toData(.bigEndian))[offset...offset + 3]
.toInt32(from: .bigEndian)

_ = (offSetBytes + UInt32(1).toData(.bigEndian))[offset...offset + 3]
.toUInt32(from: .bigEndian)

_ = (offSetBytes + Int64(1).toData(.bigEndian))[offset...offset + 7]
.toInt64(from: .bigEndian)

_ = (offSetBytes + UInt64(1).toData(.bigEndian))[offset...offset + 7]
.toUInt64(from: .bigEndian)

// floating-point

_ = (offSetBytes + Float32(1).toData(.bigEndian))[offset...offset + 3]
.toFloat32(from: .bigEndian)

_ = (offSetBytes + Double(1).toData(.bigEndian))[offset...offset + 7]
.toDouble(from: .bigEndian)

}

}


// MARK: - String

Expand Down

0 comments on commit d332d98

Please sign in to comment.