# Vote timer callback uses potentially stale timer round instead of actual vote round, causing vote misrouting

Monad consensus vote timer callback passes the timer's stale round instead of the vote's actual round, misrouting votes to wrong leaders and degrading liveness.

- Payout: Contest reward
- Severity: medium
- Program: Monad
- Credited: ZeroEx
- Disclosed: 2026-02-26
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2025-09-monad#m-06
- Library page: https://bugbountylibrary.com/r/c4%3A2025-09-monad%3AM-06
- Tags: domain:web3 impact:dos kind:contest platform:code4rena sev:medium tech:rust technique:source-review vuln:business-logic

**Target:** BFT consensus vote timer handler (bft/monad-consensus-state lib.rs)
**Root cause:** handle_vote_timer extracts the stored scheduled vote but forwards the timer's round parameter rather than the vote's own round to send_vote_and_reset_timer, which derives leader recipients from that round.
**Steps:**
1. Node votes on round R and schedules a vote timer for round R+1 via `ScheduleVote { round: round + Round(1) }`.
2. Before the timer fires, node advances rapidly through R+1 to R+2 via QC messages/consecutive proposals.
3. Node processes proposal for R+2 and sets `scheduled_vote = VoteReady(v_R+2)`.
4. Stale timer for R+1 fires, invoking `handle_vote_timer(R+1)`.
5. Handler extracts v_R+2 but calls `send_vote_and_reset_timer(round=R+1, v_R+2)`, computing leaders from R+1 instead of v_R+2.round.
**How found:** Code review of handle_vote_timer noticing the round parameter is used instead of v.round when computing leader recipients in send_vote_and_reset_timer, combined with the one-round-ahead timer scheduling.
**Exploitation:** Not attacker-controlled directly; triggered by fast network conditions, burst QC processing, or catch-up after partitions causing rapid round advancement so stale timers fire after consensus progressed multiple rounds ahead.
**Escalation:** With 3+ round advancement, vote reaches neither current nor next leader, fully blocking QC formation for that round and forcing timeout/TC generation; cascading timer drift compounds misrouting.
**Insight:** When a callback carries both a scheduled-time parameter and a stored payload, check which one downstream logic trusts; timers scheduled one round ahead can fire after state advanced, so payload's own round must drive routing.
