Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Failed notarization WAL write lets a node sign both a finalize vote and an empty vote

Đang mở
#658 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@yacovm đang làm issue này rồi.

Từ ngày 27/10/2026.

  • #672 của @yacovm — đang mở

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
38/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

Hướng nghiên cứu

Read simplex/epoch.go around persistNotarization, persistAndBroadcastNotarization, rebroadcastPastFinalizeVotes, and triggerEmptyBlockNotarization. Trace how a failed WAL.Append affects in-memory state and the two signing paths; then inspect the relevant epoch tests for WAL failure injection. Done when a failed append prevents the node from signing both vote types for the same round.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

medium security

Details

A validator that hits one failed disk write can sign both a finalize vote and an empty vote for the same round. That breaks a rule Simplex safety depends on, and up to f dishonest validators can then form both a finalization and an empty notarization for that round. It needs a transient write-ahead log (WAL) fault on the node plus a relayed finalization for an earlier round. The cause is that persistAndBroadcastNotarization returns the WAL.Append error without halting the epoch or undoing the in-memory notarization.

flowchart TD
  n1["peer relaying an earlier finalization"]
  n2["rebroadcastPastFinalizeVotes"]
  n3["triggerEmptyBlockNotarization"]
  n4["round timer from monitorProgress"]
  n5["safety of round r (no finalize vote and empty vote together)"]
  n6["halt via haltedError after failed append"]
  n1 -->|"triggers finalize vote signing"| n2
  n2 -->|"signs finalize vote for r"| n5
  n4 -->|"fires for r"| n3
  n3 -->|"signs empty vote for r"| n5
  n6 -->|"not set, node keeps running"| 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 vulnerable_code
  class n4 component
  class n5 asset
  class n6 missing_control
  1. the handlers maybeCollectNotarization and handleNotarizationMessage mark round r as notarized in memory before saving it. They call storeNotarization, which sets round.notarization, and only afterwards call the persist code. (simplex/epoch.go:1907-1912)
  2. the function persistNotarization returns the WAL.Append error before increaseRound runs. A transient disk fault makes the append fail, so the epoch never leaves round r. (simplex/epoch.go:1742-1768)
  3. the function persistAndBroadcastNotarization passes the error up without halting or rolling back. e.haltedError is not set and round.notarization is not cleared, so memory says round r is notarized while the log has no record of it. (simplex/epoch.go:1771-1775)
  4. the epoch keeps processing messages and time. HandleMessage stops only once haltedError is set, and AdvanceTime is never gated. (simplex/epoch.go:136-165)
  5. a peer relays a valid finalization for an earlier, uncommitted round. The finalization has Seq above nextSeqToCommit. It goes through handleFinalizationMessage and persistFinalization into rebroadcastPastFinalizeVotes, which signs a finalize vote for round r because it trusts the in-memory notarization. (simplex/epoch.go:1418-1461)
  6. the function triggerEmptyBlockNotarization signs and broadcasts an empty vote for round r when the round timer fires. The timer is cancelled only by increaseRound. The function checks only e.round > round and an earlier timeout, never an earlier finalize vote, so it signs. (simplex/epoch.go:2779-2821)
  7. up to f dishonest validators combine the two signatures into a finalization and an empty notarization for round r. Once an empty notarization for r exists, a later proposal that skips r is accepted by blockDependencies.

Evidence

  1. simplex/epoch.go:1771–1775
  2. simplex/epoch.go:1742–1768
  3. simplex/epoch.go:1907–1912
  4. simplex/epoch.go:1418–1461
  5. simplex/epoch.go:2779–2821
  6. simplex/epoch.go:136–165
  7. simplex/epoch.go:2179–2187

Impact

The affected node acts as one extra Byzantine validator for that round. A finalized round can also be skipped, which defeats the safety guarantee of the protocol. The report finds no data disclosure and no outage from this flaw.

Reproduction steps

  1. The trigger is a local fault the attacker does not control: one WAL.Append of a notarization record fails transiently on the victim validator, such as a disk or I/O error. The later appends at :1372 and :2805 must succeed, so the fault must be transient. After that, any peer can relay a valid finalization for an earlier, uncommitted round, and the report says this needs only low privileges. The finalize vote must be signed before the timer fires, because maybeConstructFinalizeVoteMessage refuses to sign after a timeout. Up to f Byzantine validators then collect both signatures.

Recommended fix

(1) After a failed notarization append, persistAndBroadcastNotarization and persistNotarization leave e.rounds[r].notarization set and the epoch running at round r. In-memory state is ahead of the durable log.

Fix criteria: A failed notarization append either halts the epoch, by setting haltedError as the replication paths do and stopping timers from signing, or fully rolls back the in-memory notarization. A test with an injected Append failure shows that no finalize vote is ever signed for that round.

(2) The signing paths do not exclude each other. rebroadcastPastFinalizeVotes trusts the in-memory notarization for the current round, and triggerEmptyBlockNotarization does not check whether a finalize vote was already cast for the round.

Fix criteria: Signing a finalize vote and signing an empty vote for the same round are mutually exclusive, whatever the persistence outcome. A test with any single injected Append failure shows that this node's broadcasts never include both message types for one round.

Status on main

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

persistNotarization (simplex/epoch.go:1742-1745) returns the writeNotarizationToWal error before increaseRound, and persistAndBroadcastNotarization (epoch.go:1771-1775) propagates it without setting e.haltedError or clearing round.notarization, which storeNotarization already set (epoch.go:1907-1912, 446). HandleMessage (epoch.go:163) only stops once haltedError is set, and AdvanceTime (epoch.go:136-143) is not gated. rebroadcastPastFinalizeVotes (epoch.go:1418-1461) trusts in-memory round.notarization for rounds up to e.round, and triggerEmptyBlockNotarization (epoch.go:2779-2795) checks only e.round>round and timedOut, not a prior finalize vote. Whether this is exploitable depends on the host continuing after HandleMessage returns an error. No PR or issue found.


Severity: MEDIUM
Status: Open
Category: Improper error handling
CWE: CWE-755
Repository: ava-labs/Simplex
Branch: main
Date created: 2026-10-26
Claude Security finding: https://claude.ai/security/findings/find_01JC24yL5oTXowYhuYwx6nDi

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)
33

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

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. 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.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của ava-labs/Simplex

Tất cả issue của ava-labs/Simplex

Issue tương tự

Thêm issue về Go

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.