Skip to content

fix: resize Segment data_ to match capacity in ConcurrentArrayInvertedLists#1641

Merged
sre-ci-robot merged 1 commit into
zilliztech:mainfrom
MickeyPvX:fix-segment-data-resize
May 26, 2026
Merged

fix: resize Segment data_ to match capacity in ConcurrentArrayInvertedLists#1641
sre-ci-robot merged 1 commit into
zilliztech:mainfrom
MickeyPvX:fix-segment-data-resize

Conversation

@MickeyPvX

@MickeyPvX MickeyPvX commented May 21, 2026

Copy link
Copy Markdown
Contributor

Problem

In thirdparty/faiss/faiss/cppcontrib/knowhere/invlists/InvertedLists.h, the Segment helper inside ConcurrentArrayInvertedLists reserves capacity in its constructor but never sizes the underlying buffer:

template <typename T>
struct Segment {
    Segment(size_t segment_size, size_t code_size)
        : segment_size_(segment_size), code_size_(code_size) {
        data_.reserve(segment_size_ * code_size_);
    }
    T& operator[](idx_t idx) {
        assert(idx < segment_size_);
        return data_[idx * code_size_];
    }
    ...
    std::vector<T> data_;
};

After the constructor runs, data_.size() == 0 while data_.capacity() is segment_size_ * code_size_. The subsequent operator[] accesses (directly and via the memcpy calls in add_entries) read and write at offsets that are within the allocation but past size(). Calling std::vector::operator[] with an index that is not less than size() is undefined behavior even when the bytes are allocated.

What this breaks

On a libstdc++ built with -D_GLIBCXX_ASSERTIONS (the hardened libstdc++ shipped by several distributions), operator[] has a bounds check compiled in. The first streaming insert into an IVF_FLAT_CC or IndexIVFScalarQuantizerCC collection trips it and the process aborts:

/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/c++/bits/stl_vector.h:1128:
std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::operator[](size_type)
[with _Tp = long int; size_type = long unsigned int]: Assertion '__n < this->size()' failed.
SIGABRT: abort signal arrived during cgo execution
_ZSt21__glibcxx_assert_failPKciS0_S0_
_ZN5faiss28ConcurrentArrayInvertedLists11add_entriesEmmPKlPKhPKf
_ZN5faiss13InvertedLists9add_entryEmlPKhPKfPv
_ZN5faiss12IndexIVFFlat8add_coreElPKfS2_PKlS4_Pv

Unhardened libstdc++ compiles operator[] to plain pointer arithmetic with no check, so the same code runs without complaint.

Fix

Use resize() instead of reserve() so size() == capacity() after construction. operator[] is then in-bounds, and the memcpy writes that follow keep working unchanged (they were already assuming the storage existed).

resize() does the same allocation reserve() did, plus a zero-fill of the bytes. For the trivial T types in use here (uint8_t, idx_t, float) that's a single memset(0), dominated by the allocation cost. Segment construction is not on the hot path; inserts immediately overwrite the zero bytes via memcpy.

Backport

The same constructor exists on the v2.5.x and v2.6.x lines at the older path thirdparty/faiss/faiss/invlists/InvertedLists.h (moved to cppcontrib/knowhere/invlists/ in #1554). Same one-line change applies on both with offset adjustment.

Fixes: #1643

…dLists

  The Segment helper in ConcurrentArrayInvertedLists reserves data_'s
  capacity but never sizes it, so std::vector::size() stays 0 while
  operator[] reads and writes at offsets within capacity. That is
  undefined behavior; on a libstdc++ built with -D_GLIBCXX_ASSERTIONS,
  operator[] has a bounds check compiled in and the process aborts on
  the first growing-segment write (e.g. an IVF_FLAT_CC insert).

  Switch reserve() to resize() so size() == capacity() and operator[]
  is in-bounds. Same allocation, plus a zero-fill that the subsequent
  memcpy overwrites; no measurable runtime cost. Behavior on unhardened
  libstdc++ is unchanged.

Signed-off-by: Michael Paul <31833332+MickeyPvX@users.noreply.github.com>
@sre-ci-robot

Copy link
Copy Markdown
Collaborator

Welcome @MickeyPvX! It looks like this is your first PR to zilliztech/knowhere 🎉

@mergify

mergify Bot commented May 21, 2026

Copy link
Copy Markdown

@MickeyPvX 🔍 Important: PR Classification Needed!

For efficient project management and a seamless review process, it's essential to classify your PR correctly. Here's how:

  1. If you're fixing a bug, label it as kind/bug.
  2. For small tweaks (less than 20 lines without altering any functionality), please use kind/improvement.
  3. Significant changes that don't modify existing functionalities should be tagged as kind/enhancement.
  4. Adjusting APIs or changing functionality? Go with kind/feature.

For any PR outside the kind/improvement category, ensure you link to the associated issue using the format: “issue: #”.

Thanks for your efforts and contribution to the community!.

@alexanderguzhva

Copy link
Copy Markdown
Collaborator

/kind improvement
issue: #1643

@alexanderguzhva

Copy link
Copy Markdown
Collaborator

/lgtm
/approve

@sre-ci-robot

Copy link
Copy Markdown
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: alexanderguzhva, MickeyPvX

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sre-ci-robot sre-ci-robot merged commit 0edac9d into zilliztech:main May 26, 2026
13 checks passed
@MickeyPvX MickeyPvX deleted the fix-segment-data-resize branch May 27, 2026 18:00
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.

ConcurrentArrayInvertedLists::Segment operator[] out-of-bounds; aborts under _GLIBCXX_ASSERTIONS

3 participants