Handle overflows

This commit is contained in:
Thomas Pelletier
2021-03-08 21:01:53 -05:00
parent 90f3b658c6
commit 87b9d1cf98
2 changed files with 147 additions and 7 deletions
@@ -1047,15 +1047,15 @@ func TestUnmarshalOverflow(t *testing.T) {
testCases := []TestCase{
{
desc: "byte",
input: `u8 = 300`,
input: `U8 = 300`,
},
{
desc: "int8",
input: `i8 = 300`,
input: `I8 = 300`,
},
{
desc: "float32",
input: `f32 = 1e300`,
input: `F32 = 1e300`,
},
}
+144 -4
View File
@@ -4,6 +4,7 @@ package reflectbuild
import (
"fmt"
"math"
"reflect"
"strings"
)
@@ -495,33 +496,172 @@ func convert(t reflect.Type, value reflect.Value) (reflect.Value, error) {
return result.Elem(), nil
}
type IntegerOverflowErr struct {
value int64
min int64
max int64
kind reflect.Kind
}
func (e IntegerOverflowErr) Error() string {
return fmt.Sprintf("integer overflow: cannot store %d in %s [%d, %d]", e.value, e.kind, e.min, e.max)
}
const maxInt = int64(^uint(0) >> 1)
const minInt = -maxInt - 1
func convertInt(t reflect.Type, value reflect.Value) (reflect.Value, error) {
switch value.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return value.Convert(t), nil // reflect.TypeOf(int64(0))
x := value.Int()
switch t.Kind() {
case reflect.Int:
if x > maxInt || x < minInt {
return value, IntegerOverflowErr{
value: x,
min: minInt,
max: maxInt,
kind: t.Kind(),
}
}
case reflect.Int8:
if x > math.MaxInt8 || x < math.MinInt8 {
return value, IntegerOverflowErr{
value: x,
min: math.MinInt8,
max: math.MaxInt8,
kind: t.Kind(),
}
}
case reflect.Int16:
if x > math.MaxInt16 || x < math.MinInt16 {
return value, IntegerOverflowErr{
value: x,
min: math.MinInt16,
max: math.MaxInt16,
kind: t.Kind(),
}
}
case reflect.Int32:
if x > math.MaxInt32 || x < math.MinInt32 {
return value, IntegerOverflowErr{
value: x,
min: math.MinInt32,
max: math.MaxInt32,
kind: t.Kind(),
}
}
case reflect.Int64:
if x > math.MaxInt64 || x < math.MinInt64 {
return value, IntegerOverflowErr{
value: x,
min: math.MinInt64,
max: math.MaxInt64,
kind: t.Kind(),
}
}
}
return value.Convert(t), nil
default:
return value, fmt.Errorf("cannot convert %s to integer (%s)", value.Kind(), t.Kind())
}
}
type UnsignedIntegerOverflowErr struct {
value uint64
max uint64
kind reflect.Kind
}
func (e UnsignedIntegerOverflowErr) Error() string {
return fmt.Sprintf("unsigned integer overflow: cannot store %d in %s [max %d]", e.value, e.kind, e.max)
}
func convertUint(t reflect.Type, value reflect.Value) (reflect.Value, error) {
switch value.Kind() {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
err := convertUintOverflowCheck(t.Kind(), value.Uint())
if err != nil {
return value, err
}
return value.Convert(t), nil // reflect.TypeOf(int64(0))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
x := value.Int()
if x < 0 {
return value, fmt.Errorf("cannot store negative integer '%d' into %s", x, t.Kind())
signed := value.Int()
if signed < 0 {
return value, fmt.Errorf("cannot store negative integer '%d' into %s", signed, t.Kind())
}
x := uint64(signed)
err := convertUintOverflowCheck(t.Kind(), x)
if err != nil {
return value, err
}
return value.Convert(t), nil
default:
return value, fmt.Errorf("cannot convert %s to unsigned integer (%s)", value.Kind(), t.Kind())
}
}
const maxUint = uint64(^uint(0))
func convertUintOverflowCheck(t reflect.Kind, x uint64) error {
switch t {
case reflect.Uint:
if x > maxUint {
return UnsignedIntegerOverflowErr{
value: x,
max: maxUint,
kind: t,
}
}
case reflect.Uint8:
if x > math.MaxUint8 {
return UnsignedIntegerOverflowErr{
value: x,
max: math.MaxUint8,
kind: t,
}
}
case reflect.Uint16:
if x > math.MaxUint16 {
return UnsignedIntegerOverflowErr{
value: x,
max: math.MaxUint16,
kind: t,
}
}
case reflect.Uint32:
if x > math.MaxUint32 {
return UnsignedIntegerOverflowErr{
value: x,
max: math.MaxUint32,
kind: t,
}
}
case reflect.Uint64:
if x > math.MaxUint64 {
return UnsignedIntegerOverflowErr{
value: x,
max: math.MaxUint64,
kind: t,
}
}
}
return nil
}
func convertFloat(t reflect.Type, value reflect.Value) (reflect.Value, error) {
switch value.Kind() {
case reflect.Float32, reflect.Float64:
if t.Kind() == reflect.Float32 {
f := value.Float()
if f > math.MaxFloat32 {
return value, fmt.Errorf("float overflow: %f does not fit in %s [max %f]")
}
}
return value.Convert(t), nil
default:
return value, fmt.Errorf("cannot convert %s to integer (%s)", value.Kind(), t.Kind())