mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-21 06:45:27 +00:00
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
```
This commit is contained in:
@@ -527,7 +527,6 @@ proc parseSaturatedNatural*(s: openArray[char], b: var int): int {.
|
||||
proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int =
|
||||
var
|
||||
res = 0.BiggestUInt
|
||||
prev = 0.BiggestUInt
|
||||
i = 0
|
||||
if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
|
||||
integerOutOfRangeError()
|
||||
@@ -535,8 +534,11 @@ proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int =
|
||||
if i < s.len and s[i] in {'0'..'9'}:
|
||||
b = 0
|
||||
while i < s.len and s[i] in {'0'..'9'}:
|
||||
prev = res
|
||||
res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
|
||||
if res > BiggestUInt.high div 10: # Highest value that you can multiply 10 without overflow
|
||||
integerOutOfRangeError()
|
||||
res = res * 10
|
||||
let prev = res
|
||||
res += (ord(s[i]) - ord('0')).BiggestUInt
|
||||
if prev > res:
|
||||
integerOutOfRangeError()
|
||||
inc(i)
|
||||
|
||||
@@ -6,6 +6,54 @@ import unittest, strutils
|
||||
|
||||
block: # parseutils
|
||||
check: parseBiggestUInt("0") == 0'u64
|
||||
check: parseBiggestUInt("1") == 1'u64
|
||||
check: parseBiggestUInt("2") == 2'u64
|
||||
check: parseBiggestUInt("10") == 10'u64
|
||||
check: parseBiggestUInt("11") == 11'u64
|
||||
check: parseBiggestUInt("99") == 99'u64
|
||||
check: parseBiggestUInt("123") == 123'u64
|
||||
check: parseBiggestUInt("9876") == 9876'u64
|
||||
check: parseBiggestUInt("1_234") == 1234'u64
|
||||
check: parseBiggestUInt("123__4") == 1234'u64
|
||||
for i in 1.BiggestUInt .. 9.BiggestUInt:
|
||||
var x = i
|
||||
for j in 1 .. 19:
|
||||
check parseBiggestUInt((i + '0'.uint).char.repeat j) == x
|
||||
x *= 10
|
||||
x += i
|
||||
check: parseBiggestUInt("18446744073709551609") == 0xFFFF_FFFF_FFFF_FFF9'u64
|
||||
check: parseBiggestUInt("18446744073709551610") == 0xFFFF_FFFF_FFFF_FFFA'u64
|
||||
check: parseBiggestUInt("18446744073709551611") == 0xFFFF_FFFF_FFFF_FFFB'u64
|
||||
check: parseBiggestUInt("18446744073709551612") == 0xFFFF_FFFF_FFFF_FFFC'u64
|
||||
check: parseBiggestUInt("18446744073709551613") == 0xFFFF_FFFF_FFFF_FFFD'u64
|
||||
check: parseBiggestUInt("18446744073709551614") == 0xFFFF_FFFF_FFFF_FFFE'u64
|
||||
check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("18446744073709551616")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("18446744073709551617")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("18446744073709551618")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("18446744073709551619")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("18446744073709551620")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("18446744073709551621")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("18446744073709551622")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("18446744073709551623")
|
||||
expect(ValueError):
|
||||
for i in 0 .. 999:
|
||||
discard parseBiggestUInt("18446744073709552" & intToStr(i, 3))
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("22751622367522324480000000")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("41404969074137497600000000")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("20701551093035827200000000000000000")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("225462255024603136000000000000000000")
|
||||
expect(ValueError):
|
||||
discard parseBiggestUInt("204963831854661632000000000000000000")
|
||||
|
||||
Reference in New Issue
Block a user