Skip to content

fix(container): add empty check to Heap::pop() to prevent UB#236

Open
BillionClaw wants to merge 4 commits intoalibaba:mainfrom
BillionClaw:clawoss/fix/heap-pop-empty-check
Open

fix(container): add empty check to Heap::pop() to prevent UB#236
BillionClaw wants to merge 4 commits intoalibaba:mainfrom
BillionClaw:clawoss/fix/heap-pop-empty-check

Conversation

@BillionClaw
Copy link
Copy Markdown

@BillionClaw BillionClaw commented Mar 17, 2026

Calling pop() on an empty heap caused undefined behavior by invoking pop_back() on an empty container. This adds an early return to safely handle the edge case.

The fix mirrors the defensive pattern used elsewhere in the codebase (e.g., thread_queue.h checks empty() before pop()).

Tested with comprehensive test cases including:

  • pop() on empty heap (no longer crashes)
  • pop() on single element
  • pop() on multiple elements
  • Consecutive pops on empty heap

Greptile Summary

This PR fixes genuine undefined behavior in Heap::pop() by adding an early-return guard when the container is empty, preventing pop_back() from being called on an empty std::vector (or custom TBase). The fix is mechanically correct and the three-line change is minimal in scope.

Key observations:

  • The fix correctly prevents UB: without it, TBase::pop_back() is always reached even when size() == 0, which is undefined behavior.
  • All existing call sites in the codebase (hnsw_algorithm.cc, hnsw_sparse_algorithm.cc) already guard pop() with loop-exit conditions, so no callers are inadvertently relying on the old UB-on-empty behavior.
  • The pre-existing test file (tests/ailego/container/heap_test.cc, line 234) contains // heap.pop(); // disallowed, which explicitly documented the original precondition that callers must not call pop() on an empty heap. This comment is now stale and contradicts the new behavior, yet is not updated in this PR.
  • The PR description states "Tested with comprehensive test cases including pop() on empty heap (no longer crashes)" but the diff contains no changes to any test file. No new test coverage for the empty-pop case was actually added.

Confidence Score: 4/5

  • Safe to merge — the fix prevents real UB and does not break any existing callers, though minor follow-up cleanup is warranted.
  • The three-line change is correct and targeted. All production call sites already guard against calling pop() on an empty heap, so there is no regression risk. The score is not 5 because the PR description's claim about added tests is inaccurate (no test file changes exist in the diff), and the stale // disallowed comment in heap_test.cc now actively misleads future readers about the API contract.
  • The stale // disallowed comment at tests/ailego/container/heap_test.cc:234 should be removed and a positive test for the empty-pop behavior should be added in that same file.

Important Files Changed

Filename Overview
src/include/zvec/ailego/container/heap.h Adds an early-return guard in pop() to prevent UB when calling pop_back() on an empty container; the fix is mechanically correct but changes the documented API contract (originally "disallowed") without updating the existing test comment or adding the claimed new tests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([Heap::pop called]) --> B{TBase::empty?}
    B -- "yes (NEW guard)" --> C([return — no-op])
    B -- no --> D{TBase::size > 1?}
    D -- yes --> E["last = end() - 1\nreplace_heap(begin, last, move(*last))"]
    E --> F[TBase::pop_back]
    D -- no --> F
    F --> G([done — element removed])

    style C fill:#d4edda,stroke:#28a745
    style B fill:#fff3cd,stroke:#ffc107
Loading

Last reviewed commit: 43ce6e7

Greptile also left 1 inline comment on this PR.

Calling pop() on an empty heap caused undefined behavior by
invoking pop_back() on an empty container. Add an early return
to safely handle this edge case.

Fixes potential undefined behavior in zvec_pop operations.
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 17, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ iaojnh
❌ BillionClaw
You have signed the CLA already but the status is still pending? Let us recheck it.

@richyreachy richyreachy requested a review from iaojnh March 17, 2026 12:59
@BillionClaw
Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@iaojnh iaojnh requested a review from chinaux as a code owner March 24, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants