Replicated notarized block dropped when the leader refills the round, stalling the validator
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 35/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- go
- Lĩnh vực
- distributed-systems, security
Hướng nghiên cứu
Start with simplex/epoch.go, especially processNotarizedBlock, handleBlockMessage, and createNotarizedBlockVerificationTask, then read the request handling in simplex/requestor.go. Reproduce the ordering race described in the issue by scheduling the notarized block task before storing a conflicting proposal. Done means the valid notarization is retained, the node advances to round R+1, and further proposals for R are ignored while the block is missing.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Details
A dishonest round leader, who must be a current validator, can keep an honest validator stuck in one round. With 4 validators, the stuck node plus the withholding leader leaves no quorum, so the chain stops. The cause is createNotarizedBlockVerificationTask: it drops a certified block when e.rounds[R] is refilled, and it does not ask for the block again. The attack is a race, not a sure lock.
flowchart TD
n1["dishonest round leader (validator)"]
n2["handleBlockMessage"]
n3["e.rounds[R] slot"]
n4["createNotarizedBlockVerificationTask"]
n5["victim validator stuck in round R"]
n6["re-request of the notarized block"]
n1 -->|"sends extra proposal for round R"| n2
n2 -->|"stores it, refilling the slot"| n3
n4 -->|"storeProposal refused"| n3
n4 -->|"gives up, node stays in round R"| n5
n6 -->|"not applied on this path"| n4
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 component
class n3 component
class n4 vulnerable_code
class n5 asset
class n6 missing_control
- the round leader sends a decoy block to the victim and the real block B to the other validators. The others notarize B, which means 2f+1 votes certify it, while the victim holds only the decoy.
- the victim's
processNotarizedBlockdeletes the conflicting unnotarized round and schedules a verification task. The task runs later on the single verification goroutine, so the slot is only empty at scheduling time, not when the task runs. (simplex/epoch.go:2166-2177) - the leader sends another proposal for the same round, queued ahead of the task.
handleBlockMessageaccepts it once the earlier task has cleared its in-flight flag, and nothing records that round R already has a notarization. (simplex/epoch.go:1984-1989) - the task from
createNotarizedBlockVerificationTaskfails to store B and returns the zero digest.storeProposalrefuses becausee.rounds[R]now holds a block. Unlike the verify-failure branch, this path does not callResendRoundRequest. (simplex/epoch.go:2412-2423) - the requestor sends no new request for round R. The request timeout was already cleared, and
observedSignedQuorumsends nothing oncehighestRequestedis at least R. (simplex/requestor.go:140-148) - the victim's empty-vote rebroadcast is the only way left to get B again, and each try faces the same race. Honest peers answer the rebroadcast with the notarization. A copy that finds the slot empty triggers a
BlockDigestRequestand a new task, which the leader can again try to beat. (simplex/epoch.go:1004-1015)
Evidence
- simplex/epoch.go:2412–2423
- simplex/epoch.go:2166–2177
- simplex/epoch.go:1984–1989
- simplex/epoch.go:431–446
- simplex/requestor.go:140–148
- simplex/epoch.go:3697–3705
- simplex/epoch.go:1004–1015
- simplex/util.go:150–185
Impact
The victim stays in round R and votes in no later round while the attacker keeps winning. With 4 validators the chain commits nothing during the stall. A single leader can divert up to f victims, and it can repeat the attack each time it leads. This is a race, not a guaranteed lock: a buffered copy replayed by maybeLoadFutureMessages deletes the leader's block again. A response processed while no leader task is queued ahead lets B through. Nothing is disclosed, and no conflicting block is notarized. A finalization cannot rescue the victim, because 2f honest finalize votes are below 2f+1.
Reproduction steps
- The attacker is a validator that leads round R. It needs no action from the victim. It sends the victim a decoy block and the other validators the real block B, then keeps sending the victim proposals for R. Each copy of B that reaches the victim, replicated or fetched by digest, is scheduled behind one of those proposals. The attacker must win this ordering race on every recovery attempt. It can bias the race. It can resend one block, which
OneTimeVerifierre-verifies instantly, or send distinct blocks to keep a task in flight. It also sees the victim's empty votes, so it knows when each rebroadcast cycle starts.
Recommended fix
(1) createNotarizedBlockVerificationTask refuses a verified block that carries a valid notarization for round R whenever e.rounds[R] holds any block, even an unnotarized one. It returns without re-requesting, so the certified block is lost and the node cannot leave R.
Fix criteria: A verified block with a valid notarization for round R ends up stored with that notarization, replacing any unnotarized block in the slot, or is re-requested until it is. Test it by scheduling the notarized-block task and storing a conflicting leader proposal before it runs. The node must persist the notarization and advance to R+1.
(2) After a mismatch deletes e.rounds[R], the node forgets that R already has a notarization. handleBlockMessage accepts new leader proposals for R, and the requestor's highestRequested gate stops later notarization copies from fetching the block.
Fix criteria: Once a valid notarization for round R is known, the node does not store or vote on further proposals for R. It keeps a request for the notarized block pending until the block is stored. A leader proposal for R sent after the mismatch is ignored. Each notarization copy received while the block is missing leads to a request or a live timeout.
Status on main
Re-checked against main at 90995e0 on 2026-10-26; still present:
In createNotarizedBlockVerificationTask (simplex/epoch.go:2419-2423), a storeProposal failure returns common.Digest{} without calling ResendRoundRequest. Only the verify-failure branch (epoch.go:2392-2398) and the queue-full branch (epoch.go:2200-2203) re-request. processNotarizedBlock deletes the conflicting round at scheduling time (epoch.go:2168-2177). handleBlockMessage (epoch.go:1915-2022) keeps no marker that round R has a known notarization, so a new leader proposal can refill e.rounds[R] before the task runs. No PR or issue found.
Severity: MEDIUM
Status: Open
Category: TOCTOU race condition
CWE: CWE-367
Repository: ava-labs/Simplex
Branch: main
Date created: 2026-10-26
Claude Security finding: https://claude.ai/security/findings/find_01V7wt29pkaVW2Seq83txZ3z
- Ngôn ngữ chính
- Go
- Star
- 22
- Fork
- 4
- Merge trung bình
- 1 ngày 17 giờ
- Pull request đã merge (30 ngày)
- 32
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của ava-labs/Simplex
-
low security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Epoch discovery accepts a sealing block on a head count, letting low-stake validators forge epochsĐang mởmedium security
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
medium security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 38/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Replicated certificate for a different block deletes the voted round, allowing a second voteCó thể đã có người làm @yacovm đã nhận hôm nay. Đang mởmedium security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 35/100
ava-labs/Simplex#662 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Replayed Telock leaves unsatisfiable verification dependency, stalling non-validator foreverCó thể đã có người làm @yacovm đã nhận 1 ngày trước. Đang mởmedium security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 48/100
ava-labs/Simplex#659 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của ava-labs/Simplex
Issue tương tự
-
Project submission: BivyĐang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
slavakurilyak/awesome-ai-agents#880 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
apache/openserverless#282 · 2 bình luận ·
-
Dokumentasjon på nais defaultsĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
agent-butler-finding bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
jordansmall/spindrift#6161 ·
Maintainer thường phản hồi trong vòng 1 ngày