Skip to content

Commit 95b1dda

Browse files
authored
Fix parseBiggestUInt to detect overflow (#24649)
With some inputs larger than `BiggestUInt.high`, `parseBiggestUInt` proc in `parseutils.nim` fails to detect overflow and returns random value. This is because `rawParseUInt` try to detects overflow with `if prev > res:` but it doesn't detects the overflow from multiplication. It is possible that `x *= 10` causes overflow and resulting value is larger than original value. Here is example values larger than `BiggestUInt.high` but `parseBiggestUInt` returns without detecting overflow: ``` 22751622367522324480000000 41404969074137497600000000 20701551093035827200000000000000000 22546225502460313600000000000000000 204963831854661632000000000000000000 ``` Following code search for values larger than `BiggestUInt.high` and `parseBiggestUInt` cannot detect overflow: ```nim import std/[strutils] const # Increase this to extend search range NBits = 34'u NBitsMax1 = 1'u shl NBits NBitsMax = NBitsMax1 - 1'u # Increase this when there are too many results and want to see only larger result. MinMultiply10 = 14 var nfound = 0 for i in (NBitsMax div 10'u + 1'u) .. NBitsMax: var x = i n10 = 0 for j in 0 ..< NBits: let px = x x = (x * 10'u) and NBitsMax if x < px: break inc n10 if n10 >= MinMultiply10: echo "i = ", i echo "uint: ", (i shl (64'u - NBits)), '0'.repeat n10 inc nfound if nfound > 15: break echo "found: ", nfound ```
1 parent 1f9cac1 commit 95b1dda

File tree

2 files changed

+53
-3
lines changed

2 files changed

+53
-3
lines changed

lib/pure/parseutils.nim

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -527,16 +527,18 @@ proc parseSaturatedNatural*(s: openArray[char], b: var int): int {.
527527
proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int =
528528
var
529529
res = 0.BiggestUInt
530-
prev = 0.BiggestUInt
531530
i = 0
532531
if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
533532
integerOutOfRangeError()
534533
if i < s.len and s[i] == '+': inc(i) # Allow
535534
if i < s.len and s[i] in {'0'..'9'}:
536535
b = 0
537536
while i < s.len and s[i] in {'0'..'9'}:
538-
prev = res
539-
res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
537+
if res > BiggestUInt.high div 10: # Highest value that you can multiply 10 without overflow
538+
integerOutOfRangeError()
539+
res = res * 10
540+
let prev = res
541+
res += (ord(s[i]) - ord('0')).BiggestUInt
540542
if prev > res:
541543
integerOutOfRangeError()
542544
inc(i)

tests/stdlib/tparseuints.nim

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,54 @@ import unittest, strutils
66

77
block: # parseutils
88
check: parseBiggestUInt("0") == 0'u64
9+
check: parseBiggestUInt("1") == 1'u64
10+
check: parseBiggestUInt("2") == 2'u64
11+
check: parseBiggestUInt("10") == 10'u64
12+
check: parseBiggestUInt("11") == 11'u64
13+
check: parseBiggestUInt("99") == 99'u64
14+
check: parseBiggestUInt("123") == 123'u64
15+
check: parseBiggestUInt("9876") == 9876'u64
16+
check: parseBiggestUInt("1_234") == 1234'u64
17+
check: parseBiggestUInt("123__4") == 1234'u64
18+
for i in 1.BiggestUInt .. 9.BiggestUInt:
19+
var x = i
20+
for j in 1 .. 19:
21+
check parseBiggestUInt((i + '0'.uint).char.repeat j) == x
22+
x *= 10
23+
x += i
24+
check: parseBiggestUInt("18446744073709551609") == 0xFFFF_FFFF_FFFF_FFF9'u64
25+
check: parseBiggestUInt("18446744073709551610") == 0xFFFF_FFFF_FFFF_FFFA'u64
26+
check: parseBiggestUInt("18446744073709551611") == 0xFFFF_FFFF_FFFF_FFFB'u64
27+
check: parseBiggestUInt("18446744073709551612") == 0xFFFF_FFFF_FFFF_FFFC'u64
28+
check: parseBiggestUInt("18446744073709551613") == 0xFFFF_FFFF_FFFF_FFFD'u64
29+
check: parseBiggestUInt("18446744073709551614") == 0xFFFF_FFFF_FFFF_FFFE'u64
930
check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64
1031
expect(ValueError):
1132
discard parseBiggestUInt("18446744073709551616")
33+
expect(ValueError):
34+
discard parseBiggestUInt("18446744073709551617")
35+
expect(ValueError):
36+
discard parseBiggestUInt("18446744073709551618")
37+
expect(ValueError):
38+
discard parseBiggestUInt("18446744073709551619")
39+
expect(ValueError):
40+
discard parseBiggestUInt("18446744073709551620")
41+
expect(ValueError):
42+
discard parseBiggestUInt("18446744073709551621")
43+
expect(ValueError):
44+
discard parseBiggestUInt("18446744073709551622")
45+
expect(ValueError):
46+
discard parseBiggestUInt("18446744073709551623")
47+
expect(ValueError):
48+
for i in 0 .. 999:
49+
discard parseBiggestUInt("18446744073709552" & intToStr(i, 3))
50+
expect(ValueError):
51+
discard parseBiggestUInt("22751622367522324480000000")
52+
expect(ValueError):
53+
discard parseBiggestUInt("41404969074137497600000000")
54+
expect(ValueError):
55+
discard parseBiggestUInt("20701551093035827200000000000000000")
56+
expect(ValueError):
57+
discard parseBiggestUInt("225462255024603136000000000000000000")
58+
expect(ValueError):
59+
discard parseBiggestUInt("204963831854661632000000000000000000")

0 commit comments

Comments
 (0)