diff --git a/src/jsony.nim b/src/jsony.nim index 4621185..7c57b7c 100644 --- a/src/jsony.nim +++ b/src/jsony.nim @@ -105,7 +105,12 @@ proc parseHook*(s: string, i: var int, v: var SomeUnsignedInt) = v2: uint64 = 0 startI = i while i < s.len and s[i] in {'0'..'9'}: - v2 = v2 * 10 + (s[i].ord - '0'.ord).uint64 + let digit = (s[i].ord - '0'.ord).uint64 + # Reject numbers that do not fit the target type instead of silently + # wrapping modulo (unsigned conversions are unchecked). See issue #109. + if v2 > (uint64(high(type(v))) - digit) div 10: + error("Number type to small to contain the number.", i) + v2 = v2 * 10 + digit inc i if startI == i: error("Number expected.", i) @@ -123,14 +128,20 @@ proc parseHook*(s: string, i: var int, v: var SomeSignedInt) = var v2: uint64 inc i parseHook(s, i, v2) - v = -type(v)(v2) + # The negative range extends one past high(T) (e.g. int8 reaches -128). + # Range-check explicitly; release builds do not raise. See issue #109. + if v2 > uint64(high(type(v))) + 1: + error("Number type to small to contain the number.", i) + elif v2 == uint64(high(type(v))) + 1: + v = low(type(v)) + else: + v = -type(v)(v2) else: var v2: uint64 parseHook(s, i, v2) - try: - v = type(v)(v2) - except: + if v2 > uint64(high(type(v))): error("Number type to small to contain the number.", i) + v = type(v)(v2) proc parseHook*(s: string, i: var int, v: var SomeFloat) = ## Will parse float32 and float64. diff --git a/tests/test_numbers.nim b/tests/test_numbers.nim index 867e79d..6c4dc48 100644 --- a/tests/test_numbers.nim +++ b/tests/test_numbers.nim @@ -51,3 +51,18 @@ block: @["hi", "bye", "maybe"] doAssert """[["hi", "bye"], ["maybe"], []]""".fromJson(seq[seq[string]]) == @[@["hi", "bye"], @["maybe"], @[]] + +block: + # Out-of-range integers must raise instead of silently wrapping/truncating. + # See https://github.com/treeform/jsony/issues/109 + doAssertRaises(JsonError): discard "256".fromJson(uint8) + doAssertRaises(JsonError): discard "99999999999999999999999999".fromJson(uint64) + doAssertRaises(JsonError): discard "128".fromJson(int8) + doAssertRaises(JsonError): discard "-129".fromJson(int8) + doAssertRaises(JsonError): discard "300".fromJson(uint8) + # Boundary values still parse correctly. + doAssert "255".fromJson(uint8) == 255'u8 + doAssert "127".fromJson(int8) == 127'i8 + doAssert "-128".fromJson(int8) == -128'i8 + doAssert "65535".fromJson(uint16) == 65535'u16 + doAssert "18446744073709551615".fromJson(uint64) == 18446744073709551615'u64