fix: avoid integer overflow in mapping.Unmarshal (#3582)
This commit is contained in:
@@ -609,24 +609,11 @@ func (u *Unmarshaler) processFieldPrimitiveWithJSONNumber(fieldType reflect.Type
|
|||||||
target := reflect.New(Deref(fieldType)).Elem()
|
target := reflect.New(Deref(fieldType)).Elem()
|
||||||
|
|
||||||
switch typeKind {
|
switch typeKind {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||||
iValue, err := v.Int64()
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
if err != nil {
|
if err := setValueFromString(typeKind, target, v.String()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
target.SetInt(iValue)
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
iValue, err := v.Int64()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if iValue < 0 {
|
|
||||||
return fmt.Errorf("unmarshal %q with bad value %q", fullName, v.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
target.SetUint(uint64(iValue))
|
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
fValue, err := v.Float64()
|
fValue, err := v.Float64()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -569,6 +569,330 @@ func TestUnmarshalIntWithString(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalInt8WithOverflow(t *testing.T) {
|
||||||
|
t.Run("int8 from string", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int8 `key:"int,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": "8589934592", // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int8 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int8 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int8 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int8 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("-8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int8 from int64", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int8 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": int64(1) << 36, // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalInt16WithOverflow(t *testing.T) {
|
||||||
|
t.Run("int16 from string", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int16 `key:"int,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": "8589934592", // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int16 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int16 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int16 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int16 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("-8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int16 from int64", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int16 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": int64(1) << 36, // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalInt32WithOverflow(t *testing.T) {
|
||||||
|
t.Run("int32 from string", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int32 `key:"int,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": "8589934592", // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int32 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int32 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int32 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int32 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("-8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("int32 from int64", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value int32 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": int64(1) << 36, // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalUint8WithOverflow(t *testing.T) {
|
||||||
|
t.Run("uint8 from string", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint8 `key:"int,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": "8589934592", // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint8 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint8 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint8 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint8 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("-1"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint8 from int64", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint8 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": int64(1) << 36, // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalUint16WithOverflow(t *testing.T) {
|
||||||
|
t.Run("uint16 from string", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint16 `key:"int,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": "8589934592", // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint16 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint16 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint16 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint16 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("-1"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint16 from int64", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint16 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": int64(1) << 36, // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalUint32WithOverflow(t *testing.T) {
|
||||||
|
t.Run("uint32 from string", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint32 `key:"int,string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": "8589934592", // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint32 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint32 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("8589934592"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint32 from json.Number", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint32 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": json.Number("-1"), // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("uint32 from int64", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Value uint32 `key:"int"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"int": int64(1) << 36, // overflow
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnmarshalBoolSliceRequired(t *testing.T) {
|
func TestUnmarshalBoolSliceRequired(t *testing.T) {
|
||||||
type inner struct {
|
type inner struct {
|
||||||
Bools []bool `key:"bools"`
|
Bools []bool `key:"bools"`
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
errUnsupportedType = errors.New("unsupported type on setting field value")
|
errUnsupportedType = errors.New("unsupported type on setting field value")
|
||||||
|
errNumberOverflow = errors.New("integer overflow")
|
||||||
errNumberRange = errors.New("wrong number range setting")
|
errNumberRange = errors.New("wrong number range setting")
|
||||||
optionsCache = make(map[string]optionsCacheValue)
|
optionsCache = make(map[string]optionsCacheValue)
|
||||||
cacheLock sync.RWMutex
|
cacheLock sync.RWMutex
|
||||||
@@ -482,22 +483,61 @@ func parseSegments(val string) []string {
|
|||||||
return segments
|
return segments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setIntValue(value reflect.Value, v any, min, max int64) error {
|
||||||
|
iv := v.(int64)
|
||||||
|
if iv < min || iv > max {
|
||||||
|
return errNumberOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
value.SetInt(iv)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func setMatchedPrimitiveValue(kind reflect.Kind, value reflect.Value, v any) error {
|
func setMatchedPrimitiveValue(kind reflect.Kind, value reflect.Value, v any) error {
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
value.SetBool(v.(bool))
|
value.SetBool(v.(bool))
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
return nil
|
||||||
|
case reflect.Int: // int depends on int size, 32 or 64
|
||||||
|
return setIntValue(value, v, math.MinInt, math.MaxInt)
|
||||||
|
case reflect.Int8:
|
||||||
|
return setIntValue(value, v, math.MinInt8, math.MaxInt8)
|
||||||
|
case reflect.Int16:
|
||||||
|
return setIntValue(value, v, math.MinInt16, math.MaxInt16)
|
||||||
|
case reflect.Int32:
|
||||||
|
return setIntValue(value, v, math.MinInt32, math.MaxInt32)
|
||||||
|
case reflect.Int64:
|
||||||
value.SetInt(v.(int64))
|
value.SetInt(v.(int64))
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
return nil
|
||||||
|
case reflect.Uint: // uint depends on int size, 32 or 64
|
||||||
|
return setUintValue(value, v, math.MaxUint)
|
||||||
|
case reflect.Uint8:
|
||||||
|
return setUintValue(value, v, math.MaxUint8)
|
||||||
|
case reflect.Uint16:
|
||||||
|
return setUintValue(value, v, math.MaxUint16)
|
||||||
|
case reflect.Uint32:
|
||||||
|
return setUintValue(value, v, math.MaxUint32)
|
||||||
|
case reflect.Uint64:
|
||||||
value.SetUint(v.(uint64))
|
value.SetUint(v.(uint64))
|
||||||
|
return nil
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
value.SetFloat(v.(float64))
|
value.SetFloat(v.(float64))
|
||||||
|
return nil
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
value.SetString(v.(string))
|
value.SetString(v.(string))
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
return errUnsupportedType
|
return errUnsupportedType
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUintValue(value reflect.Value, v any, boundary uint64) error {
|
||||||
|
iv := v.(uint64)
|
||||||
|
if iv > boundary {
|
||||||
|
return errNumberOverflow
|
||||||
|
}
|
||||||
|
|
||||||
|
value.SetUint(iv)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user