Skip to content

cl: GLOAS audit fixes — clone aliasing, PTC consistency, memory leaks, nil panics#21248

Merged
domiwei merged 4 commits into
mainfrom
kewei/gloas_audit_fixes_35
May 21, 2026
Merged

cl: GLOAS audit fixes — clone aliasing, PTC consistency, memory leaks, nil panics#21248
domiwei merged 4 commits into
mainfrom
kewei/gloas_audit_fixes_35

Conversation

@domiwei

@domiwei domiwei commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

  • Fix PTC consistency bug in notifyPtcMessages: old code used two different states (s vs blockState) for PTC computation, causing validators to be mapped to wrong PTC positions. Now uses blockState.GetPTCFromWindow consistently and iterates aggregation bits directly, eliminating the intermediate GetIndexedPayloadAttestation allocation and sort.
  • Fix ExecutionPayloadEnvelope.Clone() shallow copy: old Clone shared Payload and ExecutionRequests pointers (aliasing). Now deep-copies via SSZ roundtrip. Also fixes NewExecutionPayloadEnvelope to use GloasVersion instead of BellatrixVersion.
  • Add nil-panic guards in ProcessExecutionPayloadBid, verifyExecutionPayloadBidSignature, and ProcessExecutionPayloadEnvelope for malformed SSZ inputs.
  • Cap pendingELPayloads at 1024 to prevent unbounded growth; DrainPendingELPayloads now reuses backing array when small, releases to GC when large.
  • Clean up blockTimeliness sync.Map in onNewFinalized (was growing without bound).
  • Switch gossip topic scoring from strings.Contains to exact == match, removing fragile ordering dependency between execution_payload and execution_payload_bid.

No pre-GLOAS behavior changes — all fixes are in GLOAS-specific code paths or produce identical results for pre-GLOAS topics.

Test plan

  • TestSignedExecutionPayloadEnvelopeCloneNilMessage — nil Message Clone
  • TestGetPTCFromWindow / TestGetPTCFromWindowRejectsSlotOutsideWindow — PTC window accessor
  • TestPendingELPayloadsDropOldestAtCap / TestDrainPendingELPayloadsReleasesLargeBackingArray — cap limit and drain
  • TestTopicScoreParamsExactTopicMatching — exact topic match, rejects suffixed names
  • TestProcessExecutionPayloadEnvelopeRejectsNilEnvelope — nil envelope rejection
  • make lint passes
  • Existing CL tests pass (make test-short)

🤖 Generated with Claude Code

@domiwei domiwei requested a review from Giulio2002 as a code owner May 18, 2026 08:30
@yperbasis yperbasis added the Glamsterdam https://eips.ethereum.org/EIPS/eip-7773 label May 18, 2026
@yperbasis yperbasis requested a review from Copilot May 18, 2026 09:37
@yperbasis yperbasis added the Caplin Caplin: Consensus Layer, Beacon API label May 18, 2026

Copilot AI left a comment

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.

Pull request overview

This PR addresses several audit findings in Erigon's GLOAS (EIP-7732) consensus-layer code path: it tightens nil-handling around malformed SSZ inputs, deep-copies ExecutionPayloadEnvelope clones via SSZ roundtrip, makes notifyPtcMessages use a single consistent state for PTC derivation, bounds the pendingELPayloads queue and the blockTimeliness sync.Map, and replaces fragile strings.Contains topic-score matching with exact equality. New unit tests cover the cap/drain behavior, clone with nil message, exact topic matching, PTC window accessor, and nil envelope rejection.

Changes:

  • PTC vote consistency: notifyPtcMessages now uses blockState.GetPTCFromWindow and iterates aggregation bits directly; GetPTCFromWindow is exported and gains a nil-window guard.
  • Memory/lifetime fixes: cap pendingELPayloads at 1024 with drain that releases or reuses the backing array; clean up blockTimeliness on finalization; deep-clone ExecutionPayloadEnvelope via SSZ roundtrip; use GloasVersion in NewExecutionPayloadEnvelope; add nil guards in ProcessExecutionPayloadBid, verifyExecutionPayloadBidSignature, and ProcessExecutionPayloadEnvelope.
  • Gossip topic scoring switched from substring to exact match.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
cl/transition/impl/eth2/operations.go Adds nil guards for signed bid/envelope, payload, withdrawals, expected withdrawals, builder index range.
cl/transition/impl/eth2/operations_gloas_test.go New test verifying nil envelope rejection.
cl/phase1/network/gossip/score.go Replaces strings.Contains with == for topic score lookups; drops strings import.
cl/phase1/network/gossip/score_test.go New test for exact topic matching.
cl/phase1/forkchoice/utils.go Cleans up blockTimeliness entries for finalized/pruned blocks in onNewFinalized.
cl/phase1/forkchoice/pending_el_payload_test.go New tests for cap-and-drop and large-backing-array release.
cl/phase1/forkchoice/payload_vote.go Removes redundant state cache field; uses GetPTCFromWindow; iterates aggregation bits directly.
cl/phase1/forkchoice/payload_vote_test.go New tests for GetPTCFromWindow happy/error paths.
cl/phase1/forkchoice/forkchoice.go Adds maxPendingELPayloads/pendingELPayloadsShrinkCap; enforces cap; drain releases or reuses backing array.
cl/phase1/core/state/cache_accessors.go Exports GetPTCFromWindow; adds nil-window guard.
cl/cltypes/epbs_payload.go Deep-clone envelope via SSZ roundtrip; nil-safe SignedExecutionPayloadEnvelope.Clone; fixes default Payload version to Gloas.
cl/cltypes/epbs_payload_test.go New test verifying clone with nil Message.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread cl/phase1/forkchoice/payload_vote.go Outdated
Comment thread cl/cltypes/epbs_payload.go
Comment on lines +958 to +963
if len(f.pendingELPayloads) >= maxPendingELPayloads {
log.Warn("addPendingELPayload: dropping oldest pending EL payload", "queueLen", len(f.pendingELPayloads))
copy(f.pendingELPayloads, f.pendingELPayloads[1:])
f.pendingELPayloads[len(f.pendingELPayloads)-1] = PendingELPayload{}
f.pendingELPayloads = f.pendingELPayloads[:len(f.pendingELPayloads)-1]
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Acknowledged — the O(n) shift is real but this code only runs when the queue hits 1024 (abnormal condition, already logged as Warn). Under normal operation the cap is never reached. A ring buffer would be O(1) amortized but adds complexity for a safety valve that is not on the hot path. Leaving as-is for now.

domiwei and others added 4 commits May 19, 2026 07:41
…sip scoring

- forkchoice/utils: prune blockTimeliness sync.Map in onNewFinalized (memory leak)
- forkchoice: cap pendingELPayloads at 1024 with memory-conscious drain strategy
- forkchoice/payload_vote: use copy=true in notifyPtcMessages to avoid shared mutable state
- cltypes: deep-copy ExecutionPayloadEnvelope.Clone() via SSZ round-trip
- cltypes: fix NewExecutionPayloadEnvelope to use GloasVersion instead of BellatrixVersion
- operations: add nil checks for signedBid/signedBid.Message in ProcessExecutionPayloadBid
- operations: add BuilderIndex bounds check in verifyExecutionPayloadBidSignature
- gossip/score: replace strings.Contains with exact topic match for GLOAS topics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… Clone error logging

- payload_vote: pass PTC bit-position directly to applyPayloadAttestationVote
  instead of passing validatorIndex and re-scanning the PTC slice. Eliminates
  O(PtcSize²) per attestation and fixes incorrect PTC slot assignment when a
  validator appears more than once in the committee.
- epbs_payload: log.Error on SSZ encode/decode failure in
  ExecutionPayloadEnvelope.Clone() so the error is visible (clonable.Clonable
  interface has no error return).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@yperbasis yperbasis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, nice fixes. A few notes (none blocking):

  1. Test gap — no regression test for the actual deep-copy property of ExecutionPayloadEnvelope.Clone(). TestSignedExecutionPayloadEnvelopeCloneNilMessage only exercises the nil-Message path. Worth adding a test that clones a populated envelope, mutates a field on the clone (e.g. Payload.BlockNumber or BuilderIndex), and verifies the original is unchanged — this is the core bug the PR is fixing.

  2. Latent nil-panic in Clone() if e.beaconCfg == nil. NewExecutionPayloadEnvelope(nil)NewEth1Block(GloasVersion, nil) → dereferences nil.MaxBytesPerTransaction. Production envelopes carry a beaconCfg so it's not reachable today, but an e.beaconCfg == nil early-return would harden the fallback. The previous shallow code didn't allocate, so this case used to work.

  3. Duplicate-validator PTC issue still exists in OnPayloadAttestationMessage. Copilot flagged this for the in-block path (fixed here) but the gossip path at cl/phase1/forkchoice/on_payload_attestation_message.go:70-79 still does a linear scan that picks the first PTC index when a validator appears twice. Pre-existing, not a regression — but worth a follow-up tracking issue if get_ptc can actually produce duplicates.

  4. Style nits. In cl/cltypes/epbs_payload.go the new log \"github.com/erigontech/erigon/common/log/v3\" alias differs from the surrounding files (which import the package unaliased). In cl/phase1/forkchoice/forkchoice.go the same import is in its own group between stdlib and the erigontech block, breaking the grouping. New test files also lack copyright headers (mixed convention in cl/, but most have them).

  5. addPendingELPayload log spam. When the queue stays at cap under sustained back-pressure, log.Warn fires per insertion (up to 1024×). Acceptable since the cap shouldn't be hit under normal operation, but a one-shot or rate-limited warning would be friendlier.

@domiwei domiwei added this pull request to the merge queue May 21, 2026
Merged via the queue into main with commit 93480e4 May 21, 2026
73 checks passed
@domiwei domiwei deleted the kewei/gloas_audit_fixes_35 branch May 21, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Caplin Caplin: Consensus Layer, Beacon API Glamsterdam https://eips.ethereum.org/EIPS/eip-7773

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants