vm: limit ByteArray comparable length

Close #2460.
This commit is contained in:
Anna Shaleva 2022-05-06 10:27:48 +03:00
parent 740488f7f3
commit 23b78b0be2
2 changed files with 100 additions and 11 deletions

View file

@ -263,11 +263,22 @@ 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)
}
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 {
@ -278,6 +289,7 @@ func (i *Struct) equalStruct(s *Struct, limit *int) bool {
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+1)), // MaxByteArrayComparableSize is even, thus use +1
})
}
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+2)),
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)
}