Pārlūkot izejas kodu

fix: avoid integer overflow in mapping.Unmarshal (#3582)

Kevin Wan 1 gadu atpakaļ
vecāks
revīzija
ded2888759
3 mainītis faili ar 369 papildinājumiem un 18 dzēšanām
  1. 3 16
      core/mapping/unmarshaler.go
  2. 324 0
      core/mapping/unmarshaler_test.go
  3. 42 2
      core/mapping/utils.go

+ 3 - 16
core/mapping/unmarshaler.go

@@ -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:
-		iValue, err := v.Int64()
-		if err != nil {
-			return err
-		}
-
-		target.SetInt(iValue)
-	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
-		iValue, err := v.Int64()
-		if err != nil {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		if err := setValueFromString(typeKind, target, v.String()); err != nil {
 			return err
 			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 {

+ 324 - 0
core/mapping/unmarshaler_test.go

@@ -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"`

+ 42 - 2
core/mapping/utils.go

@@ -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
 }
 }