Skip to content

Commit 5b364c4

Browse files
committed
[stdlib] String.index(before:): Fix index validation issues
`String.index(before:)` (and methods that rely on it, such as `Substring.index(before:)`, `.distance(from:to:)` etc.) does not currently verify that the given index falls before the `endIndex` before aligning it to a scalar boundary. This allows an out-of-bounds memory access when the provided index points to a position beyond the end of `self`’s storage. Additionally, the `i > startIndex` check needs to be done after scalar alignment, not before, as alignment can round the index down to `startIndex`. rdar://89497074&89495373
1 parent 922201e commit 5b364c4

File tree

1 file changed

+8
-2
lines changed

1 file changed

+8
-2
lines changed

stdlib/public/core/StringCharacterView.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,16 @@ extension String: BidirectionalCollection {
6868
/// `startIndex`.
6969
/// - Returns: The index value immediately before `i`.
7070
public func index(before i: Index) -> Index {
71-
_precondition(i > startIndex, "String index is out of bounds")
72-
7371
// TODO: known-ASCII fast path, single-scalar-grapheme fast path, etc.
72+
73+
// Note: bounds checking in `index(before:)` is tricky as scalar aligning an
74+
// index may need to access storage, but it may also move it closer towards
75+
// the `startIndex`. Therefore, we must check against the `endIndex` before
76+
// aligning, but we need to delay the `i > startIndex` check until after.
77+
_precondition(i <= endIndex, "String index is out of bounds")
7478
let i = _guts.scalarAlign(i)
79+
_precondition(i > startIndex, "String index is out of bounds")
80+
7581
let stride = _characterStride(endingAt: i)
7682
let priorOffset = i._encodedOffset &- stride
7783
return Index(

0 commit comments

Comments
 (0)