The EndBlocker
in x/gov
iterates over active proposals with ended voting periods. If a proposal passes after the results are tallied, the function tries to dispatch the proposal messages with cache context.
The issue is that no gas limit is enforced when dispatching the proposal messages. In ABCI methods like EndBlocker
, the gas limit is not tracked, essentially allowing infinite gas to be consumed. If there is a MsgExecuteContract
message that calls a CosmWasm contract that loops infinitely (either maliciously or due to a coding mistake), the chain will halt due to a block production timeout.
Pseudocode:
ExecuteMsg::Test{} => {
if info.sender.to_string() == GOV_MODULE_ADDRESS {
loop {}
} else {
Ok(Response::new())
}
}
To fix this, the cache context should be set up with gas meter limits. If the malicious contract infinite loop is triggered, a panic with an out-of-gas error will occur, which will be gracefully recovered in the safeExecuteHandler
function.
For example, Osmosis creates a child context with types.BeforeSendHookGasLimit
as the gas limit when dispatching sudo calls to contracts. If an out-of-gas occurs, it will be caught by the recover
function. This prevents malicious contracts from halting the chain due to infinite loops.