Skip to content

Commit a1501d8

Browse files
committed
Fix tuple construction memory corruption.
This bug was uncovered when working with transformed captures using the DSL. When advancing the address to the next element in a tuple, we should advance by size rather than stride before aligning.
1 parent fdce624 commit a1501d8

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

Sources/_MatchingEngine/Utility/TypeConstruction.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,13 @@ public enum TypeConstruction {
128128
// Open existential on each element type.
129129
func initializeElement<T>(_ element: T) {
130130
currentElementAddressUnaligned =
131-
currentElementAddressUnaligned.roundedUp(toAlignmentOf: T.self)
131+
currentElementAddressUnaligned.roundedUp(toAlignmentOf: T.self)
132132
currentElementAddressUnaligned.bindMemory(
133133
to: T.self, capacity: MemoryLayout<T>.size
134134
).initialize(to: element)
135135
// Advance to the next element (unaligned).
136136
currentElementAddressUnaligned =
137-
currentElementAddressUnaligned.advanced(by: MemoryLayout<T>.stride)
137+
currentElementAddressUnaligned.advanced(by: MemoryLayout<T>.size)
138138
}
139139
_openExistential(element, do: initializeElement)
140140
}

Tests/MatchingEngineTests/UtilTests.swift

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,43 @@ class UtilTests: XCTestCase {
2525
}
2626

2727
func testTypeErasedTupleConstruction() throws {
28-
let tuple0Erased = TypeConstruction.tuple(of: [1, 2, 3])
29-
let tuple0 = try XCTUnwrap(tuple0Erased as? (Int, Int, Int))
30-
XCTAssertEqual(tuple0.0, 1)
31-
XCTAssertEqual(tuple0.1, 2)
32-
XCTAssertEqual(tuple0.2, 3)
28+
do {
29+
let tupleErased = TypeConstruction.tuple(of: [1, 2, 3])
30+
let tuple = try XCTUnwrap(tupleErased as? (Int, Int, Int))
31+
XCTAssertEqual(tuple.0, 1)
32+
XCTAssertEqual(tuple.1, 2)
33+
XCTAssertEqual(tuple.2, 3)
34+
}
3335

34-
let tuple1Erased = TypeConstruction.tuple(
35-
of: [[1, 2], [true, false], [3.0, 4.0]])
36-
XCTAssertTrue(type(of: tuple1Erased) == ([Int], [Bool], [Double]).self)
37-
let tuple1 = try XCTUnwrap(tuple1Erased as? ([Int], [Bool], [Double]))
38-
XCTAssertEqual(tuple1.0, [1, 2])
39-
XCTAssertEqual(tuple1.1, [true, false])
40-
XCTAssertEqual(tuple1.2, [3.0, 4.0])
36+
do {
37+
let tupleErased = TypeConstruction.tuple(
38+
of: [[1, 2], [true, false], [3.0, 4.0]])
39+
XCTAssertTrue(type(of: tupleErased) == ([Int], [Bool], [Double]).self)
40+
let tuple = try XCTUnwrap(tupleErased as? ([Int], [Bool], [Double]))
41+
XCTAssertEqual(tuple.0, [1, 2])
42+
XCTAssertEqual(tuple.1, [true, false])
43+
XCTAssertEqual(tuple.2, [3.0, 4.0])
44+
}
45+
46+
// Reproducer for a memory corruption bug with transformed captures.
47+
do {
48+
enum GraphemeBreakProperty: UInt32 {
49+
case control = 0
50+
case prepend = 1
51+
}
52+
let tupleErased = TypeConstruction.tuple(of: [
53+
"a"[...], // Substring
54+
Unicode.Scalar(0xA6F0)!, // Unicode.Scalar
55+
Unicode.Scalar(0xA6F0) as Any, // Unicode.Scalar?
56+
GraphemeBreakProperty.prepend // GraphemeBreakProperty
57+
])
58+
let tuple = try XCTUnwrap(
59+
tupleErased as?
60+
(Substring, Unicode.Scalar, Unicode.Scalar, GraphemeBreakProperty))
61+
XCTAssertEqual(tuple.0, "a")
62+
XCTAssertEqual(tuple.1, Unicode.Scalar(0xA6F0)!)
63+
XCTAssertEqual(tuple.2, Unicode.Scalar(0xA6F0)!)
64+
XCTAssertEqual(tuple.3, .prepend)
65+
}
4166
}
4267
}

0 commit comments

Comments
 (0)