Repository navigation
fix(heap): bound non-fixed MemmoveHeap Push to prevent buffer overflow (topk<10) [0.18] - #2300
Conversation
Merge Protections🟢 Merge protection satisfied — ready to merge. Show 1 satisfied protection🟢 Require linked issue for feature/bug PRs
|
There was a problem hiding this comment.
Code Review
This pull request addresses a buffer overflow issue in MemmoveHeap when topk < 10 by ensuring that the non-fixed heap's Push operation only searches up to cur_size_ and reuses stale tail slots instead of always appending. A regression test has been added to verify this behavior. The reviewer suggested a more robust approach to handle the buffer size check using resize instead of conditional emplace_back to prevent potential out-of-bounds writes.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
3615cb4 to
a4be575
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## 0.18 #2300 +/- ##
==========================================
+ Coverage 91.40% 91.77% +0.36%
==========================================
Files 335 335
Lines 20112 20114 +2
==========================================
+ Hits 18384 18460 +76
+ Misses 1728 1654 -74
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
731c74f to
267e07c
Compare
|
/label status/waiting-for-review |
LHT129
left a comment
There was a problem hiding this comment.
Thank you for this well-crafted fix. The root cause analysis is thorough and the fix is surgically precise:
-
memmove_heap.cpp: Boundingstd::upper_boundatbegin() + cur_size_(matching the already-correct fixed-size branch) and replacingemplace_backwith conditionalresize(cur_size_ + 1)are exactly the right changes. The stale-tail reuse viaresizeis a nice bonus that prevents unbounded buffer growth. -
distance_heap_test.cpp: The regression test faithfully replays the IVF Push/Pop interleaving pattern across topk 1–9 with a known triggering seed, and cross-validates against brute-force top-k. This is exactly the kind of targeted regression test a bug like this needs.
No blocking issues found. The existing review comments about the emplace_back → resize transition have been addressed in the current diff.
LHT129
left a comment
There was a problem hiding this comment.
LGTM. The fix correctly addresses the buffer overflow in the non-fixed MemmoveHeap::Push path:
upper_boundrange: bounded fromend()tobegin() + cur_size_, matching the fixed-size branch and preventing stale-tail insertion index from exceedingcur_size_.- Buffer growth:
resize(cur_size_ + 1)replacesemplace_back, reusing stale tail slots and bounding buffer growth. memmovesize: changed frommax_size_ - postocur_size_ - pos, eliminating the underflow risk whenpos > cur_size_.
The regression test exercises the exact IVF Push/Pop interleaving pattern across topk 1-9 and validates against brute-force top-k — good coverage of the buggy path.
LHT129
left a comment
There was a problem hiding this comment.
I have reviewed this PR thoroughly. The fix is correct and well-targeted:
Core fix (src/impl/heap/memmove_heap.cpp):
- Bounding
std::upper_boundatbegin() + cur_size_instead ofend()prevents the stale tail from producing apos > cur_size_, which would cause(cur_size_ - pos)to underflow to a hugesize_tin thememmovecall. - Replacing unconditional
emplace_backwithresize(cur_size_ + 1)when the buffer is too small bounds growth and reuses stale tail slots. This matches the suggestion from the earlier bot review and is the correct approach.
Test (src/impl/heap/distance_heap_test.cpp):
- The regression test faithfully replays the IVF
Push/Popinterleaving pattern acrosstopk1–9, which is exactly the scenario that triggers the bug. Validating against a brute-force top-k is the right way to verify correctness. - The test uses
seed=0which the PR description confirms triggers the overflow within a few dozen iterations — good choice for deterministic reproduction.
No concerns found. The change is minimal, localized, and has no API/ABI impact. The existing bot review comment has been addressed in the current diff.
LHT129
left a comment
There was a problem hiding this comment.
I have reviewed this cherry-pick of #2299 to the 0.18 release branch. The fix is identical in logic to the already-merged mainline PR.
Core fix (src/impl/heap/memmove_heap.cpp):
- Bounding
std::upper_boundatbegin() + cur_size_instead ofend()prevents the stale tail from producing apos > cur_size_, which would cause(cur_size_ - pos)to underflow to a hugesize_tand overrun the heap buffer. - Replacing unconditional
emplace_backwith conditionalresize(cur_size_ + 1)bounds the previously unbounded buffer growth and reuses stale tail slots.
Test (src/impl/heap/distance_heap_test.cpp):
- The regression test correctly replays the IVF
Push/Popinterleaving pattern fortopk1-9 with a triggering seed (0), and validates the retained records against a brute-force top-k. - The test only covers
MemmoveHeap<true, false>(max-heap, non-fixed), which is the variant used by IVF. TheMemmoveHeap<false, false>path has the same bug but is not exercised by any production code path; this is acceptable for a release-branch cherry-pick.
No issues found. The fix is correct and well-tested.
LHT129
left a comment
There was a problem hiding this comment.
This is a clean, well-focused cherry-pick of #2299 to the 0.18 release branch. The fix is minimal and correct, and the regression test thoroughly covers the affected code path.
Fix (src/impl/heap/memmove_heap.cpp):
std::upper_boundis now correctly bounded to[begin, begin + cur_size_)instead of searching the stale tail, which was the root cause of the overflow.- The
resize(cur_size_ + 1)guard replacesemplace_back, ensuring the buffer always has enough storage for the subsequentmemmoveand element assignment — this is more robust and addresses the earlier review suggestion. - The
memmovesize(cur_size_ - pos)is now guaranteed non-negative sinceposis bounded bycur_size_.
Test (src/impl/heap/distance_heap_test.cpp):
- The regression test correctly mirrors IVF's actual Push/Pop interleaving pattern across all topk 1–9.
- Validates results against a brute-force sorted reference, which is the right approach.
No issues found. The changes are correct, well-tested, and appropriate for a release-branch cherry-pick.
LHT129
left a comment
There was a problem hiding this comment.
The fix is clean, minimal, and well-tested. The two changes in memmove_heap.cpp are correct:
-
upper_boundrange bounded tobegin() + cur_size_— prevents searching the stale tail that accumulates afterPop(), which was the root cause of the buffer overflow. -
resize(cur_size_ + 1)guard replacingemplace_back— ensures the buffer has enough capacity before thememmovewithout unbounded growth from repeatedemplace_back.
The memmove size (cur_size_ - pos) is now safe because pos is guaranteed to be in [0, cur_size_] by the bounded upper_bound.
The regression test in distance_heap_test.cpp correctly mirrors the IVF Push/Pop interleaving pattern across topk 1–9 and validates against a brute-force top-k, which is exactly the scenario that triggers the bug.
No issues found. This is a solid cherry-pick to the 0.18 release branch.
The non-fixed MemmoveHeap searched its whole ordered buffer on Push, including the stale tail left behind by Pop, which only decrements the size. Interleaved Push/Pop with topk < 10 -- the pattern used by IVF search -- could then compute a negative memmove size and overrun the heap. Bound the search to the live region and add a regression test that replays that interleaving. Signed-off-by: jc543239 <jc543239@antgroup.com> Signed-off-by: jac0626 <jac0626@users.noreply.github.com> Signed-off-by: LHT129 <lht129@users.noreply.github.com> Assisted-by: Codex:GPT-5
267e07c to
3d4578e
Compare
Merge Protections🟢 Merge protection satisfied — ready to merge. Show 1 satisfied protection🟢 Require linked issue for feature/bug PRs
|
Change Type
0.18)Linked Issue
Note
Cherry-pick of #2299 (target
main) onto the0.18release branch. Thebuggy code is identical on
0.18; only a trivial include-block contextdiffered in the test file and was resolved. Verified on the
0.18tree(all topk 1-9 correct, no overflow under AddressSanitizer).
What Changed
The non-fixed
MemmoveHeap(MemmoveHeap<max_heap, false>) ranstd::upper_boundover the entireordered_buffer_(begin()..end()).Because
Pop()only decrementscur_size_without shrinkingordered_buffer_,a stale tail accumulates, so the insertion index
poscould exceedcur_size_and make
memmove(..., (cur_size_ - pos) * sizeof(DistanceRecord))underflow toa huge
size_t, overrunning the heap buffer.DistanceHeap::MakeInstanceBySizeselectsMemmoveHeaponly whenmax_size < 10, andIVF::searchrequests the non-fixed variant(
MakeInstanceBySize<true, false>(topk)) then interleavesPushwith a boundedPop. So anyKnnSearchwithtopk < 10reaches the buggy path; whether itoverruns is data-dependent. The fixed-size branch was already correct (it bounds
upper_boundatbegin() + cur_size_).upper_boundatbegin() + cur_size_, matchingthe already-correct fixed-size branch.
emplace_backwhenordered_buffer_.size() > cur_size_, which also bounds the previouslyunbounded buffer growth.
memmove_heap non-fixed Pop-then-Push (topk<10)insrc/impl/heap/distance_heap_test.cpp) replaying IVF'sPush/Popinterleaving across
topk1–9 with a triggering seed, validated against abrute-force top-k.
Test Evidence
make fmtmake lintmake testmake cov, run tests, and collect coverageTest details:
Compatibility Impact
to
src/impl/heap/).KnnSearchwithtopk < 10now returns correct top-kresults without memory corruption, matching
topk >= 10(StandardHeap).Performance and Concurrency Impact
Pushnow searches a tighterrange (
[begin, begin + cur_size_)) and reuses slots, reducing reallocations.Documentation Impact
Risk and Rollback
MemmoveHeap::Pushbranch; the fixed-size branch and
StandardHeapare untouched.Checklist