execution/rlp, execution/types: reject malformed RLP instead of dropping elements - #21818
Merged
Merged
Conversation
…ing elements
Two divergences from go-ethereum let slice/struct RLP decoding silently accept
malformed input and drop elements instead of erroring:
1. execution/rlp: the generic slice/array/struct element decoders used
errors.Is(err, EOL) to detect end-of-list. EOL is a sentinel meaning "the
stream reached the end of this list". A custom Decoder (e.g.
Receipt.decodePayload) legitimately wraps EOL via fmt.Errorf("...: %w", EOL)
on malformed input; errors.Is matched those wrapped EOLs and treated them as
end-of-list, silently dropping the element and swallowing the error. Use the
exact sentinel match (err == EOL), as upstream go-ethereum does.
2. execution/types: Receipt.DecodeRLP returned bare rlp.EOL for an empty typed
receipt (0x80). As a slice element that sentinel is read as end-of-list,
silently dropping the receipt. Return errShortTypedReceipt, matching the
"typed receipt too short" handling elsewhere in the file. The existing
TestDecodeEmptyTypedReceipt is updated: it had enshrined the EOL behaviour.
Found by FuzzRLP (inputs c2c23030 and c180). Validated: execution/rlp and
execution/types suites green; FuzzRLP ran 240s / 17M execs with no crash.
domiwei
approved these changes
Jun 16, 2026
pull Bot
pushed a commit
to Dustin4444/erigon
that referenced
this pull request
Jun 16, 2026
…fuzz (erigontech#21820) ## What - **`.github/workflows/test-fuzz.yml`** — scheduled (nightly 03:00 UTC) active fuzzing of all 23 native Go fuzzers (`testing.F`), one matrix leg per target, on `ubuntu-latest` (free/unlimited on this public repo), with a per-target corpus cache. Manually dispatchable with a custom `fuzztime`. - **`docs/fuzzing.md`** — targets, local usage, the nightly job, and the OSS-Fuzz integration ([google/oss-fuzz#15642](google/oss-fuzz#15642)). - **`make fuzz PKG=<pkg> FUZZ=<FuzzName> [FUZZTIME=60s]`** — local single-target helper. ## Design Intentionally **not** part of the CI gate: seed-corpus regression already runs in `go test ./...`, and putting mutation fuzzing on the gate would be flaky (a newly found crash would red unrelated PRs). It complements OSS-Fuzz: runs today before that integration is live, and also covers the `txnprovider/txpool` (MDBX) targets deferred upstream (native `go test -fuzz` needs no sanitizer toolchain). ## Notifications (scheduled failures only) Uploads each crash reproducer as a `fuzz-crash-<target>` artifact, opens/updates a `nightly-fuzz` tracking issue, and posts to Discord if the `DISCORD_WEBHOOK` secret is set (no-ops otherwise). ## Notes - Requires a `DISCORD_WEBHOOK` repository secret for Discord alerts (optional; safe to merge without). - Best merged after erigontech#21818 — that fixes the one pre-existing `FuzzRLP` crash this workflow surfaces, so the first nightly starts green.
pull Bot
pushed a commit
to Dustin4444/erigon
that referenced
this pull request
Jun 19, 2026
…ontech#21852) ## What `checkErrListEnd` (used by the EIP-7928 Block Access List decoder and block-body decoding) detected end-of-list with `errors.Is(err, rlp.EOL)`. A nested BAL decoder returns a *wrapped* `EOL` on malformed input — e.g. an account list with no Address (`AccountChanges.DecodeRLP` → `"read Address: %w"` wrapping `rlp.EOL`). `errors.Is` matched the wrapped EOL and treated it as a clean end-of-list, so the **production** decoder `DecodeBlockAccessListBytes` **silently truncated** a malformed BAL to empty instead of erroring: `0xc1c0` (a list with one empty account) decoded to an empty BAL with no error on `main`. Fix: match the bare sentinel (`err == rlp.EOL`) so a wrapped EOL propagates as a real error. ## Severity (corrected from the original description) This is a **real correctness fix, not no-op hardening** — thanks @yperbasis for catching this. `DecodeBlockAccessListBytes` is the production decoder, called from `NewPayload` (`engine_server.go:374`), where the header commitment is `crypto.HashData` over the **raw** BAL bytes (`:388`) — so the decoder is the only gate. Once Glamsterdam activates, silently truncating malformed input would be an accept/reject divergence vs a strict client. **No mainnet impact today** (BALs are pre-mainnet, Glamsterdam devnets only). Same spirit as erigontech#21818 ("reject malformed RLP instead of dropping elements"). The rlp reflection slice decoder already used exact `== EOL` and is unchanged; block-body callers return a bare EOL at genuine end-of-list, so the change is scoped to the only vulnerable site. ## Test `TestBlockAccessListRejectsAddresslessAccount` now goes through `DecodeBlockAccessListBytes` (the production path) so it actually exercises `checkErrListEnd` — verified red→green (FAIL on `main` without the fix, PASS with it). Full `execution/types` suite green; `make lint` clean. Co-authored-by: Andrew Ashikhmin <34320705+yperbasis@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
FuzzRLPfound that slice/struct RLP decoding silently accepts malformed input and drops elements instead of erroring. Two independent divergences from go-ethereum:1.
execution/rlp: wrappedEOLswallowedThe generic slice/array/struct element decoders used
errors.Is(err, EOL)to detect end-of-list.EOLis a sentinel meaning "the stream reached the end of this list". A customDecoder(e.g.Receipt.decodePayload) legitimately wrapsEOLviafmt.Errorf("...: %w", EOL)on malformed input —errors.Ismatched those and treated them as end-of-list, dropping the element and swallowing the error. Switched to the exact sentinel matcherr == EOLat all three sites, as upstream go-ethereum does.2.
execution/types: empty typed receipt returned the sentinelReceipt.DecodeRLPreturned barerlp.EOLfor an empty typed receipt (0x80). As a slice element that sentinel is read as end-of-list, silently dropping the receipt. Now returnserrShortTypedReceipt(the existing "typed receipt too short" error). go-ethereum returns a real error here too; thisrlp.EOLtraces to a 2019 geth-sync.Impact
Malformed/non-canonical RLP that should be rejected was silently accepted with elements dropped. Bug 1 is type-generic (any
[]T/struct whereThas a customDecodeRLPthat wrapsEOL). Main risks: inter-client parsing differential vs go-ethereum (consensus-divergence surface) and silent truncation masking malformed/corrupt data. Not a proven state-forgery — receipts are normally re-checked against the headerreceiptsRoot— but it closes a malleability/silent-drop class and re-aligns with geth.Found by / validation
FuzzRLP(inputsc2c23030andc180).execution/rlp+execution/typessuites: green.FuzzRLP: 240s / 17M execs, no crash (was crashing in seconds).make lint: 0 issues.Related (not in this PR)
checkErrListEnd(block.go) uses the sameerrors.Is(EOL)anti-pattern and is used by the EIP-7928 BAL decoders (which wrapEOL) and block-body decode. There's a partials.ListEnd()backstop and no fuzzer covering BAL RLP — tracking as a separate follow-up (BAL/block-body RLP fuzzer +checkErrListEndfix).Backport
release/3.5(and likelyrelease/3.4) have both bugs — good cherry-pick candidate.