Skip to content

chore: bring cosmos/evm v0.6.0 onto audit-fixes (fixes F-2026-18785) - #47

Merged
0xNilesh merged 13 commits into
audit-fixesfrom
chore/evm-0.6.0-on-audit-fixes
Aug 26, 2026
Merged

chore: bring cosmos/evm v0.6.0 onto audit-fixes (fixes F-2026-18785)#47
0xNilesh merged 13 commits into
audit-fixesfrom
chore/evm-0.6.0-on-audit-fixes

Conversation

@0xNilesh

Copy link
Copy Markdown
Member

Brings cosmos/evm v0.6.0 onto the audit-fixes line by merging develop, which already carries it.

Why

audit-fixes (and therefore main/mainnet) sits on the v0.5.1 line, while develop and testnet/donut have run v0.6.0 since Arya's upgrade in 2ed3c930 (2026-06-27). That gap is what F-2026-18785 reports.

F-2026-18785 — RevertMultiStore EventManager / writeCache desync. On our side RevertMultiStore replaced the writeCache closure with one holding a frozen event slice, and nothing ever restored it:

func (s *StateDB) RevertMultiStore(snapshot int, events sdk.Events) {
	s.snapshotter.RevertToSnapshot(snapshot)
	s.writeCache = func() {
		s.ctx.EventManager().EmitEvents(events)   // static pre-call slice
		s.cacheCtx.MultiStore().(storetypes.CacheMultiStore).Write()
	}
}

Two consequences: precompiles succeeding after a caught revert had their store writes committed but their events swallowed; and because cacheCtx's events were never truncated, a later reverting frame could freeze in a slice still containing earlier reverted emissions. Trigger is ordinary Solidity try/catch around a precompile call.

v0.6.0 fixes it structurally — the function no longer takes events and only rolls the store, with restoration moved into the journal entry where it belongs:

func (s *StateDB) RevertMultiStore(snapshot int) {
	s.snapshotter.RevertToSnapshot(snapshot)
}

func (pc precompileCallChange) Revert(s *StateDB) {
	s.RevertMultiStore(pc.snapshot)
	s.cacheCtx.EventManager().OverrideEvents(pc.prevEvents)
}

writeCache stays the live flush, and revert rewinds events rather than freezing them.

The merge

git merge-tree reported no conflicts, and the real merge was clean. The lineages share a very recent base — 96231e7a (our pin) is a direct ancestor of develop, with the v0.6.0 upgrade sitting immediately on top. Divergence was only 3 commits vs 9.

Verified after merging:

  • RevertMultiStore is the v0.6.0 store-only form; event restoration present in precompileCallChange.Revert
  • all three previously-merged audit fixes survive — F-2026-18201 (VerifySender), F-2026-18197, F-2026-18187 (getTallyResult)
  • go build ./x/... ./precompiles/... ./ante/... clean

Second commit — retiring a reflect/unsafe shim

The merge brought a Push-fork adaptation that writes the SDK's unexported events field by reflection:

f := reflect.ValueOf(em).Elem().FieldByName("events")
reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().Set(reflect.ValueOf(events))

Its comment explains why: "push-chain pins cosmos-sdk to the v0.50.x line … which has no in-place events setter." That was correct when written — v0.50.10 has zero occurrences of OverrideEvents — but the premise is now stale. This fork pins cosmos-sdk v0.53.6, where:

func (em *EventManager) OverrideEvents(events Events) { em.events = events }

is exactly the in-place mutation the comment says is required, and is on EventManagerI too. So the shim is replaced with the direct call and the reflect/unsafe imports are dropped.

Worth doing beyond tidiness: as written it was a latent panic on a consensus path — FieldByName returns a zero Value if the concrete type behind EventManagerI is ever not *sdk.EventManager, and .UnsafeAddr() on a zero Value panics inside Revert. It also breaks silently if the SDK renames the field.

Scope — what this does not fix

Checked every evm-attributed finding in this audit against v0.6.0; it closes exactly one:

finding v0.6.0
F-2026-18785 RevertMultiStore desync ✅ fixed by this PR
F-2026-18135 nested precompile double-applies balance events ❌ unfixed here and on upstream main
F-2026-18144 unchecked cumulative GasWanted ante/types/block.go byte-identical to ours
F-2026-18786 unknown selector runs CommitWithCacheCtx ❌ unfixed in every tag and main
F-2026-18788 unauthenticated TraceCall timeout TraceCall identical to ours
F-2026-18784 uppercase bech32 fee payer ⚪ not an evm issue (cosmossdk.io/x/tx)

So this is a drift-reduction and one-finding fix, not a sweep of the evm cluster.

Sequencing

The node side needs a matching PR — v0.6.0 removes cosmos/evm's custom x/ibc/transfer wrapper and changes the CallEVM signature, so push-chain-node does not compile against this until its adaptation lands. Merge this first, then the node PR pins to the resulting commit.

Open evm PRs #43#46 will need rebasing onto this once merged.

Follow-up

A regression test for F-2026-18785 (Hacken's rec 4 — revert precompile A via try/catch, then a successful precompile B must emit B's events and not A's) is not in this PR. Tracked separately; the fix here rests on the upstream diff plus the verifications above.

vladcosmoslabs and others added 11 commits March 2, 2026 09:49
* Changelog

* fix line length
Seven jobs never run — they queue until GitHub cancels them at 24h. Bumps
test/lint timeouts 15m->30m for the smaller GitHub runners.
ci: move jobs off dead Depot runners to GitHub-hosted
fix(rpc): report base fee as the gas price of derived txs
The shim existed because cosmos-sdk v0.50.x had no in-place events setter;
the fork now pins v0.53.6, where OverrideEvents is exactly em.events = events.
# Conflicts:
#	tests/integration/x/erc20/test_msg_server.go
#	x/erc20/keeper/ibc_callbacks.go
#	x/erc20/keeper/msg_server.go
#	x/ibc/callbacks/keeper/keeper.go
#	x/vm/keeper/call_evm.go
@0xNilesh
0xNilesh merged commit b678fbc into audit-fixes Aug 26, 2026
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants