Replayed Telock leaves unsatisfiable verification dependency, stalling non-validator forever
Maintainer thường phản hồi trong vòng 1 ngày
Đá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
- 48/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
Hướng nghiên cứu
Start with scheduleNewFinalizedBlockTask in nonvalidator/non_validator.go and the pending task logic in common/block_scheduler.go; trace how indexed sequences and epochs affect queued dependencies. Then inspect the non-validator pruning path and existing tests. Done means stale replay tasks are removed and unmarked, superseded old-epoch blocks are not scheduled, and the described regression test passes.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Details
A Byzantine validator from the old epoch can permanently stall a non-validator node (one that follows the chain without voting) at one block height after an epoch change. This needs the old epoch to have ended with two or more Telocks (empty filler blocks). Only a restart recovers the node. The cause is that scheduleNewFinalizedBlockTask queues a replayed Telock whose parent is never indexed, and nothing removes that task.
flowchart TD
n1["Old-epoch validator that led T2's round"]
n2["handleFinalization and handleBlock"]
n3["scheduleNewFinalizedBlockTask"]
n4["BlockDependencyManager pending task for seq k+2"]
n5["Non-validator indexing at seq k+2 and later"]
n6["RemoveOldTasks (never called by non-validator)"]
n1 -->|"re-sends T2 finalization and block"| n2
n2 -->|"accepts and schedules T2"| n3
n3 -->|"queues task waiting on digest of T1"| n4
n4 -->|"keeps seq k+2 marked scheduled, real block refused"| n5
n6 -->|"not applied, task never removed"| 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 vulnerable_code
class n4 component
class n5 asset
class n6 missing_control
- Network finishes epoch
ewith a sealing block and two Telocks. Sealing blockSis at seqk, TelockT1atk+1and TelockT2atk+2. The Telocks are finalized but never indexed, and the new epochkreuses their sequence numbers. - Attacker (old-epoch validator that led
T2's round) sends the finalization ofT2to the non-validator.handleFinalizationstill finds epocheinn.epochsuntil the first epoch-kblockR1is indexed. It therefore verifies and stores the old-epoch finalization, and any peer could send it. (nonvalidator/non_validator.go:470-557) - Attacker sends the
T2block message as the round leader.handleBlockaccepts the block only from the leader of its round, and the attacker is that leader. A matching finalization is already stored, so the block is scheduled. (nonvalidator/non_validator.go:306-357) - the function
scheduleNewFinalizedBlockTaskregisters a task for seqk+2that waits ondigest(T1). Seqk+1is not indexed, so the task depends on theprevdigest from the finalization header. The task marks seqk+2as scheduled. (nonvalidator/non_validator.go:559-574) - the scheduler in
common/block_scheduler.gonever releases the waiting task.ExecuteBlockDependentsclears a dependency only when a finished task returns exactly that digest.R1returns its own digest, andT1's task has already finished or never runs. (common/block_scheduler.go:67-98) - the non-validator never prunes the stale task. After indexing it prunes sequences and epochs but never calls
RemoveOldTasks, so the task stays pending. (nonvalidator/non_validator.go:405-414) - the non-validator refuses the real block at seq
k+2.IsSequenceScheduledstays true for that sequence, so every later attempt to schedule it returns early. Later sequences wait behind it, and the node stops until restarted. (common/block_scheduler.go:131-137)
Evidence
- nonvalidator/non_validator.go:559–574
- common/block_scheduler.go:131–137
- common/block_scheduler.go:67–98
- nonvalidator/non_validator.go:306–357
- nonvalidator/non_validator.go:470–557
- nonvalidator/non_validator.go:405–414
Impact
The non-validator stops indexing at seq k+2 for good, until it is restarted. It cannot follow the chain, serve newer blocks by replication, or become a validator in a later epoch. Nothing is disclosed or altered.
Reproduction steps
- The attacker is the epoch-
evalidator that led the round of finalized TelockT2at seqk+2. It may be Byzantine, and it may already be removed from the validator set. The victim is a non-validator running across the epoch transition. That node has indexed the sealing block but not the first epoch-kblockR1. It must also not be in epochk's validator set, because members switch to validator mode. A node started afterSknows only epochkand is affected too. The attacker sends theT2finalization (any peer may) and theT2block message (leader only). Honest ordering can also trigger it:Sis indexed,T1's task finishes, thenT2arrives.incompleteSequences[k+2]keepsT2's finalization, so a re-sentT2block reschedules it even if the node already processed both Telocks correctly. The report did not run the reproduction. It comes from a static audit.
Recommended fix
(1) A verification task whose prev dependency can no longer be met stays pending forever and keeps its sequence marked as scheduled. This happens when the preceding sequence was indexed with a different digest, or when the task's epoch was dropped.
Fix criteria: When a sequence is indexed, pending tasks for later sequences are removed and their sequence is unmarked. This applies to tasks whose prev differs from the indexed digest and to tasks whose block epoch is below the minimum kept epoch. The genuine block for that sequence can then be scheduled. The test TestNonValidatorStallsOnStaleTelockDependency from the description passes by indexing seq 5 of epoch 3 after the Telock replay.
(2) The non-validator accepts and schedules old-epoch blocks at sequences above the new epoch's first block. It does not recognize them as superseded Telocks.
Fix criteria: A finalized block is not scheduled for verification in either of two cases. Its epoch is older than that of an already-indexed block at a lower or equal sequence. Or it is a Telock at a sequence above the new epoch's first block. Replaying such a block before seq k+1 is indexed no longer prevents seq k+2 from being indexed.
Status on main
Re-checked against main at 90995e0 on 2026-10-26; still present:
scheduleNewFinalizedBlockTask (nonvalidator/non_validator.go:559-574) schedules with prev=&bh.Prev whenever seq-1 is not indexed, and returns early when IsSequenceScheduled(seq) is true. BlockDependencyManager.IsSequenceScheduled (common/block_scheduler.go:131-137) stays true for a pending task until it runs or RemoveOldTasks drops it. The nonvalidator package never calls RemoveOldTasks (grep finds no call). After R1 is indexed, removeOldSequencesAndEpochs (non_validator.go:405-414) only prunes incompleteSequences and epochs. A T2 task waiting on digest(T1) is never released, so later genuine scheduling of k+2 via handleBlock, handleFinalization or processReplicationState (non_validator.go:356/551/691) is a no-op. PR #648 ('Ignore telocks') only changed tests.
Severity: MEDIUM
Status: Open
Category: Improper resource shutdown or release
CWE: CWE-404
Repository: ava-labs/Simplex
Branch: main
Date created: 2026-10-26
Claude Security finding: https://claude.ai/security/findings/find_0177fGoBt3cquBsrHSqEkpXS
- Ngôn ngữ chính
- Go
- Star
- 22
- Fork
- 4
- Merge trung bình
- 1 ngày 16 giờ
- Pull request đã merge (30 ngày)
- 35
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
-
Unverified sealing-block finalization lets a validator redirect a syncing node's replication requestsCó thể đã có người làm @yacovm đã nhận 2 ngày trước. Đang mởlow security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
ava-labs/Simplex#666 · 1 người được giao ·
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 epochsCó thể đã có người làm @yacovm đã nhận 3 ngày trước. Đ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
-
Failed notarization WAL write lets a node sign both a finalize vote and an empty voteCó thể đã có người làm @yacovm đã nhận 5 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 38/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Builder ignores parent's P-chain height, so a leader's maximal height stalls honest proposalsCó thể đã có người làm @yacovm đã nhận 5 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 55/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Honest builders never raise timestamp to parent's, so a leader's future timestamp voids honest proposalsCó thể đã có người làm @yacovm đã nhận 5 ngày trước. Đang mởhigh security
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 43/100
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ự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
OneBusAway/maglev#1914 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
documentation needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
Maintainer thường phản hồi trong vòng 1 ngày
-
go good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày
-
area: api bug severity:low
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
-
Missing validation for port numbers exceeding valid TCP range (> 65535)Có thể đã có người làm @faizanfirdousi đã nhận hôm nay. Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 80/100
llm-d/llm-d-inference-sim#853 ·
Maintainer thường phản hồi trong vòng 1 ngày