Follow-up to #24259 (background sync-committee gossip publish), flagged by domiwei during review, explicitly out of scope for that PR.
Problem
EthereumClock.GetSlotTime and IsSlotCurrentSlotWithMaximumClockDisparity (cl/utils/eth_clock/ethereum_clock.go) use unchecked uint64 arithmetic (genesisTime + SecondsPerSlot*slot, and the reverse conversion in GetSlotByTime). On mainnet, a large but bounded slot value - domiwei's example: 4611686018442700405 - wraps under this arithmetic to a timestamp that maps back to the current slot, so IsSlotCurrentSlotWithMaximumClockDisparity returns true for it.
cl/phase1/network/services/sync_committee_messages_service.go's ProcessMessage uses exactly this check as its [IGNORE] gate before signature verification. The sync-committee message signature does not cover the slot field, so a forged slot aliasing to "now" can pass this check, enter the pool, and be forwarded over gossip - independent of #24259's own admission-side guard (maxFutureSlotLookahead in cl/beacon/handler/pool.go), which only bounds what reaches the publish side, not what ProcessMessage/the gossip validator itself accepts.
Scope note
Not introduced by #24259 - the underlying arithmetic pre-dates it. #24259's own syncCommitteeMessageExpiry guard already handles the equivalent overflow on the publish-admission side (see its maxFutureSlotLookahead bound and TestSyncCommitteeMessageExpiryDoesNotAliasToFutureForHugeSlot), but that guard has no effect here since this is a different call path (gossip/pool ingestion, not REST-triggered publish).
Candidate direction (not yet implemented/reviewed)
Bound-check slot inputs before any slot-to-time arithmetic in eth_clock (analogous to the guard added in #24259), or reject slots beyond a sane lookahead earlier in the validation pipeline, before IsSlotCurrentSlotWithMaximumClockDisparity is ever reached. Since GetSlotTime/GetSlotByTime are used broadly across the codebase, a fix likely belongs in eth_clock itself rather than at each call site.
Follow-up to #24259 (background sync-committee gossip publish), flagged by domiwei during review, explicitly out of scope for that PR.
Problem
EthereumClock.GetSlotTimeandIsSlotCurrentSlotWithMaximumClockDisparity(cl/utils/eth_clock/ethereum_clock.go) use uncheckeduint64arithmetic (genesisTime + SecondsPerSlot*slot, and the reverse conversion inGetSlotByTime). On mainnet, a large but bounded slot value - domiwei's example:4611686018442700405- wraps under this arithmetic to a timestamp that maps back to the current slot, soIsSlotCurrentSlotWithMaximumClockDisparityreturns true for it.cl/phase1/network/services/sync_committee_messages_service.go'sProcessMessageuses exactly this check as its[IGNORE]gate before signature verification. The sync-committee message signature does not cover the slot field, so a forged slot aliasing to "now" can pass this check, enter the pool, and be forwarded over gossip - independent of #24259's own admission-side guard (maxFutureSlotLookaheadincl/beacon/handler/pool.go), which only bounds what reaches the publish side, not whatProcessMessage/the gossip validator itself accepts.Scope note
Not introduced by #24259 - the underlying arithmetic pre-dates it. #24259's own
syncCommitteeMessageExpiryguard already handles the equivalent overflow on the publish-admission side (see itsmaxFutureSlotLookaheadbound andTestSyncCommitteeMessageExpiryDoesNotAliasToFutureForHugeSlot), but that guard has no effect here since this is a different call path (gossip/pool ingestion, not REST-triggered publish).Candidate direction (not yet implemented/reviewed)
Bound-check slot inputs before any slot-to-time arithmetic in
eth_clock(analogous to the guard added in #24259), or reject slots beyond a sane lookahead earlier in the validation pipeline, beforeIsSlotCurrentSlotWithMaximumClockDisparityis ever reached. SinceGetSlotTime/GetSlotByTimeare used broadly across the codebase, a fix likely belongs ineth_clockitself rather than at each call site.