smartcontract: restrict maximum NEF file size on deserialisation
Port https://github.com/neo-project/neo/pull/2939. Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
b2205940fa
commit
14d98811a5
2 changed files with 32 additions and 4 deletions
|
@ -4,10 +4,12 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NEO Executable Format 3 (NEF3)
|
// NEO Executable Format 3 (NEF3)
|
||||||
|
@ -31,8 +33,6 @@ import (
|
||||||
const (
|
const (
|
||||||
// Magic is a magic File header constant.
|
// Magic is a magic File header constant.
|
||||||
Magic uint32 = 0x3346454E
|
Magic uint32 = 0x3346454E
|
||||||
// MaxScriptLength is the maximum allowed contract script length.
|
|
||||||
MaxScriptLength = 512 * 1024
|
|
||||||
// MaxSourceURLLength is the maximum allowed source URL length.
|
// MaxSourceURLLength is the maximum allowed source URL length.
|
||||||
MaxSourceURLLength = 256
|
MaxSourceURLLength = 256
|
||||||
// compilerFieldSize is the length of `Compiler` File header field in bytes.
|
// compilerFieldSize is the length of `Compiler` File header field in bytes.
|
||||||
|
@ -139,7 +139,7 @@ func (n *File) DecodeBinary(r *io.BinReader) {
|
||||||
r.Err = errInvalidReserved
|
r.Err = errInvalidReserved
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n.Script = r.ReadVarBytes(MaxScriptLength)
|
n.Script = r.ReadVarBytes(stackitem.MaxSize)
|
||||||
if r.Err == nil && len(n.Script) == 0 {
|
if r.Err == nil && len(n.Script) == 0 {
|
||||||
r.Err = errors.New("empty script")
|
r.Err = errors.New("empty script")
|
||||||
return
|
return
|
||||||
|
@ -165,6 +165,9 @@ func (n File) Bytes() ([]byte, error) {
|
||||||
// FileFromBytes returns a NEF File deserialized from the given bytes.
|
// FileFromBytes returns a NEF File deserialized from the given bytes.
|
||||||
func FileFromBytes(source []byte) (File, error) {
|
func FileFromBytes(source []byte) (File, error) {
|
||||||
result := File{}
|
result := File{}
|
||||||
|
if len(source) > stackitem.MaxSize {
|
||||||
|
return result, fmt.Errorf("invalid NEF file size: expected %d at max, got %d", stackitem.MaxSize, len(source))
|
||||||
|
}
|
||||||
r := io.NewBinReaderFromBuf(source)
|
r := io.NewBinReaderFromBuf(source)
|
||||||
result.DecodeBinary(r)
|
result.DecodeBinary(r)
|
||||||
if r.Err != nil {
|
if r.Err != nil {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ func TestEncodeDecodeBinary(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid script length", func(t *testing.T) {
|
t.Run("invalid script length", func(t *testing.T) {
|
||||||
newScript := make([]byte, MaxScriptLength+1)
|
newScript := make([]byte, stackitem.MaxSize+1)
|
||||||
expected.Script = newScript
|
expected.Script = newScript
|
||||||
expected.Checksum = expected.CalculateChecksum()
|
expected.Checksum = expected.CalculateChecksum()
|
||||||
checkDecodeError(t, expected)
|
checkDecodeError(t, expected)
|
||||||
|
@ -126,6 +127,30 @@ func TestBytesFromBytes(t *testing.T) {
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewFileFromBytesLimits(t *testing.T) {
|
||||||
|
expected := File{
|
||||||
|
Header: Header{
|
||||||
|
Magic: Magic,
|
||||||
|
Compiler: "best compiler version 1",
|
||||||
|
},
|
||||||
|
Tokens: []MethodToken{{
|
||||||
|
Hash: random.Uint160(),
|
||||||
|
Method: "someMethod",
|
||||||
|
ParamCount: 3,
|
||||||
|
HasReturn: true,
|
||||||
|
CallFlag: callflag.WriteStates,
|
||||||
|
}},
|
||||||
|
Script: make([]byte, stackitem.MaxSize-100),
|
||||||
|
}
|
||||||
|
expected.Checksum = expected.CalculateChecksum()
|
||||||
|
|
||||||
|
bytes, err := expected.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = FileFromBytes(bytes)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "invalid NEF file size")
|
||||||
|
}
|
||||||
|
|
||||||
func TestMarshalUnmarshalJSON(t *testing.T) {
|
func TestMarshalUnmarshalJSON(t *testing.T) {
|
||||||
expected := &File{
|
expected := &File{
|
||||||
Header: Header{
|
Header: Header{
|
||||||
|
|
Loading…
Reference in a new issue