Skip to content

Commit 8d1261e

Browse files
[stdlib] Fix Int(_:radix:) accepting unintentional cases (SR-187)
1 parent 0998e38 commit 8d1261e

File tree

1 file changed

+28
-24
lines changed

1 file changed

+28
-24
lines changed

stdlib/public/core/IntegerParsing.swift.gyb

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,17 @@ internal func _parseUnsignedAsciiAsUIntMax(
7070
/// - Note: For text matching the regular expression "-0+", the result
7171
/// is `0`, not `nil`.
7272
internal func _parseAsciiAsUIntMax(
73-
u16: String.UTF16View, _ radix: Int, _ maximum: UIntMax
73+
utf16: String.UTF16View, _ radix: Int, _ maximum: UIntMax
7474
) -> UIntMax? {
75-
if u16.isEmpty { return nil }
76-
let c = u16.first
77-
if _fastPath(c != _ascii16("-")) {
78-
let unsignedText
79-
= c == _ascii16("+") ? u16.dropFirst() : u16
80-
return _parseUnsignedAsciiAsUIntMax(unsignedText, radix, maximum)
81-
}
82-
else {
83-
return _parseAsciiAsIntMax(u16, radix, 0) == 0 ? 0 : nil
84-
}
75+
if utf16.isEmpty { return nil }
76+
// Parse (optional) sign
77+
let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16)
78+
// Parse digits
79+
guard let result = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, maximum) else { return nil }
80+
// Disallow < 0
81+
if hasMinus && result != 0 { return nil }
82+
// Return
83+
return result
8584
}
8685

8786
/// If text is an ASCII representation in the given `radix` of a
@@ -91,23 +90,28 @@ internal func _parseAsciiAsUIntMax(
9190
/// - Note: For text matching the regular expression "-0+", the result
9291
/// is `0`, not `nil`.
9392
internal func _parseAsciiAsIntMax(
94-
u16: String.UTF16View, _ radix: Int, _ maximum: IntMax
93+
utf16: String.UTF16View, _ radix: Int, _ maximum: IntMax
9594
) -> IntMax? {
9695
_sanityCheck(maximum >= 0, "maximum should be non-negative")
96+
if utf16.isEmpty { return nil }
97+
// Parse (optional) sign
98+
let (digitsUTF16, hasMinus) = _parseOptionalAsciiSign(utf16)
99+
// Parse digits
100+
let absValueMax = UIntMax(bitPattern: maximum) + (hasMinus ? 1 : 0)
101+
guard let absValue = _parseUnsignedAsciiAsUIntMax(digitsUTF16, radix, absValueMax) else { return nil }
102+
// Return signed result
103+
return IntMax(bitPattern: hasMinus ? 0 &- absValue : absValue)
104+
}
97105

98-
if u16.isEmpty { return nil }
99-
100-
// Drop any leading "-"
101-
let negative = u16.first == _ascii16("-")
102-
let absResultText = negative ? u16.dropFirst() : u16
103-
104-
let absResultMax = UIntMax(bitPattern: maximum) + (negative ? 1 : 0)
105-
106-
// Parse the result as unsigned
107-
if let absResult = _parseAsciiAsUIntMax(absResultText, radix, absResultMax) {
108-
return IntMax(bitPattern: negative ? 0 &- absResult : absResult)
106+
/// Strip an optional single leading ASCII plus/minus sign from `utf16`.
107+
private func _parseOptionalAsciiSign(
108+
utf16: String.UTF16View
109+
) -> (digitsUTF16: String.UTF16View, isMinus: Bool) {
110+
switch utf16.first {
111+
case _ascii16("-")?: return (utf16.dropFirst(), true)
112+
case _ascii16("+")?: return (utf16.dropFirst(), false)
113+
default: return (utf16, false)
109114
}
110-
return nil
111115
}
112116

113117
//===--- Loop over all integer types --------------------------------------===//

0 commit comments

Comments
 (0)