Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BaseApp Security Improvements #3801

Merged
merged 12 commits into from
Mar 8, 2019
3 changes: 3 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ CLI flag.
* [\#3300] Update the spec-spec, spec file reorg, and TOC updates.
* [\#3694] Push tagged docker images on docker hub when tag is created.
* [\#3716] Update file permissions the client keys directory and contents to `0700`.
* [\#3791] Ensure proper handling of invalid block maximum gas in the `BaseApp`.
* [\#3790] Ensure a valid height is provided by `abci.RequestBeginBlock` in
`BeginBlock`.
* [\#3681](https://github.com/cosmos/cosmos-sdk/issues/3681) Migrate ledger-cosmos-go from ZondaX to Cosmos organization

### Tendermint
Expand Down
24 changes: 22 additions & 2 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,13 @@ func (app *BaseApp) storeConsensusParams(consensusParams *abci.ConsensusParams)
}

// getMaximumBlockGas gets the maximum gas from the consensus params.
func (app *BaseApp) getMaximumBlockGas() (maxGas uint64) {
if app.consensusParams == nil || app.consensusParams.BlockSize == nil {
func (app *BaseApp) getMaximumBlockGas() uint64 {
if app.consensusParams == nil ||
app.consensusParams.BlockSize == nil ||
app.consensusParams.BlockSize.MaxGas <= 0 {
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
return 0
}

return uint64(app.consensusParams.BlockSize.MaxGas)
}

Expand Down Expand Up @@ -510,6 +513,19 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res
}
}

func (app *BaseApp) validateHeight(req abci.RequestBeginBlock) error {
if req.Header.Height < 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Tendermint starts at height 1 now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but can the same be said for other consensus engines?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily, but I think we should panic otherwise, because elsewhere in the SDK we assume that the first block height is 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point -- updated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can a comment be made somewhere or we amend ABCI to stipulate that the first block is at height 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ValarDragon yes, I'll follow up on this.

return fmt.Errorf("invalid height: %d", req.Header.Height)
}

prevHeight := app.LastBlockHeight()
if prevHeight != 0 && req.Header.Height != prevHeight+1 {
cwgoes marked this conversation as resolved.
Show resolved Hide resolved
return fmt.Errorf("invalid height: %d; expected: %d", req.Header.Height, prevHeight+1)
}

return nil
}

// BeginBlock implements the ABCI application interface.
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
if app.cms.TracingEnabled() {
Expand All @@ -518,6 +534,10 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
))
}

if err := app.validateHeight(req); err != nil {
panic(err)
}

// Initialize the DeliverTx state. If this is the first block, it should
// already be initialized in InitChain. Otherwise app.deliverState will be
// nil, since it is reset on Commit.
Expand Down
32 changes: 28 additions & 4 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ func TestInitChainer(t *testing.T) {
// stores are mounted and private members are set - sealing baseapp
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
require.Nil(t, err)
require.Equal(t, int64(0), app.LastBlockHeight())

app.InitChain(abci.RequestInitChain{AppStateBytes: []byte("{}"), ChainId: "test-chain-id"}) // must have valid JSON genesis file, even if empty

Expand All @@ -308,6 +309,7 @@ func TestInitChainer(t *testing.T) {

app.Commit()
res = app.Query(query)
require.Equal(t, int64(1), app.LastBlockHeight())
require.Equal(t, value, res.Value)

// reload app
Expand All @@ -316,14 +318,17 @@ func TestInitChainer(t *testing.T) {
app.MountStores(capKey, capKey2)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
require.Nil(t, err)
require.Equal(t, int64(1), app.LastBlockHeight())

// ensure we can still query after reloading
res = app.Query(query)
require.Equal(t, value, res.Value)

// commit and ensure we can still query
app.BeginBlock(abci.RequestBeginBlock{})
header := abci.Header{Height: app.LastBlockHeight() + 1}
app.BeginBlock(abci.RequestBeginBlock{Header: header})
app.Commit()

res = app.Query(query)
require.Equal(t, value, res.Value)
}
Expand Down Expand Up @@ -514,7 +519,6 @@ func TestCheckTx(t *testing.T) {
app := setupBaseApp(t, anteOpt, routerOpt)

nTxs := int64(5)

app.InitChain(abci.RequestInitChain{})

// Create same codec used in txDecoder
Expand Down Expand Up @@ -567,16 +571,22 @@ func TestDeliverTx(t *testing.T) {

nBlocks := 3
txPerHeight := 5

for blockN := 0; blockN < nBlocks; blockN++ {
app.BeginBlock(abci.RequestBeginBlock{})
header := abci.Header{Height: int64(blockN) + 1}
app.BeginBlock(abci.RequestBeginBlock{Header: header})

for i := 0; i < txPerHeight; i++ {
counter := int64(blockN*txPerHeight + i)
tx := newTxCounter(counter, counter)

txBytes, err := codec.MarshalBinaryLengthPrefixed(tx)
require.NoError(t, err)

res := app.DeliverTx(txBytes)
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
}

app.EndBlock(abci.RequestEndBlock{})
app.Commit()
}
Expand Down Expand Up @@ -692,7 +702,8 @@ func TestSimulateTx(t *testing.T) {
nBlocks := 3
for blockN := 0; blockN < nBlocks; blockN++ {
count := int64(blockN + 1)
app.BeginBlock(abci.RequestBeginBlock{})
header := abci.Header{Height: count}
app.BeginBlock(abci.RequestBeginBlock{Header: header})

tx := newTxCounter(count, count)
txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx)
Expand Down Expand Up @@ -1216,3 +1227,16 @@ func TestP2PQuery(t *testing.T) {
res = app.Query(idQuery)
require.Equal(t, uint32(4), res.Code)
}

func TestGetMaximumBlockGas(t *testing.T) {
app := setupBaseApp(t)

app.setConsensusParams(&abci.ConsensusParams{BlockSize: &abci.BlockSizeParams{MaxGas: 0}})
require.Equal(t, uint64(0), app.getMaximumBlockGas())

app.setConsensusParams(&abci.ConsensusParams{BlockSize: &abci.BlockSizeParams{MaxGas: 5000000}})
require.Equal(t, uint64(5000000), app.getMaximumBlockGas())

app.setConsensusParams(&abci.ConsensusParams{BlockSize: &abci.BlockSizeParams{MaxGas: -5000000}})
require.Equal(t, uint64(0), app.getMaximumBlockGas())
rigelrozanski marked this conversation as resolved.
Show resolved Hide resolved
}
19 changes: 12 additions & 7 deletions x/bank/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ func TestSendNotEnoughBalance(t *testing.T) {
origSeq := res1.GetSequence()

sendMsg := NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 100)})
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, []sdk.Msg{sendMsg}, []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1)
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, []sdk.Msg{sendMsg}, []uint64{origAccNum}, []uint64{origSeq}, false, false, priv1)

mock.CheckBalance(t, mapp, addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 67)})

Expand Down Expand Up @@ -173,7 +174,8 @@ func TestMsgMultiSendWithAccounts(t *testing.T) {
}

for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)

for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
Expand Down Expand Up @@ -212,7 +214,8 @@ func TestMsgMultiSendMultipleOut(t *testing.T) {
}

for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
rigelrozanski marked this conversation as resolved.
Show resolved Hide resolved

for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
Expand Down Expand Up @@ -241,7 +244,7 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) {
testCases := []appTestCase{
{
msgs: []sdk.Msg{multiSendMsg3},
accNums: []uint64{0, 0},
accNums: []uint64{0, 2},
accSeqs: []uint64{0, 0},
expSimPass: true,
expPass: true,
Expand All @@ -256,7 +259,8 @@ func TestMsgMultiSendMultipleInOut(t *testing.T) {
}

for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)

for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
Expand Down Expand Up @@ -289,7 +293,7 @@ func TestMsgMultiSendDependent(t *testing.T) {
},
{
msgs: []sdk.Msg{multiSendMsg4},
accNums: []uint64{0},
accNums: []uint64{1},
accSeqs: []uint64{0},
expSimPass: true,
expPass: true,
Expand All @@ -301,7 +305,8 @@ func TestMsgMultiSendDependent(t *testing.T) {
}

for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)
header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, header, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)

for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
Expand Down
20 changes: 16 additions & 4 deletions x/gov/endblocker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import (

func TestTickExpiredDepositPeriod(t *testing.T) {
mapp, keeper, _, addrs, _, _ := getMockApp(t, 10, GenesisState{}, nil)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})
keeper.ck.SetSendEnabled(ctx, true)
govHandler := NewHandler(keeper)
Expand Down Expand Up @@ -56,7 +59,10 @@ func TestTickExpiredDepositPeriod(t *testing.T) {

func TestTickMultipleExpiredDepositPeriod(t *testing.T) {
mapp, keeper, _, addrs, _, _ := getMockApp(t, 10, GenesisState{}, nil)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})
keeper.ck.SetSendEnabled(ctx, true)
govHandler := NewHandler(keeper)
Expand Down Expand Up @@ -113,7 +119,10 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) {

func TestTickPassedDepositPeriod(t *testing.T) {
mapp, keeper, _, addrs, _, _ := getMockApp(t, 10, GenesisState{}, nil)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})
keeper.ck.SetSendEnabled(ctx, true)
govHandler := NewHandler(keeper)
Expand Down Expand Up @@ -156,7 +165,10 @@ func TestTickPassedDepositPeriod(t *testing.T) {
func TestTickPassedVotingPeriod(t *testing.T) {
mapp, keeper, _, addrs, _, _ := getMockApp(t, 10, GenesisState{}, nil)
SortAddresses(addrs)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})
keeper.ck.SetSendEnabled(ctx, true)
govHandler := NewHandler(keeper)
Expand Down
15 changes: 11 additions & 4 deletions x/gov/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ func TestEqualProposals(t *testing.T) {
// Generate mock app and keepers
mapp, keeper, _, addrs, _, _ := getMockApp(t, 2, GenesisState{}, nil)
SortAddresses(addrs)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})

// Create two proposals
Expand Down Expand Up @@ -56,11 +59,13 @@ func TestEqualProposals(t *testing.T) {
}

func TestImportExportQueues(t *testing.T) {

// Generate mock app and keepers
mapp, keeper, _, addrs, _, _ := getMockApp(t, 2, GenesisState{}, nil)
SortAddresses(addrs)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})

// Create two proposals, put the second into the voting period
Expand All @@ -82,7 +87,9 @@ func TestImportExportQueues(t *testing.T) {
genState := ExportGenesis(ctx, keeper)
mapp2, keeper2, _, _, _, _ := getMockApp(t, 2, genState, genAccs)

mapp2.BeginBlock(abci.RequestBeginBlock{})
header = abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp2.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx2 := mapp2.BaseApp.NewContext(false, abci.Header{})

// Jump the time forward past the DepositPeriod and VotingPeriod
Expand Down
33 changes: 24 additions & 9 deletions x/gov/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import (

func TestGetSetProposal(t *testing.T) {
mapp, keeper, _, _, _, _ := getMockApp(t, 0, GenesisState{}, nil)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})

proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
Expand All @@ -26,7 +29,10 @@ func TestGetSetProposal(t *testing.T) {

func TestIncrementProposalNumber(t *testing.T) {
mapp, keeper, _, _, _, _ := getMockApp(t, 0, GenesisState{}, nil)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})

keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
Expand All @@ -41,9 +47,11 @@ func TestIncrementProposalNumber(t *testing.T) {

func TestActivateVotingPeriod(t *testing.T) {
mapp, keeper, _, _, _, _ := getMockApp(t, 0, GenesisState{}, nil)
mapp.BeginBlock(abci.RequestBeginBlock{})
ctx := mapp.BaseApp.NewContext(false, abci.Header{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)

require.True(t, proposal.GetVotingStartTime().Equal(time.Time{}))
Expand All @@ -63,9 +71,11 @@ func TestActivateVotingPeriod(t *testing.T) {
func TestDeposits(t *testing.T) {
mapp, keeper, _, addrs, _, _ := getMockApp(t, 2, GenesisState{}, nil)
SortAddresses(addrs)
mapp.BeginBlock(abci.RequestBeginBlock{})
ctx := mapp.BaseApp.NewContext(false, abci.Header{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()

Expand Down Expand Up @@ -149,9 +159,11 @@ func TestDeposits(t *testing.T) {
func TestVotes(t *testing.T) {
mapp, keeper, _, addrs, _, _ := getMockApp(t, 2, GenesisState{}, nil)
SortAddresses(addrs)
mapp.BeginBlock(abci.RequestBeginBlock{})
ctx := mapp.BaseApp.NewContext(false, abci.Header{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
proposalID := proposal.GetProposalID()

Expand Down Expand Up @@ -204,7 +216,10 @@ func TestVotes(t *testing.T) {

func TestProposalQueues(t *testing.T) {
mapp, keeper, _, _, _, _ := getMockApp(t, 0, GenesisState{}, nil)
mapp.BeginBlock(abci.RequestBeginBlock{})

header := abci.Header{Height: mapp.LastBlockHeight() + 1}
mapp.BeginBlock(abci.RequestBeginBlock{Header: header})

ctx := mapp.BaseApp.NewContext(false, abci.Header{})
mapp.InitChainer(ctx, abci.RequestInitChain{})

Expand Down
Loading