Skip to content

fix: bound ABI decoding in Zone transaction paths - #1379

Open
0xalpharush wants to merge 1 commit into
tanishk/tempo-7444-precompilesfrom
t11-zone-abi-hardening
Open

fix: bound ABI decoding in Zone transaction paths#1379
0xalpharush wants to merge 1 commit into
tanishk/tempo-7444-precompilesfrom
t11-zone-abi-hardening

Conversation

@0xalpharush

@0xalpharush 0xalpharush commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • skip duplicate Zone decoding for AccountKeychain selectors that have no Zone-specific privacy policy
  • strictly decode nested Inbox deposit payloads with the shared 16 MiB decoder limit
  • classify finalizeWithdrawalBatch system transactions by destination and selector, matching advanceTempo
  • strictly decode replicated advanceTempo calls with the shared 16 MiB limit

Safety

This PR is stacked on #1372.

For unrestricted AccountKeychain selectors, the Zone wrapper forwards the original calldata and caller without decoding them first. The upstream Tempo precompile remains authoritative: it charges input gas, enforces selector scheduling, performs bounded ABI decoding, and applies AccountKeychain authorization and state checks. Zone privacy-sensitive getters still decode before applying their caller restrictions.

System-transaction classification now performs only constant-time destination and selector checks for both advanceTempo and finalizeWithdrawalBatch. Classification only proposes the next block phase. The phase commits after successful authoritative precompile execution, so calldata rejected during execution cannot advance it.

Nested Inbox decoding uses strict mode and the shared ABI_DECODER_MEMORY_LIMIT. Strict mode replaces the previous decode-and-reencode canonicality check. Replication applies the same bound before processing peer-supplied advanceTempo data.

Testing

  • cargo test -p zone-evm --lib: 36 passed
  • cargo test -p zone-precompiles --lib: 153 passed, 1 ignored
  • cargo test -p zone-precompiles inbox --lib: 23 passed
  • cargo test -p zone-precompiles account_keychain --lib: 4 passed
  • cargo fmt --check
  • git diff --check

A full zone-node check remains blocked by an unrelated existing zone-spf API mismatch around HashedStorage.wiped/wipe.

@0xalpharush
0xalpharush force-pushed the t11-zone-abi-hardening branch 3 times, most recently from 5ee3a6e to 304cff9 Compare September 3, 2026 16:29
@0xalpharush

Copy link
Copy Markdown
Contributor Author

cyclops audit

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

cc @0xalpharush

Cyclops audit event published. View workflow run

Config: config: default, iterations: default, hours: default

@tempoxyz-bot tempoxyz-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

👁️ Cyclops Review

Found 3 actionable items. Inline comments cover findings on changed lines; the non-diff finding is included below.

🛡️ [DEFENSE-IN-DEPTH] SPF proof replay canonically re-encodes advanceTempo instead of replaying committed bytes

Severity: Medium
File: crates/spf/src/execution/evm.rs:229

execute_advance_tempo rebuilds the system transaction with IZoneInbox::advanceTempoCall { ... }.abi_encode() from fields extracted by crates/sequencer/src/prover.rs:660, which non-strictly decodes and discards the original calldata. Pre-T11 block validation does not enforce canonical outer advanceTempo calldata, so a consensus-valid non-canonical system transaction can be replayed as different bytes in SPF, changing the transaction root and assembled block hash and making the submitted batch unprovable.

Recommended Fix: Carry the original system-transaction calldata from extract_zone_block into SPF and execute exactly those committed bytes for both system transactions. Separately, consider adding a pre-T11 is_advance_tempo_calldata canonicality gate and relying on strict dispatch from T11 onward.

Reviewer Callouts
  • crates/precompiles/src/inbox/mod.rs:153-166: The comment says partial deposit processing is supported and that requiring equality would forbid it, but the code immediately requires tempo_current_hash == current_hash. If partial processing is intended, this path needs a human design check.
  • crates/l1/src/event.rs:104-124: validate_advance_tempo_inputs deliberately excludes the sequencer-controlled rejected flag from deposit identity; confirm that no downstream indexer/prover path treats that flag as L1-authenticated data.
  • Fork-gating asymmetry: executor.rs is fork-gated for finalizeWithdrawalBatch, while outbox/mod.rs, inbox/mod.rs, and replication.rs are not. The un-gated strict-decoder changes were mostly shown equivalent, but permanent equivalence tests would help prevent future historical-consensus drift.

signed.tx().input.as_ref(),
AbiDecoderConfig::new()
.memory_limit(ABI_DECODER_MEMORY_LIMIT)
.strict(true),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 [POTENTIAL-VULNERABILITY] Fork-unaware strict advanceTempo decoding can make followers reject valid pre-T11 blocks

This unconditional strict decoder runs before engine.new_payload, but pre-T11 ZoneInbox execution uses fork-aware non-strict decoding and the executor classifies advanceTempo by selector prefix. A leader can add otherwise-ignored ABI trailing data that execution accepts while followers reject during replication import, stalling backfill.

Recommended Fix:
Use the same fork-aware decoder config as ZoneInbox dispatch for the block's active spec, or keep only the memory limit pre-T11. Add a regression test that replication and execution accept the same advanceTempo calldata set across forks.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

leader is normal ABI encoding

Comment thread crates/precompiles/src/outbox/mod.rs Outdated
call.abi_encode() == calldata
IZoneOutbox::finalizeWithdrawalBatchCall::abi_decode_with_config(
calldata,
AbiDecoderConfig::new().strict(true),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛡️ [DEFENSE-IN-DEPTH] Outbox canonical-call predicate omits the shared ABI decoder memory limit

This predicate uses AbiDecoderConfig::new().strict(true) without memory_limit(ABI_DECODER_MEMORY_LIMIT), leaving Alloy's default 1 GiB decoder bound on a path still used by pre-T11 executor classification. Verification did not show a practical allocation DoS, but the classifier now differs from the bounded configuration used by precompile dispatch and the other ABI hardening in this PR.

Recommended Fix:
Add .memory_limit(ABI_DECODER_MEMORY_LIMIT) before .strict(true), or reuse the shared decoder configuration, so classification and dispatch remain consistently bounded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

@legion2002
legion2002 marked this pull request as ready for review September 3, 2026 19:06
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T19:10:32.018712Z 304cff9 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 304cff9ff2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/node/src/replication.rs
@0xalpharush
0xalpharush force-pushed the t11-zone-abi-hardening branch 3 times, most recently from a96d045 to 20511e6 Compare September 3, 2026 19:21
Comment thread crates/evm/src/executor.rs Outdated
if tx.calls().any(|(kind, input)| {
kind.to() == Some(&ZONE_OUTBOX_ADDRESS) && is_finalize_withdrawal_batch_calldata(input)
kind.to() == Some(&ZONE_OUTBOX_ADDRESS)
&& if spec.is_t11() {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

tbh i think it does not need to be gated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yeah we can remove. the encoding is kinda guaranteed to be correct rn

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed

legion2002
legion2002 previously approved these changes Sep 3, 2026
@0xalpharush
0xalpharush force-pushed the t11-zone-abi-hardening branch from 20511e6 to 6482149 Compare September 3, 2026 19:59
@tempo-voight-kampff
tempo-voight-kampff Bot dismissed legion2002’s stale review September 3, 2026 19:59

Voight-Kampff: dismissing this approval review because it targets an earlier commit. Voight-Kampff verdicts apply only to the pull request's current head (6482149). Re-submit the review on the current head and complete the Voight-Kampff prompt if you want this verdict to count.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants