Merge pull request #2467 from nspcc-dev/limit-eq

vm: adjust comparable limit for ByteArray
This commit is contained in:
Roman Khimov 2022-05-10 19:53:20 +03:00 committed by GitHub
commit 2b79a162ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 13 deletions

View file

@ -27,8 +27,8 @@ const (
// MaxClonableNumOfItems is the maximum number of items that can be cloned in structs.
MaxClonableNumOfItems = MaxDeserialized
// MaxByteArrayComparableSize is the maximum allowed length of a ByteArray for Equals method.
// It is set to be the maximum uint16 value.
MaxByteArrayComparableSize = math.MaxUint16
// It is set to be the maximum uint16 value + 1.
MaxByteArrayComparableSize = math.MaxUint16 + 1
// MaxKeySize is the maximum size of a map key.
MaxKeySize = 64
)
@ -263,19 +263,31 @@ func (i *Struct) equalStruct(s *Struct, limit *int) bool {
} else if len(i.value) != len(s.value) {
return false
}
var maxComparableSize = MaxByteArrayComparableSize
for j := range i.value {
*limit--
if *limit == 0 {
panic(errTooBigElements)
}
sa, oka := i.value[j].(*Struct)
sb, okb := s.value[j].(*Struct)
if oka && okb {
if !sa.equalStruct(sb, limit) {
arr, ok := i.value[j].(*ByteArray)
if ok {
if !arr.equalsLimited(s.value[j], &maxComparableSize) {
return false
}
} else {
if maxComparableSize == 0 {
panic(errTooBigComparable)
}
maxComparableSize--
sa, oka := i.value[j].(*Struct)
sb, okb := s.value[j].(*Struct)
if oka && okb {
if !sa.equalStruct(sb, limit) {
return false
}
} else if !i.value[j].Equals(s.value[j]) {
return false
}
} else if !i.value[j].Equals(s.value[j]) {
return false
}
}
return true
@ -594,19 +606,39 @@ func (i ByteArray) TryInteger() (*big.Int, error) {
// Equals implements the Item interface.
func (i *ByteArray) Equals(s Item) bool {
if len(*i) > MaxByteArrayComparableSize {
var limit = MaxByteArrayComparableSize
return i.equalsLimited(s, &limit)
}
// equalsLimited compares ByteArray with provided stackitem using the limit.
func (i *ByteArray) equalsLimited(s Item, limit *int) bool {
if i == nil {
return s == nil
}
lCurr := len(*i)
if lCurr > *limit || *limit == 0 {
panic(errTooBigComparable)
}
if i == s {
return true
} else if s == nil {
var comparedSize = 1
defer func() { *limit -= comparedSize }()
if s == nil {
return false
}
val, ok := s.(*ByteArray)
if !ok {
return false
}
if len(*val) > MaxByteArrayComparableSize {
comparedSize = lCurr
lOther := len(*val)
if lOther > comparedSize {
comparedSize = lOther
}
if i == val {
return true
}
if lOther > *limit {
panic(errTooBigComparable)
}
return bytes.Equal(*i, *val)

View file

@ -809,6 +809,63 @@ func TestEQUAL(t *testing.T) {
t.Run("Buffer", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{42}), stackitem.NewBuffer([]byte{42})))
}
func TestEQUALByteArrayWithLimit(t *testing.T) {
prog := makeProgram(opcode.EQUAL)
t.Run("fits limit, equal", func(t *testing.T) {
args := make([]stackitem.Item, 2)
for i := range args {
args[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize/2)),
stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize/2)),
})
}
getTestFuncForVM(prog, true, args[0], args[1])(t)
})
t.Run("exceeds limit", func(t *testing.T) {
args := make([]stackitem.Item, 2)
for i := range args {
args[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize/2+1)),
stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize/2)),
})
}
getTestFuncForVM(prog, nil, args[0], args[1])(t) // should FAULT due to comparable limit exceeding
})
t.Run("fits limit, second elements are not equal", func(t *testing.T) {
args := make([]stackitem.Item, 2)
for i := range args {
args[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize-1)),
stackitem.NewBuffer(make([]byte, 1)),
})
}
getTestFuncForVM(prog, false, args[0], args[1])(t) // no limit is exceeded, but the second struct item is a Buffer.
})
t.Run("fits limit, equal", func(t *testing.T) {
args := make([]stackitem.Item, 2)
buf := stackitem.NewBuffer(make([]byte, 100500)) // takes only 1 comparable unit despite its length
for i := range args {
args[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize-1)),
buf,
})
}
getTestFuncForVM(prog, true, args[0], args[1])(t) // should HALT, because no limit exceeded
})
t.Run("exceeds limit, equal", func(t *testing.T) {
args := make([]stackitem.Item, 2)
buf := stackitem.NewBuffer(make([]byte, 100500)) // takes only 1 comparable unit despite its length
for i := range args {
args[i] = stackitem.NewStruct([]stackitem.Item{
stackitem.NewByteArray(make([]byte, stackitem.MaxByteArrayComparableSize-1)), // MaxByteArrayComparableSize-1 comparable units
buf, // 1 comparable unit
buf, // 1 comparable unit
})
}
getTestFuncForVM(prog, nil, args[0], args[1])(t) // should FAULT, because limit is exceeded:
})
}
func runWithArgs(t *testing.T, prog []byte, result interface{}, args ...interface{}) {
getTestFuncForVM(prog, result, args...)(t)
}