Skip to content

Commit 1bba62e

Browse files
authored
Merge pull request #60613 from glessard/pointer-arithmetic-overflow-checks
[stdlib] add overflow checks for some pointer arithmetic
2 parents bd6aa99 + 3fcf2a2 commit 1bba62e

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

stdlib/public/core/UnsafePointer.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,11 @@ public struct UnsafePointer<Pointee>: _Pointer {
374374
to property: KeyPath<Pointee, Property>
375375
) -> UnsafePointer<Property>? {
376376
guard let o = property._storedInlineOffset else { return nil }
377+
_internalInvariant(o >= 0)
378+
_debugPrecondition(
379+
o == 0 || UnsafeRawPointer(self) < UnsafeRawPointer(bitPattern: 0 &- o)!,
380+
"Overflow in pointer arithmetic"
381+
)
377382
return .init(Builtin.gepRaw_Word(_rawValue, o._builtinWordValue))
378383
}
379384

@@ -1094,6 +1099,11 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
10941099
to property: KeyPath<Pointee, Property>
10951100
) -> UnsafePointer<Property>? {
10961101
guard let o = property._storedInlineOffset else { return nil }
1102+
_internalInvariant(o >= 0)
1103+
_debugPrecondition(
1104+
o == 0 || UnsafeRawPointer(self) < UnsafeRawPointer(bitPattern: 0 &- o)!,
1105+
"Overflow in pointer arithmetic"
1106+
)
10971107
return .init(Builtin.gepRaw_Word(_rawValue, o._builtinWordValue))
10981108
}
10991109

@@ -1111,6 +1121,11 @@ public struct UnsafeMutablePointer<Pointee>: _Pointer {
11111121
to property: WritableKeyPath<Pointee, Property>
11121122
) -> UnsafeMutablePointer<Property>? {
11131123
guard let o = property._storedInlineOffset else { return nil }
1124+
_internalInvariant(o >= 0)
1125+
_debugPrecondition(
1126+
o == 0 || UnsafeRawPointer(self) < UnsafeRawPointer(bitPattern: 0 &- o)!,
1127+
"Overflow in pointer arithmetic"
1128+
)
11141129
return .init(Builtin.gepRaw_Word(_rawValue, o._builtinWordValue))
11151130
}
11161131

stdlib/public/core/UnsafeRawPointer.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,7 @@ extension UnsafeRawPointer {
500500
public func alignedUp<T>(for type: T.Type) -> Self {
501501
let mask = UInt(Builtin.alignof(T.self)) &- 1
502502
let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask
503+
_debugPrecondition(bits != 0, "Overflow in pointer arithmetic")
503504
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
504505
}
505506

@@ -516,6 +517,7 @@ extension UnsafeRawPointer {
516517
public func alignedDown<T>(for type: T.Type) -> Self {
517518
let mask = UInt(Builtin.alignof(T.self)) &- 1
518519
let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask
520+
_debugPrecondition(bits != 0, "Overflow in pointer arithmetic")
519521
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
520522
}
521523

@@ -537,6 +539,7 @@ extension UnsafeRawPointer {
537539
"alignment must be a whole power of 2."
538540
)
539541
let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask
542+
_debugPrecondition(bits != 0, "Overflow in pointer arithmetic")
540543
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
541544
}
542545

@@ -558,6 +561,7 @@ extension UnsafeRawPointer {
558561
"alignment must be a whole power of 2."
559562
)
560563
let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask
564+
_debugPrecondition(bits != 0, "Overflow in pointer arithmetic")
561565
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
562566
}
563567
}
@@ -1353,6 +1357,7 @@ extension UnsafeMutableRawPointer {
13531357
public func alignedUp<T>(for type: T.Type) -> Self {
13541358
let mask = UInt(Builtin.alignof(T.self)) &- 1
13551359
let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask
1360+
_debugPrecondition(bits != 0, "Overflow in pointer arithmetic")
13561361
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
13571362
}
13581363

@@ -1369,6 +1374,7 @@ extension UnsafeMutableRawPointer {
13691374
public func alignedDown<T>(for type: T.Type) -> Self {
13701375
let mask = UInt(Builtin.alignof(T.self)) &- 1
13711376
let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask
1377+
_debugPrecondition(bits != 0, "Overflow in pointer arithmetic")
13721378
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
13731379
}
13741380

@@ -1390,6 +1396,7 @@ extension UnsafeMutableRawPointer {
13901396
"alignment must be a whole power of 2."
13911397
)
13921398
let bits = (UInt(Builtin.ptrtoint_Word(_rawValue)) &+ mask) & ~mask
1399+
_debugPrecondition(bits != 0, "Overflow in pointer arithmetic")
13931400
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
13941401
}
13951402

@@ -1411,6 +1418,7 @@ extension UnsafeMutableRawPointer {
14111418
"alignment must be a whole power of 2."
14121419
)
14131420
let bits = UInt(Builtin.ptrtoint_Word(_rawValue)) & ~mask
1421+
_debugPrecondition(bits != 0, "Overflow in pointer arithmetic")
14141422
return .init(Builtin.inttoptr_Word(bits._builtinWordValue))
14151423
}
14161424
}

test/stdlib/UnsafePointer.swift.gyb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,28 @@ ${SelfName}TestSuite.test("pointer(to:)") {
537537
% end
538538
}
539539

540+
${SelfName}TestSuite.test("pointer(to:).overflow") {
541+
struct Example {
542+
var a = false
543+
var b = 0
544+
var c: String { "\(a),\(b)" }
545+
var d = 0.0
546+
}
547+
let o = MemoryLayout<Example>.offset(of: \.b)!
548+
let p = UnsafePointer<Example>(bitPattern: -o)
549+
expectNotNil(p)
550+
guard let p else { fatalError() }
551+
if _isDebugAssertConfiguration() {
552+
expectCrashLater()
553+
}
554+
let intPointer = p.pointer(to: \.b)
555+
expectNil(intPointer) // nil because of overflow
556+
let stringPointer = p.pointer(to: \.c)
557+
expectNil(stringPointer) // nil because c is a computed property
558+
let doublePointer = p.pointer(to: \.d)
559+
expectNotNil(doublePointer)
560+
}
561+
540562
% end
541563

542564
runAllTests()

validation-test/stdlib/UnsafeRawPointerInternal.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,76 @@ UnsafeRawPointerTestSuite.test("load.unaligned.largeAlignment.mutablePointer")
5858
}
5959
}
6060

61+
UnsafeRawPointerTestSuite.test("alignedUp.for.overflow") {
62+
let p = UnsafeRawPointer(bitPattern: 1-MemoryLayout<Int>.stride)!
63+
if _isDebugAssertConfiguration() {
64+
expectCrashLater()
65+
}
66+
let up = p.alignedUp(for: Int.self)
67+
expectEqual(Int(bitPattern: up), 0)
68+
}
69+
70+
UnsafeRawPointerTestSuite.test("alignedUp.toMultiple.overflow") {
71+
let p = UnsafeRawPointer(bitPattern: -7)!
72+
if _isDebugAssertConfiguration() {
73+
expectCrashLater()
74+
}
75+
let up = p.alignedUp(toMultipleOf: 8)
76+
expectEqual(Int(bitPattern: up), 0)
77+
}
78+
79+
UnsafeRawPointerTestSuite.test("alignedUp.for.overflow") {
80+
let p = UnsafeRawPointer(bitPattern: MemoryLayout<Int64>.stride-1)!
81+
if _isDebugAssertConfiguration() {
82+
expectCrashLater()
83+
}
84+
let up = p.alignedDown(for: Int64.self)
85+
expectEqual(Int(bitPattern: up), 0)
86+
}
87+
88+
UnsafeRawPointerTestSuite.test("alignedUp.toMultiple.overflow") {
89+
let p = UnsafeRawPointer(bitPattern: 13)!
90+
if _isDebugAssertConfiguration() {
91+
expectCrashLater()
92+
}
93+
let up = p.alignedDown(toMultipleOf: 16)
94+
expectEqual(Int(bitPattern: up), 0)
95+
}
96+
97+
UnsafeRawPointerTestSuite.test("alignedUp.for.overflow.mutable") {
98+
let p = UnsafeMutableRawPointer(bitPattern: 1-MemoryLayout<Int>.stride)!
99+
if _isDebugAssertConfiguration() {
100+
expectCrashLater()
101+
}
102+
let up = p.alignedUp(for: Int.self)
103+
expectEqual(Int(bitPattern: up), 0)
104+
}
105+
106+
UnsafeRawPointerTestSuite.test("alignedUp.toMultiple.overflow.mutable") {
107+
let p = UnsafeMutableRawPointer(bitPattern: -7)!
108+
if _isDebugAssertConfiguration() {
109+
expectCrashLater()
110+
}
111+
let up = p.alignedUp(toMultipleOf: 8)
112+
expectEqual(Int(bitPattern: up), 0)
113+
}
114+
115+
UnsafeRawPointerTestSuite.test("alignedUp.for.overflow.mutable") {
116+
let p = UnsafeMutableRawPointer(bitPattern: MemoryLayout<Int64>.stride-1)!
117+
if _isDebugAssertConfiguration() {
118+
expectCrashLater()
119+
}
120+
let up = p.alignedDown(for: Int64.self)
121+
expectEqual(Int(bitPattern: up), 0)
122+
}
123+
124+
UnsafeRawPointerTestSuite.test("alignedUp.toMultiple.overflow.mutable") {
125+
let p = UnsafeMutableRawPointer(bitPattern: 13)!
126+
if _isDebugAssertConfiguration() {
127+
expectCrashLater()
128+
}
129+
let up = p.alignedDown(toMultipleOf: 16)
130+
expectEqual(Int(bitPattern: up), 0)
131+
}
132+
61133
runAllTests()

0 commit comments

Comments
 (0)