Honest builders never raise timestamp to parent's, so a leader's future timestamp voids honest proposals

未关闭
#654 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

@yacovm 已经在做这个了。

开始于 2026年10月27日。

  • #669 来自 @yacovm —— 未关闭

评估

难度
4/5
预计耗时
3-5 天
新手友好度
43/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
go

调研方向

Read the builders and timestamp validation in msm/msm.go, starting with buildBlockOrTransitionEpoch and verifyTimestamp; then check the other builder paths at the cited locations for normal, collecting-approvals, Telock, and new-epoch blocks. Reproduce the stated case with a parent timestamped T+9s and verify the built block against its actual parent. Done means every honest builder's block passes VerifyBlock in that case.

由索引模型根据 Issue 内容生成。

描述

high security

Details

One Byzantine validator can stop honest leaders from getting blocks accepted, so the chain advances mostly through the attacker's own blocks. It needs only its normal turn as round leader. Honest builders stamp blocks with their own clock and never raise it to the parent's timestamp, so a future-dated parent makes their blocks fail verifyTimestamp.

flowchart TD
  n1["Byzantine validator acting as leader"]
  n2["honest builders in msm/msm.go (stamp raw sm.GetTime)"]
  n3["verifyTimestamp"]
  n4["honest leaders' proposals and chain progress"]
  n5["clamp of timestamp to parent's"]
  n1 -->|"future-dated block accepted"| n3
  n2 -->|"child stamped below parent"| n3
  n3 -->|"rejects honest blocks, rounds skipped"| n4
  n5 -->|"not applied"| n2
  classDef actor stroke:#2563eb,stroke-width:2px
  classDef vulnerable_code stroke:#dc2626,stroke-width:2px
  classDef existing_control stroke:#16a34a,stroke-width:2px
  classDef missing_control stroke:#dc2626,stroke-width:2px,stroke-dasharray:5 5
  classDef asset stroke:#9333ea,stroke-width:2px
  classDef component stroke:#6b7280,stroke-width:1px
  class n1 actor
  class n2 vulnerable_code
  class n3 existing_control
  class n4 asset
  class n5 missing_control
  1. Byzantine validator proposes a block stamped up to about 10 seconds ahead of real time on its turn as leader. The verifier accepts any timestamp up to TimeSkewLimit ahead, and production wiring sets no TimeSkewLimit, so the default maxSkew = 10s applies. (instance.go:570-591)
  2. verifyTimestamp accepts the future-dated block, which is then notarized. It rejects a timestamp below the parent's but allows one up to now + timeSkewLimit. (msm/msm.go:526-540)
  3. the next honest leader's builder stamps its child block with the raw local clock. buildBlockOrTransitionEpoch uses sm.GetTime() and never raises it to the parent's timestamp, so the child is stamped earlier than its parent. (msm/msm.go:617-632)
  4. the honest leader's BuildBlock proposes the block without verifying it. The leader's own node does not run VerifyBlock on what it builds, so only other nodes catch the bad timestamp. (adapters.go:263-277)
  5. honest verifiers reject the block with errTimestampDecreasing. verifyNonZeroBlock runs verifyTimestamp first on every non-zero proposal, against the real parent. (msm/msm.go:498-504)
  6. honest nodes empty-vote at once, so the round is skipped. triggerEmptyBlockNotarization is called on the verification failure, and the parent stays the attacker's block. (simplex/epoch.go:2227-2242)
  7. Byzantine validator builds another future-dated child on its next turn. LeaderForRound is round-robin over round numbers, so the attacker leads again after a fixed number of rounds, skipped ones included. (simplex/epoch.go:3827-3830)

Evidence

  1. msm/msm.go:617–632
  2. msm/msm.go:1076–1077
  3. msm/msm.go:1477–1482
  4. msm/msm.go:526–540
  5. msm/msm.go:498–504
  6. external.go:62–81
  7. adapters.go:263–277
  8. simplex/epoch.go:2227–2242
  9. simplex/epoch.go:3827–3830
  10. instance.go:570–591

Impact

Honest leaders' proposals are rejected for up to TimeSkewLimit (10s) after each attacker turn. If the attacker's turns recur inside that window, honest leaders never land a block. The chain then advances only through attacker-built blocks, at a fraction of its normal rate, and the attacker picks every included transaction. No data is disclosed or changed, and every finalized block is valid.

Reproduction steps

  1. The attacker is a single Byzantine validator, which the report treats as the primary threat actor. It needs only its own round-robin turn as leader. Only a validator's signed leader proposal is processed. Whether the attack repeats without end depends on how soon the attacker's turns recur. That depends on validator-set size and network delay. With small validator sets a failed round ends after one network delay, so the attacker's turns can recur inside the 10 second window. Each turn is bounded by TimeSkewLimit.

Recommended fix

The builders at msm/msm.go:588, :617, :1076 and :1478 stamp blocks with the raw local clock. The verifier requires timestamps that never decrease but accepts a parent up to TimeSkewLimit in the future. Honest builders therefore make blocks that honest verifiers reject.

Fix criteria: Every block an honest node builds passes verifyTimestamp against its actual parent, the block named by metadata.Prev, for example by using a timestamp at least the parent's. That parent is not the sealing block that buildBlockEpochSealed passes on. Test: with a verifier and an honest builder at time T, a block the honest builder builds on a parent stamped T+9s is accepted by VerifyBlock. Check this for normal, collecting-approvals, Telock and new-epoch blocks.

Status on main

Re-checked against main at 90995e0 on 2026-10-26; still present:

Every builder still stamps blocks with sm.GetTime() directly and never clamps to the parent's timestamp: msm/msm.go:588, 617, 1076 and 1478, via wrapBlock at msm/msm.go:1422. verifyTimestamp (msm/msm.go:526-541) rejects any block whose timestamp is below its parent's (531-533), but accepts timestamps up to now+TimeSkewLimit. The default limit is 10s (msm/msm.go:24, 232-233), and production wiring uses GetTime: time.Now (instance.go:571). No issue or PR tracks this.


Severity: HIGH
Status: Open
Category: Improper input validation
CWE: CWE-20
Repository: ava-labs/Simplex
Branch: main
Date created: 2026-10-26
Claude Security finding: https://claude.ai/security/findings/find_01PE8Rjj8nHkYYyQHYY372eT

主要语言
Go
星标
22
派生
4
平均合并
1 天 16 小时
30 天内合并 PR
35

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

ava-labs/Simplex 的其他 Issue

查看 ava-labs/Simplex 的全部 Issue

相似的 Issue

更多 Go Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。