Add invlists null check to IndexBinaryIVF::search (#4980)

Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4980

IndexBinaryIVF::search dereferences invlists via
invlists->prefetch_lists() without any prior null check. The invlists
pointer can legitimately be nullptr through multiple paths:

1. Default construction: IndexBinaryIVF::invlists is initialized to
   nullptr (IndexBinaryIVF.h:35) and the default constructor is
   defaulted, leaving it null.

2. Deserialization with IO_FLAG_SKIP_IVF_DATA: When this flag is set,
   read_InvertedLists_up returns nullptr, and the binary IVF reader
   at index_read.cpp:2130 stores it directly (ils.release() on a null
   unique_ptr yields nullptr). This is intentional — the flag allows
   loading index metadata without the (potentially large) inverted
   list data.

3. Deserialization with fourcc "il00": The serialized data explicitly
   indicates inverted lists were not stored with the IVF object
   (index_read.cpp:453), returning nullptr.

Without this check, any of these cases would segfault on the
invlists->prefetch_lists() call. With this check, the caller receives
a clear FaissException explaining the misconfiguration.

This mirrors the existing null check in the float IndexIVF::search
(IndexIVF.cpp:312) which already guards against the same condition.

Reviewed By: mnorris11

Differential Revision: D97848088

fbshipit-source-id: 406abb870dfa093ffc3ce49ba65904bebe4763a2
This commit is contained in:
Justin Gibbs
2026-03-26 12:17:04 -07:00
committed by meta-codesync[bot]
parent ba1981ca4c
commit b9f49601d1
2 changed files with 32 additions and 0 deletions
+1
View File
@@ -124,6 +124,7 @@ void IndexBinaryIVF::search(
idx_t* labels,
const SearchParameters* params_in) const {
FAISS_THROW_IF_NOT(k > 0);
FAISS_THROW_IF_NOT_MSG(invlists, "IVF index has no inverted lists");
const IVFSearchParameters* params = nullptr;
if (params_in) {
params = dynamic_cast<const IVFSearchParameters*>(params_in);
+31
View File
@@ -15,6 +15,7 @@
#include <faiss/Index.h>
#include <faiss/IndexBinary.h>
#include <faiss/IndexBinaryHNSW.h>
#include <faiss/IndexBinaryIVF.h>
#include <faiss/IndexFlat.h>
#include <faiss/IndexHNSW.h>
#include <faiss/IndexIVFFlat.h>
@@ -1924,3 +1925,33 @@ TEST(ReadIndexDeserialize, BinaryHNSWCagraZeroEntrypoints) {
},
faiss::FaissException);
}
// ---- IndexBinaryIVF runtime safety checks ----
TEST(ReadIndexDeserialize, BinaryIVFNullInvlistsSearch) {
IndexBinaryIVF idx;
idx.d = 16;
idx.code_size = 2;
idx.invlists = nullptr;
idx.own_invlists = false;
std::vector<int32_t> distances(1);
std::vector<idx_t> labels(1);
std::vector<uint8_t> query(2);
EXPECT_THROW(
{
try {
idx.search(
1,
query.data(),
1,
distances.data(),
labels.data());
} catch (const faiss::FaissException& e) {
EXPECT_NE(
std::string(e.what()).find("inverted lists"),
std::string::npos);
throw;
}
},
faiss::FaissException);
}