From bdb94a90ca41f19795ab28215cb8e0ea02cd98bf Mon Sep 17 00:00:00 2001 From: apocelipes Date: Fri, 13 Oct 2023 05:40:29 +0900 Subject: [PATCH] fix: overflow when parsing a huge uint64 number (#751) Such as 1<<64 - 1, math.MaxInt64 + 1, etc. Sending these values to task arguments will cause a panic. These values are legal golang uint64 values. --- v1/tasks/reflect.go | 11 ++++++----- v1/tasks/reflect_test.go | 6 ++++++ v2/tasks/reflect.go | 11 ++++++----- v2/tasks/reflect_test.go | 6 ++++++ 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/v1/tasks/reflect.go b/v1/tasks/reflect.go index 53aa7eb71..a17a04b51 100644 --- a/v1/tasks/reflect.go +++ b/v1/tasks/reflect.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "reflect" + "strconv" "strings" ) @@ -288,21 +289,21 @@ func getIntValue(theType string, value interface{}) (int64, error) { } func getUintValue(theType string, value interface{}) (uint64, error) { - // We use https://golang.org/pkg/encoding/json/#Decoder.UseNumber when unmarshaling signatures. - // This is because JSON only supports 64-bit floating point numbers and we could lose precision - // when converting from float64 to unsigned integer + // Losing precision only happens in receiving a JSON number from a language like js, + // and receiving a large uint number from golang or python could cause json.Number.Int64 be turned into a panic. + // So we use strconv.ParseUint to correctly parse a uint value. if strings.HasPrefix(fmt.Sprintf("%T", value), "json.Number") { n, ok := value.(json.Number) if !ok { return 0, typeConversionError(value, typesMap[theType].String()) } - intVal, err := n.Int64() + uintVal, err := strconv.ParseUint(string(n), 10, 64) if err != nil { return 0, err } - return uint64(intVal), nil + return uintVal, nil } var n uint64 diff --git a/v1/tasks/reflect_test.go b/v1/tasks/reflect_test.go index 8593666c5..392af5e4b 100644 --- a/v1/tasks/reflect_test.go +++ b/v1/tasks/reflect_test.go @@ -81,6 +81,12 @@ var ( expectedType: "uint64", expectedValue: uint64(185135722552891243), }, + { + name: "uint64", + value: json.Number("9223372036854775808"), // math.MaxInt64 + 1 + expectedType: "uint64", + expectedValue: uint64(9223372036854775808), + }, { name: "float32", value: json.Number("0.5"), diff --git a/v2/tasks/reflect.go b/v2/tasks/reflect.go index 53aa7eb71..a17a04b51 100644 --- a/v2/tasks/reflect.go +++ b/v2/tasks/reflect.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "reflect" + "strconv" "strings" ) @@ -288,21 +289,21 @@ func getIntValue(theType string, value interface{}) (int64, error) { } func getUintValue(theType string, value interface{}) (uint64, error) { - // We use https://golang.org/pkg/encoding/json/#Decoder.UseNumber when unmarshaling signatures. - // This is because JSON only supports 64-bit floating point numbers and we could lose precision - // when converting from float64 to unsigned integer + // Losing precision only happens in receiving a JSON number from a language like js, + // and receiving a large uint number from golang or python could cause json.Number.Int64 be turned into a panic. + // So we use strconv.ParseUint to correctly parse a uint value. if strings.HasPrefix(fmt.Sprintf("%T", value), "json.Number") { n, ok := value.(json.Number) if !ok { return 0, typeConversionError(value, typesMap[theType].String()) } - intVal, err := n.Int64() + uintVal, err := strconv.ParseUint(string(n), 10, 64) if err != nil { return 0, err } - return uint64(intVal), nil + return uintVal, nil } var n uint64 diff --git a/v2/tasks/reflect_test.go b/v2/tasks/reflect_test.go index 3fe9e692d..1892ca319 100644 --- a/v2/tasks/reflect_test.go +++ b/v2/tasks/reflect_test.go @@ -81,6 +81,12 @@ var ( expectedType: "uint64", expectedValue: uint64(185135722552891243), }, + { + name: "uint64", + value: json.Number("9223372036854775808"), // math.MaxInt64 + 1 + expectedType: "uint64", + expectedValue: uint64(9223372036854775808), + }, { name: "float32", value: json.Number("0.5"),