Skip to content

Fix off-by-one in PushBlock that causes nil dereference panic#2924

Open
wen-coding wants to merge 7 commits intomainfrom
wen/fix_pushblock_verify
Open

Fix off-by-one in PushBlock that causes nil dereference panic#2924
wen-coding wants to merge 7 commits intomainfrom
wen/fix_pushblock_verify

Conversation

@wen-coding
Copy link
Contributor

@wen-coding wen-coding commented Feb 18, 2026

Summary

  • Fix off-by-one in PushBlock: The WaitUntil condition used n <= inner.nextQC, which allows PushBlock to proceed when n == nextQC. Since inner.qcs stores entries for the half-open range [first, nextQC), the map lookup returns nil, and the subsequent qc.Headers() call panics with a nil pointer dereference. Changed to n < inner.nextQC to match every other waiter in the file (QC, Block, GlobalBlock, AppProposal).

PushBlock's WaitUntil used n <= inner.nextQC, allowing it to proceed
when n == nextQC. Since inner.qcs stores [first, nextQC), the lookup
returns nil, causing a panic on qc.Headers(). Changed to n < nextQC
to match every other waiter in the file. Added defense-in-depth nil
check with a descriptive error message.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions
Copy link

github-actions bot commented Feb 18, 2026

The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedFeb 19, 2026, 8:11 PM

@codecov
Copy link

codecov bot commented Feb 18, 2026

Codecov Report

❌ Patch coverage is 0% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 57.33%. Comparing base (7f57cdb) to head (cb0235f).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
sei-tendermint/internal/autobahn/data/state.go 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2924      +/-   ##
==========================================
- Coverage   57.35%   57.33%   -0.02%     
==========================================
  Files        2095     2095              
  Lines      172870   172870              
==========================================
- Hits        99142    99110      -32     
- Misses      64842    64869      +27     
- Partials     8886     8891       +5     
Flag Coverage Δ
sei-chain 52.83% <0.00%> (-0.03%) ⬇️
sei-cosmos 48.19% <ø> (+<0.01%) ⬆️
sei-db 68.42% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
sei-tendermint/internal/autobahn/data/state.go 64.94% <0.00%> (+1.03%) ⬆️

... and 24 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

wen-coding and others added 4 commits February 18, 2026 12:41
WaitUntil(n < nextQC) guarantees inner.qcs[n] is non-nil, making the
defense-in-depth nil check dead code that triggers coverage complaints.

Co-authored-by: Cursor <cursoragent@cursor.com>
@pompon0 pompon0 self-requested a review February 19, 2026 10:07
"github.com/sei-protocol/sei-chain/sei-tendermint/internal/autobahn/types"
"github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils"
"github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils/scope"
"github.com/stretchr/testify/require"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libs/utils/require. It contains a strongly typed wrappers of testify/require. If you are missing any wrappers, you can add them there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

_, err := state.TryBlock(gr2.First)
require.ErrorIs(t, err, ErrNotFound)

// PushBlock for a block in qc2's range. With the off-by-one bug
Copy link
Contributor

@pompon0 pompon0 Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: scope.Run?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed

// (n <= inner.nextQC), this would immediately dereference a nil QC
// pointer and panic. With the fix, it waits for the QC.
errc := make(chan error, 1)
go func() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like a great use case for https://go.dev/blog/synctest . Do you want to give it a try?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Replace manual goroutine + channel pattern with synctest.Test/Wait,
enabling a stronger intermediate assertion that the block is absent
while PushBlock is blocked. Also switch to libs/utils/require.

Co-authored-by: Cursor <cursoragent@cursor.com>
@wen-coding wen-coding requested a review from arajasek February 19, 2026 19:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments