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:
Anna Shaleva 2023-11-02 14:01:36 +03:00
parent b2205940fa
commit 14d98811a5
2 changed files with 32 additions and 4 deletions

View file

@ -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 {

View file

@ -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{