diff --git a/PENDING.md b/PENDING.md index bc9cb8cb5b..f3117616e5 100644 --- a/PENDING.md +++ b/PENDING.md @@ -101,6 +101,8 @@ IMPROVEMENTS * \#2509 Sanitize all usage of Dec.RoundInt64() * [\#556](https://github.com/cosmos/cosmos-sdk/issues/556) Increase `BaseApp` test coverage. + * [\#3552](https://github.com/cosmos/cosmos-sdk/pull/3552) Validate bit length when + deserializing `Int` types. * Tendermint diff --git a/types/int.go b/types/int.go index 0ac8737a52..e19809548f 100644 --- a/types/int.go +++ b/types/int.go @@ -2,12 +2,15 @@ package types import ( "encoding/json" + "fmt" "testing" "math/big" "math/rand" ) +const maxBitLen = 255 + func newIntegerFromString(s string) (*big.Int, bool) { return new(big.Int).SetString(s, 0) } @@ -58,9 +61,21 @@ func marshalAmino(i *big.Int) (string, error) { return string(bz), err } +func unmarshalText(i *big.Int, text string) error { + if err := i.UnmarshalText([]byte(text)); err != nil { + return err + } + + if i.BitLen() > maxBitLen { + return fmt.Errorf("integer out of range: %s", text) + } + + return nil +} + // UnmarshalAmino for custom decoding scheme func unmarshalAmino(i *big.Int, text string) (err error) { - return i.UnmarshalText([]byte(text)) + return unmarshalText(i, text) } // MarshalJSON for custom encoding scheme @@ -81,12 +96,13 @@ func unmarshalJSON(i *big.Int, bz []byte) error { if err != nil { return err } - return i.UnmarshalText([]byte(text)) + + return unmarshalText(i, text) } // Int wraps integer with 256 bit range bound // Checks overflow, underflow and division by zero -// Exists in range from -(2^255-1) to 2^255-1 +// Exists in range from -(2^maxBitLen-1) to 2^maxBitLen-1 type Int struct { i *big.Int } @@ -103,7 +119,7 @@ func NewInt(n int64) Int { // NewIntFromBigInt constructs Int from big.Int func NewIntFromBigInt(i *big.Int) Int { - if i.BitLen() > 255 { + if i.BitLen() > maxBitLen { panic("NewIntFromBigInt() out of bound") } return Int{i} @@ -116,7 +132,7 @@ func NewIntFromString(s string) (res Int, ok bool) { return } // Check overflow - if i.BitLen() > 255 { + if i.BitLen() > maxBitLen { ok = false return } @@ -134,7 +150,7 @@ func NewIntWithDecimal(n int64, dec int) Int { i.Mul(big.NewInt(n), exp) // Check overflow - if i.BitLen() > 255 { + if i.BitLen() > maxBitLen { panic("NewIntWithDecimal() out of bound") } return Int{i} @@ -210,7 +226,7 @@ func (i Int) LTE(i2 Int) bool { func (i Int) Add(i2 Int) (res Int) { res = Int{add(i.i, i2.i)} // Check overflow - if res.i.BitLen() > 255 { + if res.i.BitLen() > maxBitLen { panic("Int overflow") } return @@ -225,7 +241,7 @@ func (i Int) AddRaw(i2 int64) Int { func (i Int) Sub(i2 Int) (res Int) { res = Int{sub(i.i, i2.i)} // Check overflow - if res.i.BitLen() > 255 { + if res.i.BitLen() > maxBitLen { panic("Int overflow") } return @@ -239,12 +255,12 @@ func (i Int) SubRaw(i2 int64) Int { // Mul multiples two Ints func (i Int) Mul(i2 Int) (res Int) { // Check overflow - if i.i.BitLen()+i2.i.BitLen()-1 > 255 { + if i.i.BitLen()+i2.i.BitLen()-1 > maxBitLen { panic("Int overflow") } res = Int{mul(i.i, i2.i)} // Check overflow if sign of both are same - if res.i.BitLen() > 255 { + if res.i.BitLen() > maxBitLen { panic("Int overflow") } return diff --git a/types/int_test.go b/types/int_test.go index a6c3c90023..20a2f3f61b 100644 --- a/types/int_test.go +++ b/types/int_test.go @@ -631,3 +631,23 @@ func TestSafeSub(t *testing.T) { ) } } + +func TestSerializationOverflow(t *testing.T) { + bx, _ := new(big.Int).SetString("91888242871839275229946405745257275988696311157297823662689937894645226298583", 10) + x := Int{bx} + y := new(Int) + + // require amino deserialization to fail due to overflow + xStr, err := x.MarshalAmino() + require.NoError(t, err) + + err = y.UnmarshalAmino(xStr) + require.Error(t, err) + + // require JSON deserialization to fail due to overflow + bz, err := x.MarshalJSON() + require.NoError(t, err) + + err = y.UnmarshalJSON(bz) + require.Error(t, err) +}