For the complete documentation index, see llms.txt. This page is also available as Markdown.

76045 bc medium malformed batch will be retried as invalid payload attributes forever

Submitted on May 2nd 2026 at 12:01:14 UTC by @pks271 for Audit Comp | Base Azul

  • Report ID: #76045

  • Report Type: Blockchain/DLT

  • Report severity: Medium

  • Target: https://github.com/base/base/tree/v0.8.0-rc.28

  • Impacts:

    • Network not being able to confirm new transactions (total network shutdown)

Description

Bug Description

A malformed ordinary transaction byte string can survive batch validation because SingleBatch::check_batch() only rejects empty txs, deposit txs, and pre-Isthmus 7702 txs; it never fully decodes ordinary envelopes. AttributesQueue then appends the malformed bytes unchanged into payload attributes. When the engine tries to start a safe build, the attr-bearing engine_forkchoiceUpdated call can legitimately return PayloadStatus::Invalid for an invalid transaction or invalid state transition, which the bundled derivation spec explicitly treats as a normal payload-processing failure that must cause the attrs to be dropped. That means this path is not limited to a malicious operator: it can also happen if the authenticated batch source is simply buggy, version-skewed, or emits malformed data during a normal upgrade or serialization mistake. Instead, the current implementation classifies that Invalid result as Temporary, so EngineTask::execute() retries the same task in-place forever with only yield_now(). Because EngineProcessor drains the task queue before it accepts the next request, the poisoned safe-build task starves later engine requests behind it.

Code path:

crates/consensus/protocol/src/batch/single.rs:168#SingleBatch::check_batch -> malformed tx bytes are accepted without envelope decode -> crates/consensus/derive/src/stages/attributes_queue.rs:106#AttributesQueue::create_next_attributes -> malformed bytes are forwarded unchanged into payload attributes -> crates/consensus/engine/src/task_queue/tasks/build/task.rs:87#BuildTask::start_build -> attr-bearing engine_forkchoiceUpdated returns PayloadStatusEnum::Invalid -> crates/consensus/engine/src/task_queue/tasks/build/error.rs:58#BuildTaskError::severity -> EngineBuildError::InvalidPayload(_) is classified as Temporary -> crates/consensus/engine/src/task_queue/tasks/task.rs:248#EngineTask::execute -> Temporary => yield_now() + continue on the same task -> crates/consensus/service/src/actors/engine/engine_request_processor.rs:512#EngineProcessor::start -> drains the queue before receiving the next request, so later engine work is starved

This also drifts from the bundled Base/OP derivation spec, which still requires invalid batch-derived payload attributes to be dropped rather than retried in place:

  • docs/specs/pages/protocol/consensus/derivation.md:821

If a payload attributes created from a batch cannot be inserted into the chain because of a validation error (i.e. there
was an invalid transaction or state transition in the block) the batch should be dropped & the safe head should not be
advanced.
  • docs/specs/pages/protocol/consensus/derivation.md:843

Impact

A malicious, compromised, or simply buggy authenticated batch source can publish one malformed batch that survives derivation validation and pins the consensus node on a single poisoned safe-build task. This does not require an actively malicious operator; the same behavior can arise from a serializer bug, version skew, or upgrade-edge batch that the EL rejects as invalid. The node keeps re-sending the same attr-bearing forkchoice update without drop or backoff, amplifying CPU work and preventing later engine requests from progressing.

Recommendation

Fully decode ordinary batch transaction envelopes during batch validation and treat attr-bearing InvalidPayload responses as payload-processing failures rather than temporary retry conditions. Drop the bad attrs, keep forkchoice unchanged, and continue with the next batch or deposits-only fallback; add a regression test that invalid batch attrs are not retried in place.

Proof of Concept

File: base-base/crates/consensus/service/tests/actors/batch_invalid_attrs.rs

Run the test: cargo test --config 'target.x86_64-unknown-linux-gnu.rustflags=["-C","link-arg=-fuse-ld=lld"]' -p base-consensus-node --test integration authenticated_malformed_batch_transactions_reach_el_as_invalid_attrs_and_starve_engine -- --nocapture

Expected result:

Was this helpful?