fix: amend incorrect int bound check (#65)

This commit is contained in:
gabrielmer 2023-11-24 17:18:23 +01:00 committed by GitHub
parent 85b7ea093c
commit 230e226da0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 28 deletions

View File

@ -54,7 +54,7 @@ type
IntOverflowError* = object of JsonReaderError IntOverflowError* = object of JsonReaderError
isNegative: bool isNegative: bool
absIntVal: uint64 absIntVal: BiggestUint
Json.setReader JsonReader Json.setReader JsonReader
@ -115,7 +115,7 @@ proc raiseUnexpectedValue*(r: JsonReader, msg: string) {.noreturn, raises: [Json
ex.msg = msg ex.msg = msg
raise ex raise ex
proc raiseIntOverflow*(r: JsonReader, absIntVal: uint64, isNegative: bool) {.noreturn, raises: [JsonReaderError].} = proc raiseIntOverflow*(r: JsonReader, absIntVal: BiggestUint, isNegative: bool) {.noreturn, raises: [JsonReaderError].} =
var ex = new IntOverflowError var ex = new IntOverflowError
ex.assignLineNumber(r) ex.assignLineNumber(r)
ex.absIntVal = absIntVal ex.absIntVal = absIntVal
@ -173,13 +173,6 @@ proc skipToken*(r: var JsonReader, tk: TokKind) {.raises: [IOError, JsonReaderEr
r.requireToken tk r.requireToken tk
r.lexer.next() r.lexer.next()
func maxAbsValue(T: type[SomeInteger]): uint64 {.compileTime.} =
when T is int8 : 128'u64
elif T is int16: 32768'u64
elif T is int32: 2147483648'u64
elif T is int64: 9223372036854775808'u64
else: uint64(high(T))
proc parseJsonNode(r: var JsonReader): JsonNode proc parseJsonNode(r: var JsonReader): JsonNode
{.gcsafe, raises: [IOError, JsonReaderError].} {.gcsafe, raises: [IOError, JsonReaderError].}
@ -194,7 +187,7 @@ proc readJsonNodeField(r: var JsonReader, field: var JsonNode)
field = r.parseJsonNode() field = r.parseJsonNode()
proc parseJsonNode(r: var JsonReader): JsonNode = proc parseJsonNode(r: var JsonReader): JsonNode =
const maxIntValue = maxAbsValue(BiggestInt) const maxIntValue: BiggestUint = BiggestInt.high.BiggestUint + 1
case r.lexer.tok case r.lexer.tok
of tkCurlyLe: of tkCurlyLe:
@ -245,7 +238,7 @@ proc parseJsonNode(r: var JsonReader): JsonNode =
else: else:
# `0 - x` is a magical trick that turns the unsigned # `0 - x` is a magical trick that turns the unsigned
# value into its negative signed counterpart: # value into its negative signed counterpart:
result = JsonNode(kind: JInt, num: cast[int64](uint64(0) - r.lexer.absIntVal)) result = JsonNode(kind: JInt, num: cast[BiggestInt](BiggestUint(0) - r.lexer.absIntVal))
r.lexer.next() r.lexer.next()
of tkFloat: of tkFloat:
@ -587,20 +580,24 @@ proc readValue*[T](r: var JsonReader, value: var T)
r.parseEnum(value) r.parseEnum(value)
r.lexer.next() r.lexer.next()
elif value is SomeInteger: elif value is SomeSignedInt:
type TargetType = type(value) type TargetType = type(value)
const maxValidValue = maxAbsValue(TargetType) let
isNegative = tok == tkNegativeInt
maxValidAbsValue: BiggestUint =
if isNegative:
TargetType.high.BiggestUint + 1
else:
TargetType.high.BiggestUint
let isNegative = tok == tkNegativeInt if r.lexer.absIntVal > maxValidAbsValue:
if r.lexer.absIntVal > maxValidValue + uint64(isNegative): r.raiseIntOverflow(r.lexer.absIntVal, isNegative)
r.raiseIntOverflow r.lexer.absIntVal, isNegative
case tok case tok
of tkInt: of tkInt:
value = TargetType(r.lexer.absIntVal) value = TargetType(r.lexer.absIntVal)
of tkNegativeInt: of tkNegativeInt:
when value is SomeSignedInt: if r.lexer.absIntVal == maxValidAbsValue:
if r.lexer.absIntVal == maxValidValue:
# We must handle this as a special case because it would be illegal # We must handle this as a special case because it would be illegal
# to convert a value like 128 to int8 before negating it. The max # to convert a value like 128 to int8 before negating it. The max
# int8 value is 127 (while the minimum is -128). # int8 value is 127 (while the minimum is -128).
@ -608,7 +605,18 @@ proc readValue*[T](r: var JsonReader, value: var T)
else: else:
value = -TargetType(r.lexer.absIntVal) value = -TargetType(r.lexer.absIntVal)
else: else:
r.raiseIntOverflow r.lexer.absIntVal, true r.raiseUnexpectedToken etInt
r.lexer.next()
elif value is SomeUnsignedInt:
type TargetType = type(value)
if r.lexer.absIntVal > TargetType.high.BiggestUint:
r.raiseIntOverflow(r.lexer.absIntVal, isNegative = false)
case tok
of tkInt:
value = TargetType(r.lexer.absIntVal)
else: else:
r.raiseUnexpectedToken etInt r.raiseUnexpectedToken etInt
r.lexer.next() r.lexer.next()

View File

@ -647,14 +647,36 @@ suite "toJson tests":
""" """
test "max unsigned value": test "max unsigned value":
var uintVal = not uint64(0) var uintVal = not BiggestUint(0)
let jsonValue = Json.encode(uintVal) let jsonValue = Json.encode(uintVal)
check: check:
jsonValue == "18446744073709551615" jsonValue == "18446744073709551615"
Json.decode(jsonValue, uint64) == uintVal Json.decode(jsonValue, BiggestUint) == uintVal
expect JsonReaderError: expect JsonReaderError:
discard Json.decode(jsonValue, uint64, mode = Portable) discard Json.decode(jsonValue, BiggestUint, mode = Portable)
test "max signed value":
let intVal = BiggestInt.high
let validJsonValue = Json.encode(intVal)
let invalidJsonValue = "9223372036854775808"
check:
validJsonValue == "9223372036854775807"
Json.decode(validJsonValue, BiggestInt) == intVal
expect IntOverflowError:
discard Json.decode(invalidJsonValue, BiggestInt)
test "min signed value":
let intVal = BiggestInt.low
let validJsonValue = Json.encode(intVal)
let invalidJsonValue = "-9223372036854775809"
check:
validJsonValue == "-9223372036854775808"
Json.decode(validJsonValue, BiggestInt) == intVal
expect IntOverflowError:
discard Json.decode(invalidJsonValue, BiggestInt)
test "Unusual field names": test "Unusual field names":
let r = HasUnusualFieldNames(`type`: "uint8", renamedField: "field") let r = HasUnusualFieldNames(`type`: "uint8", renamedField: "field")