vm: restrict map key size

This commit is contained in:
Anna Shaleva 2020-09-29 10:56:57 +03:00
parent 1f9b92c295
commit 543fd58e93
5 changed files with 20 additions and 11 deletions

View file

@ -24,6 +24,7 @@ a dialect of Go rather than a complete port of the language:
overhead for all contracts. This can easily be mitigated by first storing values overhead for all contracts. This can easily be mitigated by first storing values
in variables and returning the result. in variables and returning the result.
* lambdas are supported, but closures are not. * lambdas are supported, but closures are not.
* maps are supported, but valid map keys are booleans, integers and strings with length <= 64
## VM API (interop layer) ## VM API (interop layer)
Compiler translates interop function calls into NEO VM syscalls or (for custom Compiler translates interop function calls into NEO VM syscalls or (for custom

View file

@ -27,6 +27,8 @@ const (
// MaxByteArrayComparableSize is the maximum allowed length of ByteArray for Equals method. // MaxByteArrayComparableSize is the maximum allowed length of ByteArray for Equals method.
// It is set to be the maximum uint16 value. // It is set to be the maximum uint16 value.
MaxByteArrayComparableSize = math.MaxUint16 MaxByteArrayComparableSize = math.MaxUint16
// MaxKeySize is the maximum size of map key.
MaxKeySize = 64
) )
// Item represents the "real" value that is pushed on the stack. // Item represents the "real" value that is pushed on the stack.
@ -773,8 +775,8 @@ func (i *Map) Convert(typ Type) (Item, error) {
// Add adds key-value pair to the map. // Add adds key-value pair to the map.
func (i *Map) Add(key, value Item) { func (i *Map) Add(key, value Item) {
if !IsValidMapKey(key) { if err := IsValidMapKey(key); err != nil {
panic("wrong key type") panic(err)
} }
index := i.Index(key) index := i.Index(key)
if index >= 0 { if index >= 0 {
@ -792,12 +794,18 @@ func (i *Map) Drop(index int) {
// IsValidMapKey checks whether it's possible to use given Item as a Map // IsValidMapKey checks whether it's possible to use given Item as a Map
// key. // key.
func IsValidMapKey(key Item) bool { func IsValidMapKey(key Item) error {
switch key.(type) { switch key.(type) {
case *Bool, *BigInteger, *ByteArray: case *Bool, *BigInteger:
return true return nil
case *ByteArray:
size := len(key.Value().([]byte))
if size > MaxKeySize {
return fmt.Errorf("invalid map key size: %d", size)
}
return nil
default: default:
return false return fmt.Errorf("invalid map key of type %s", key.Type())
} }
} }

View file

@ -360,8 +360,8 @@ func FromJSONWithTypes(data []byte) (Item, error) {
key, err := FromJSONWithTypes(arr[i].Key) key, err := FromJSONWithTypes(arr[i].Key)
if err != nil { if err != nil {
return nil, err return nil, err
} else if !IsValidMapKey(key) { } else if err = IsValidMapKey(key); err != nil {
return nil, fmt.Errorf("invalid map key of type %s", key.Type()) return nil, err
} }
value, err := FromJSONWithTypes(arr[i].Value) value, err := FromJSONWithTypes(arr[i].Value)
if err != nil { if err != nil {

View file

@ -88,7 +88,7 @@ func TestFromToJSON(t *testing.T) {
t.Run("BigNestedArray", getTestDecodeFunc(`[[[[[[[[[[[]]]]]]]]]]]`, nil)) t.Run("BigNestedArray", getTestDecodeFunc(`[[[[[[[[[[[]]]]]]]]]]]`, nil))
t.Run("EncodeRecursive", func(t *testing.T) { t.Run("EncodeRecursive", func(t *testing.T) {
// add this item to speed up test a bit // add this item to speed up test a bit
item := NewByteArray(make([]byte, MaxSize/100)) item := NewByteArray(make([]byte, MaxKeySize))
t.Run("Array", func(t *testing.T) { t.Run("Array", func(t *testing.T) {
arr := NewArray([]Item{item}) arr := NewArray([]Item{item})
arr.Append(arr) arr.Append(arr)

View file

@ -1668,8 +1668,8 @@ func validateMapKey(key *Element) {
if key == nil { if key == nil {
panic("no key found") panic("no key found")
} }
if !stackitem.IsValidMapKey(key.Item()) { if err := stackitem.IsValidMapKey(key.Item()); err != nil {
panic("key can't be a collection") panic(err)
} }
} }