Skip to content

Commit

Permalink
Parameter for max wasm code size
Browse files Browse the repository at this point in the history
  • Loading branch information
alpe committed Nov 6, 2020
1 parent e55cc9f commit d4824f3
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 140 deletions.
5 changes: 3 additions & 2 deletions x/wasm/internal/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestGenesisExportImport(t *testing.T) {
srcKeeper.importContractState(srcCtx, contractAddr, stateModels)
}
var wasmParams types.Params
f.Fuzz(&wasmParams)
f.NilChance(0).Fuzz(&wasmParams)
srcKeeper.setParams(srcCtx, wasmParams)

// export
Expand Down Expand Up @@ -369,7 +369,8 @@ func TestImportContractWithCodeHistoryReset(t *testing.T) {
"code_upload_access": {
"permission": "Everybody"
},
"instantiate_default_permission": "Everybody"
"instantiate_default_permission": "Everybody",
"max_wasm_code_size": 500000
},
"codes": [
{
Expand Down
12 changes: 6 additions & 6 deletions x/wasm/internal/keeper/ioutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import (
// and https://github.com/golang/go/blob/master/src/net/http/sniff.go#L186
var gzipIdent = []byte("\x1F\x8B\x08")

// limit max bytes read to prevent gzip bombs
const maxSize = 400 * 1024

// uncompress returns gzip uncompressed content or given src when not gzip.
func uncompress(src []byte) ([]byte, error) {
if len(src) < 3 {
func uncompress(src []byte, limit uint64) ([]byte, error) {
switch n := uint64(len(src)); {
case n < 3:
return src, nil
case n > limit:
return nil, types.ErrLimit
}
if !bytes.Equal(gzipIdent, src[0:3]) {
return src, nil
Expand All @@ -31,7 +31,7 @@ func uncompress(src []byte) ([]byte, error) {
}
zr.Multistream(false)
defer zr.Close()
return ioutil.ReadAll(LimitReader(zr, maxSize))
return ioutil.ReadAll(LimitReader(zr, int64(limit)))
}

// LimitReader returns a Reader that reads from r
Expand Down
10 changes: 5 additions & 5 deletions x/wasm/internal/keeper/ioutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ func TestUncompress(t *testing.T) {
wasmGzipped, err := ioutil.ReadFile("./testdata/hackatom.wasm.gzip")
require.NoError(t, err)

_, _ = wasmRaw, wasmGzipped
const maxSize = 400_000

specs := map[string]struct {
src []byte
expError error
Expand All @@ -44,8 +45,8 @@ func TestUncompress(t *testing.T) {
expResult: []byte{0x1, 0x2},
},
"handle big input slice": {
src: []byte(strings.Repeat("a", maxSize+1)),
expResult: []byte(strings.Repeat("a", maxSize+1)),
src: []byte(strings.Repeat("a", maxSize+1)),
expError: types.ErrLimit,
},
"handle gzip identifier only": {
src: gzipIdent,
Expand Down Expand Up @@ -74,15 +75,14 @@ func TestUncompress(t *testing.T) {
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
r, err := uncompress(spec.src)
r, err := uncompress(spec.src, maxSize)
require.True(t, errors.Is(spec.expError, err), "exp %v got %+v", spec.expError, err)
if spec.expError != nil {
return
}
assert.Equal(t, spec.expResult, r)
})
}

}

func asGzip(src []byte) []byte {
Expand Down
10 changes: 8 additions & 2 deletions x/wasm/internal/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ func (k Keeper) getInstantiateAccessConfig(ctx sdk.Context) types.AccessType {
return a
}

func (k Keeper) GetMaxWasmCodeSize(ctx sdk.Context) uint64 {
var a uint64
k.paramSpace.Get(ctx, types.ParamStoreKeyMaxWasmCodeSize, &a)
return a
}

// GetParams returns the total set of wasm parameters.
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
var params types.Params
Expand All @@ -130,7 +136,7 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
if !authZ.CanCreateCode(k.getUploadAccessConfig(ctx), creator) {
return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code")
}
wasmCode, err = uncompress(wasmCode)
wasmCode, err = uncompress(wasmCode, k.GetMaxWasmCodeSize(ctx))
if err != nil {
return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
}
Expand All @@ -155,7 +161,7 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
}

func (k Keeper) importCode(ctx sdk.Context, codeID uint64, codeInfo types.CodeInfo, wasmCode []byte) error {
wasmCode, err := uncompress(wasmCode)
wasmCode, err := uncompress(wasmCode, k.GetMaxWasmCodeSize(ctx))
if err != nil {
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
}
Expand Down
1 change: 1 addition & 0 deletions x/wasm/internal/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func TestCreateStoresInstantiatePermission(t *testing.T) {
keeper.setParams(ctx, types.Params{
CodeUploadAccess: types.AllowEverybody,
InstantiateDefaultPermission: spec.srcPermission,
MaxWasmCodeSize: types.DefaultMaxWasmCodeSize,
})
fundAccounts(t, ctx, accKeeper, bankKeeper, myAddr, deposit)

Expand Down
24 changes: 20 additions & 4 deletions x/wasm/internal/keeper/proposal_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import (
func TestStoreCodeProposal(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
wasmKeeper.setParams(ctx, types.Params{CodeUploadAccess: types.AllowNobody, InstantiateDefaultPermission: types.AccessTypeNobody})
wasmKeeper.setParams(ctx, types.Params{
CodeUploadAccess: types.AllowNobody,
InstantiateDefaultPermission: types.AccessTypeNobody,
MaxWasmCodeSize: types.DefaultMaxWasmCodeSize,
})
wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)

Expand Down Expand Up @@ -55,7 +59,11 @@ func TestStoreCodeProposal(t *testing.T) {
func TestInstantiateProposal(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
wasmKeeper.setParams(ctx, types.Params{CodeUploadAccess: types.AllowNobody, InstantiateDefaultPermission: types.AccessTypeNobody})
wasmKeeper.setParams(ctx, types.Params{
CodeUploadAccess: types.AllowNobody,
InstantiateDefaultPermission: types.AccessTypeNobody,
MaxWasmCodeSize: types.DefaultMaxWasmCodeSize,
})

wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)
Expand Down Expand Up @@ -107,7 +115,11 @@ func TestInstantiateProposal(t *testing.T) {
func TestMigrateProposal(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
wasmKeeper.setParams(ctx, types.Params{CodeUploadAccess: types.AllowNobody, InstantiateDefaultPermission: types.AccessTypeNobody})
wasmKeeper.setParams(ctx, types.Params{
CodeUploadAccess: types.AllowNobody,
InstantiateDefaultPermission: types.AccessTypeNobody,
MaxWasmCodeSize: types.DefaultMaxWasmCodeSize,
})

wasmCode, err := ioutil.ReadFile("./testdata/hackatom.wasm")
require.NoError(t, err)
Expand Down Expand Up @@ -236,7 +248,11 @@ func TestAdminProposals(t *testing.T) {
t.Run(msg, func(t *testing.T) {
ctx, keepers := CreateTestInput(t, false, "staking", nil, nil)
govKeeper, wasmKeeper := keepers.GovKeeper, keepers.WasmKeeper
wasmKeeper.setParams(ctx, types.Params{CodeUploadAccess: types.AllowNobody, InstantiateDefaultPermission: types.AccessTypeNobody})
wasmKeeper.setParams(ctx, types.Params{
CodeUploadAccess: types.AllowNobody,
InstantiateDefaultPermission: types.AccessTypeNobody,
MaxWasmCodeSize: types.DefaultMaxWasmCodeSize,
})

codeInfoFixture := types.CodeInfoFixture(types.WithSHA256CodeHash(wasmCode))
require.NoError(t, wasmKeeper.importCode(ctx, 1, codeInfoFixture, wasmCode))
Expand Down
19 changes: 19 additions & 0 deletions x/wasm/internal/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ import (
const (
// DefaultParamspace for params keeper
DefaultParamspace = ModuleName
// DefaultMaxWasmCodeSize limit max bytes read to prevent gzip bombs
DefaultMaxWasmCodeSize = 600 * 1024
)

var ParamStoreKeyUploadAccess = []byte("uploadAccess")
var ParamStoreKeyInstantiateAccess = []byte("instantiateAccess")
var ParamStoreKeyMaxWasmCodeSize = []byte("maxWasmCodeSize")

var AllAccessTypes = []AccessType{
AccessTypeNobody,
Expand Down Expand Up @@ -95,6 +98,7 @@ func DefaultParams() Params {
return Params{
CodeUploadAccess: AllowEverybody,
InstantiateDefaultPermission: AccessTypeEverybody,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
}
}

Expand All @@ -108,6 +112,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
return paramtypes.ParamSetPairs{
paramtypes.NewParamSetPair(ParamStoreKeyUploadAccess, &p.CodeUploadAccess, validateAccessConfig),
paramtypes.NewParamSetPair(ParamStoreKeyInstantiateAccess, &p.InstantiateDefaultPermission, validateAccessType),
paramtypes.NewParamSetPair(ParamStoreKeyMaxWasmCodeSize, &p.MaxWasmCodeSize, validateMaxWasmCodeSize),
}
}

Expand All @@ -119,6 +124,9 @@ func (p Params) ValidateBasic() error {
if err := validateAccessConfig(p.CodeUploadAccess); err != nil {
return errors.Wrap(err, "upload access")
}
if err := validateMaxWasmCodeSize(p.MaxWasmCodeSize); err != nil {
return errors.Wrap(err, "max wasm code size")
}
return nil
}

Expand Down Expand Up @@ -146,6 +154,17 @@ func validateAccessType(i interface{}) error {
return sdkerrors.Wrapf(ErrInvalid, "unknown type: %q", a)
}

func validateMaxWasmCodeSize(i interface{}) error {
a, ok := i.(uint64)
if !ok {
return sdkerrors.Wrapf(ErrInvalid, "type: %T", i)
}
if a == 0 {
return sdkerrors.Wrap(ErrInvalid, "must be greater 0")
}
return nil
}

func (v AccessConfig) ValidateBasic() error {
switch v.Permission {
case AccessTypeUndefined:
Expand Down
30 changes: 27 additions & 3 deletions x/wasm/internal/types/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"encoding/json"
"testing"

"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -26,63 +28,81 @@ func TestValidateParams(t *testing.T) {
src: Params{
CodeUploadAccess: AllowNobody,
InstantiateDefaultPermission: AccessTypeNobody,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
},
"all good with everybody": {
src: Params{
CodeUploadAccess: AllowEverybody,
InstantiateDefaultPermission: AccessTypeEverybody,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
},
"all good with only address": {
src: Params{
CodeUploadAccess: AccessTypeOnlyAddress.With(anyAddress),
InstantiateDefaultPermission: AccessTypeOnlyAddress,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
},
"reject empty type in instantiate permission": {
src: Params{
CodeUploadAccess: AllowNobody,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
expErr: true,
},
"reject unknown type in instantiate": {
src: Params{
CodeUploadAccess: AllowNobody,
InstantiateDefaultPermission: 1111,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
expErr: true,
},
"reject invalid address in only address": {
src: Params{
CodeUploadAccess: AccessConfig{Permission: AccessTypeOnlyAddress, Address: invalidAddress},
InstantiateDefaultPermission: AccessTypeOnlyAddress,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
expErr: true,
},
"reject CodeUploadAccess Everybody with obsolete address": {
src: Params{
CodeUploadAccess: AccessConfig{Permission: AccessTypeEverybody, Address: anyAddress},
InstantiateDefaultPermission: AccessTypeOnlyAddress,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
expErr: true,
},
"reject CodeUploadAccess Nobody with obsolete address": {
src: Params{
CodeUploadAccess: AccessConfig{Permission: AccessTypeNobody, Address: anyAddress},
InstantiateDefaultPermission: AccessTypeOnlyAddress,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
expErr: true,
},
"reject empty CodeUploadAccess": {
src: Params{
InstantiateDefaultPermission: AccessTypeOnlyAddress,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
expErr: true,
}, "reject undefined permission in CodeUploadAccess": {
},
"reject undefined permission in CodeUploadAccess": {
src: Params{
CodeUploadAccess: AccessConfig{Permission: AccessTypeUndefined},
InstantiateDefaultPermission: AccessTypeOnlyAddress,
MaxWasmCodeSize: DefaultMaxWasmCodeSize,
},
expErr: true,
},
"reject empty max wasm code size": {
src: Params{
CodeUploadAccess: AllowNobody,
InstantiateDefaultPermission: AccessTypeNobody,
},
expErr: true,
},
Expand Down Expand Up @@ -147,14 +167,18 @@ func TestParamsUnmarshalJson(t *testing.T) {

"defaults": {
src: `{"code_upload_access": {"permission": "Everybody"},
"instantiate_default_permission": "Everybody"}`,
"instantiate_default_permission": "Everybody",
"max_wasm_code_size": 614400}`,
exp: DefaultParams(),
},
}
for msg, spec := range specs {
t.Run(msg, func(t *testing.T) {
var val Params
err := ModuleCdc.UnmarshalJSON([]byte(spec.src), &val)
interfaceRegistry := codectypes.NewInterfaceRegistry()
marshaler := codec.NewProtoCodec(interfaceRegistry)

err := marshaler.UnmarshalJSON([]byte(spec.src), &val)
require.NoError(t, err)
assert.Equal(t, spec.exp, val)
})
Expand Down
Loading

0 comments on commit d4824f3

Please sign in to comment.