Summary

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())
		}
}

Workarounds

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.

Fix PR: https://github.com/cosmos/cosmos-sdk/pull/20348