Skip to content

fix(server): guard uint64 subtractions in timing-games getHeader budget - #908

Open
SashaMIT wants to merge 1 commit into
flashbots:developfrom
SashaMIT:fix/timing-games-underflow
Open

fix(server): guard uint64 subtractions in timing-games getHeader budget#908
SashaMIT wants to merge 1 commit into
flashbots:developfrom
SashaMIT:fix/timing-games-underflow

Conversation

@SashaMIT

@SashaMIT SashaMIT commented Aug 7, 2026

Copy link
Copy Markdown

Problem

In handleTimingGamesGetHeader:

delayMs := relayConfig.TargetFirstRequestMs - msIntoSlot  // both uint64
if delayMs > 0 {
    timeoutLeftMs -= delayMs
    ...
}

Both operands are unsigned. Whenever the consensus client calls getHeader later than target_first_request_ms into the slot — the common case with the repo's own example config (target_first_request_ms: 200) — the subtraction wraps to ~2^64. The delayMs > 0 check then passes on the wrapped value, and timeoutLeftMs -= delayMs wraps in the opposite direction, i.e. it increases the remaining budget by (msIntoSlot - target).

The budget was deliberately computed as min(timeoutGetHeaderMs, lateInSlotTimeMs - msIntoSlot) so header requests stop at the late-in-slot deadline; the wrap silently defeats that bound and lets requests run past it. The symmetric case (target beyond the remaining budget) could zero/pre-expire requests instead.

Fix

  • Only compute/subtract when msIntoSlot < TargetFirstRequestMs (the wrap is then impossible).
  • Clamp the budget decrement at zero so the budget can never be pushed negative (which would wrap to ~2^64 as well).

Tests

go build ./... and go test ./server/ pass. The changed block is inside the per-relay timing-games path; no interface changes.

Made with Cursor

Made with Cursor

In handleTimingGamesGetHeader both operands of
delayMs = TargetFirstRequestMs - msIntoSlot are uint64. Whenever the
consensus client calls getHeader later than target_first_request_ms
into the slot (the common case), the subtraction wraps to ~2^64,
delayMs > 0 passes, and timeoutLeftMs -= delayMs INCREASES the
remaining budget by (msIntoSlot - target) - defeating the
late-in-slot deadline the budget was computed against and letting
header requests run past it. The inverse path (target beyond remaining
budget) could pre-expire every request.

Guard the wrap (only subtract when msIntoSlot < target) and clamp the
budget decrement at zero.
Copilot AI review requested due to automatic review settings August 7, 2026 07:31

Copilot AI left a comment

Copy link
Copy Markdown

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 fixes an unsigned integer underflow in handleTimingGamesGetHeader by guarding uint64 subtractions when calculating the initial timing-games delay and clamping the timeout budget reduction to avoid wraparound that could defeat the late-in-slot deadline.

Changes:

  • Guard TargetFirstRequestMs - msIntoSlot by only subtracting when msIntoSlot < TargetFirstRequestMs.
  • Clamp the timeout budget decrement to prevent timeoutLeftMs from wrapping.

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

Comment thread server/get_header.go
Comment on lines +209 to +213
} else {
// The target time is already at/past the budget end: nothing left.
timeoutLeftMs = 0
}
time.Sleep(time.Duration(delayMs) * time.Millisecond)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants