Repository navigation
Fix: Bound entry slot and max level from the file header - #824
Merged
ashvardanian merged 1 commit intoOct 24, 2026
Merged
Conversation
peterkc
marked this pull request as ready for review
October 23, 2026 15:41
`load` and `view` took the graph header's `entry_slot` and `max_level` without checks. A correctly sized file with a corrupted header was accepted, and the first search read past the stored nodes and crashed. Both paths now reject a non-empty file when `entry_slot` is not below the node count, or when `max_level` exceeds the entry node's level. `view` resets before failing, as for the other size checks. The test of truncated files now also corrupts each field and expects `load` and `view` to fail cleanly.
ashvardanian
force-pushed
the
fix/header-entry-slot-bounds
branch
from
October 24, 2026 21:21
0995ef6 to
cd4ef3e
Compare
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.
Summary
index_gtloadsentry_slotandmax_levelfrom the serialized header without checking them, in both the streamload()andview(). A corrupted or crafted.usearchfile of the correct size opens without an error, and the first search reads out of bounds.For a non-empty file, both paths now return an error:
entry_slot >= size: "File is corrupted: entry slot exceeds node count". Search starts atnodes_[entry_slot], past the node array.max_levelabove the entry node's level: "File is corrupted: max level exceeds entry node level".search_for_one_readsneighbors_non_base_(entry, level)for each level frommax_leveldown to 1, past the entry node.view()callsreset()before it fails, as Bound file-controlled sizes in view(): levels region and dense vectors span (OOB read on crafted index) #776 does. The empty-index early return is unchanged, because an empty save writesmax_level = -1.Diagrams
This follows #776, which bounded the node count and the dense matrix span in
view(). As in that PR, the same checks appear in both paths.Impact
Loading or viewing a corrupted or crafted index file crashes on the first search (SIGSEGV) or reads past the node array or the entry node without a crash. This applies to every binding that calls
load()orview(). CWE-125. We found it while testing corrupted index files through the Go binding.Validation
test_corrupted_graph_headerincpp/test.cpp. It saves a 256-vector index, setsentry_slot = size, thenmax_level + 1, and expectsload()andview()to fail cleanly for each.test_cppaborts becauseload()accepts the file. A badentry_slotcrashes both paths (SIGSEGV; ASan: heap-buffer-overflow). A badmax_levelcrashesview()and makesload()read out of bounds without a crash.test_cpppasses in the CMake Debug build with ASan and UBSan, with no sanitizer reports.view()never reads node bytes, so checking them would add a full pass over the file. Aload()-only check could follow.