Fix reverse secondary-index scans over an inverted range - #644
Merged
Merged
Conversation
The reverse secondary-index scan starts at the upper bound and walks down to begin, the lower bound's position. With upper_bound below lower_bound it started below begin, so its first step down decremented the index's begin() whenever the table's partition is the first in the whole secondary index: undefined behavior, reachable by any API caller against the contract whose account sorts first. Start at begin instead, so an inverted range is empty, as it already is forward and on the primary index.
huangminghuang
approved these changes
Sep 25, 2026
huangminghuang
left a comment
Contributor
There was a problem hiding this comment.
Reviewed the diff and surrounding index and bounds-handling code at f7b5123. No actionable findings. Validation was source-based; I did not run the PR tests locally.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A reverse
get_table_rowson a secondary index withupper_boundbelowlower_boundstarted its scan below its own stopping point. When the queried table's partition comes first in the chain's secondary index (true of whichever contract account sorts lowest), the scan's first step decremented the index'sbegin(). That is undefined behavior any API caller could trigger:The response already came back empty in our builds, so callers see no change. The scan now starts at the lower bound's position when the range is inverted, so the page is empty by construction, as it already was forward and on the primary index.
get_kv_rows_inverted_range_testchecks an inverted range on both indexes in both directions. The contract is deployed to1secrevso that its secondary partition comes first in the index, and the reverse scan lands on the index's first entry. The test also passes against the old code, because the stray read happened to come back empty. With a temporary assertion on the decrement, the test tripped it on the old code, and the assertion stayed silent with the fix.