owned this note
owned this note
Published
Linked with GitHub
Short description *
1 sentence description of the bug
Malformed blob tx causes Nethermind validators to stop producing blocks
Attack scenario *
More detailed description of the attack/bug scenario and unexpected/buggy behaviour
Nethermind's missing length-equality check between a blob transaction’s `BlobVersionedHashes` and its sidecar contents lets malformed blob transactions be accepted, producing malformed payloads/blob-serving, causing CL proposers paired with Nethermind to miss proposals.
1. An attacker submits a high tip EIP-4844 blob transaction to a Nethermind node where the payload `blobVersionedHashes` has N entries while the sidecar has M < N. Each entry is individually well-formed (correct version/length), so validation passes, but the length mismatch is never enforced.
2. The mempool admits and propagates the tx to other Nethermind peers, that accept it too.
3. During block building, `GetBlobCount()` uses N to size `BlobsBundleV1` while only M blobs/proofs exist, resulting in null trailing entries. When the CL requests blobs via `engine_getBlobsV1`, the EL returns a mixed list like `[blob, null]` (or hits an out-of-bounds path), so the CL cannot assemble a valid block and skips the proposal.
4. As long as the malformed tx remains in mempools and is attempted for inclusion, Nethermind-based proposers repeatedly miss slots.
Note: Fusaka/Osaka (Engine V5) shows the same upstream acceptance bug with V5-specific handling differences - that will be submitted to the Fusaka contest as well due to the separate new-in-Fusaka code paths being affected.
Impact *
Describe the effect this may have in a production setting
Until Nethermind validators are patched:
1. 38% of the network capacity is removed.
2. 38% of validators lose block rewards and fees.
3. 38% of validators lose stake due coordinated inactivity leak penalties.
Components *
Point to the files, functions, and/or specific line numbers where the bug occurs
- `nethermind/src/Nethermind/Nethermind.Crypto/IBlobProofsManager.cs` - `IBlobProofsVerifier.ValidateHashes(ShardBlobNetworkWrapper blobs, byte[][] blobVersionedHashes)` — validates shape/per-index but does not enforce `blobVersionedHashes.Length == blobs.Blobs.Length` (or equality with commitments/proofs). - `nethermind/src/Nethermind/Nethermind.Core/TransactionExtensions.cs` - `TransactionExtensions.GetBlobCount(this Transaction tx)` — derives count from `tx.BlobVersionedHashes?.Length`, propagating the mutated N. - `nethermind/src/Nethermind/Nethermind.Merge.Plugin/Data/BlobsBundleV1.cs` - Allocates arrays by N and copies M, leaving null gaps when `N > M`. - `nethermind/src/Nethermind/Nethermind.TxPool/Collections/BlobTxDistinctSortedPool.cs` - `TryGetBlobAndProofV0` uses index from `BlobVersionedHashes` to fetch `wrapper.Blobs[index]` without guarding `index >= wrapper.Blobs.Length`. - `nethermind/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetBlobsHandler.cs` and `EngineRpcModule.Prague.cs`
Reproduction *
If used any sort of tools/simulations to find the bug, describe in detail how to reproduce the buggy behaviour.
https://gist.github.com/guhu95/26a201929ba6a0cfd673c3b9be832504
Fix
Description of suggested fix, if available
- Enforce length equality at or before mempool admission:
- In `ValidateHashes` and/or a dedicated `ValidateLengths`, require `BlobVersionedHashes.Length == wrapper.Blobs.Length == wrapper.Commitments.Length == wrapper.Proofs.Length / Ckzg.CellsPerExtBlob`.
- Alternatively enforce in mempool validators (e.g., `BlobFieldsTxValidator`, `MempoolBlobTxValidator`) prior to acceptance.
- Harden serving path:
- Add bounds checks in `BlobTxDistinctSortedPool.TryGetBlobAndProofV0` to return `false` when out-of-range instead of indexing.
- Bundle sanity:
- Validate bundle sizes against sidecar lengths.