Skip to content

Commit 269ac43

Browse files
Michael Norrismeta-codesync[bot]
authored andcommitted
Bound recursion depth in index deserialization (CWE-674) (#5468)
Summary: Pull Request resolved: #5468 Agentic fuzzing (T278233426) found an uncontrolled-recursion stack overflow in `faiss::read_index_binary_up`. The serialized index format is self-describing and recursively nestable: wrapper types (`IBMp`/`IBM2` IndexBinaryIDMap, `IBHf`/`IBHc` HNSW storage, `IBFf` IndexBinaryFromFloat) each recurse back into the reader to deserialize their inner index, with no depth bound. A stream of a few thousand nested wrapper markers — each only a 4-byte fourcc plus a small header — drives recursion until the thread stack is exhausted, killing the process (SIGSEGV on the guard page). The float reader `read_index_up` has the identical structure (`IxMp`, `IxPT`, `IxRF`, HNSW storage, ...). Add a shared, thread-local nesting-depth guard (RAII) at the entry of both `read_index_up` and `read_index_binary_up`, capped at 50 levels. The check runs before incrementing so a thrown limit leaves the counter consistent; the destructor decrements on unwind. Legitimate indexes nest only a handful of levels, so the limit is generous while turning the stack-exhaustion crash into a catchable `FaissException`. Reviewed By: alibeklfc Differential Revision: D113119945 fbshipit-source-id: 7c4015131ca6c0bc7de4a3b8ba764ecff0119dae
1 parent 3e313e4 commit 269ac43

1 file changed

Lines changed: 23 additions & 0 deletions

File tree

faiss/impl/index_read.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,7 +1608,29 @@ static std::unique_ptr<IndexIVFPQ> read_ivfpq(
16081608

16091609
int read_old_fmt_hack = 0;
16101610

1611+
namespace {
1612+
1613+
constexpr int kMaxIndexNestingDepth = 50;
1614+
thread_local int index_read_nesting_depth = 0;
1615+
1616+
struct IndexNestingGuard {
1617+
IndexNestingGuard() {
1618+
FAISS_THROW_IF_NOT_FMT(
1619+
index_read_nesting_depth < kMaxIndexNestingDepth,
1620+
"faiss index nesting depth exceeds limit of %d; "
1621+
"input may be corrupt or malicious",
1622+
kMaxIndexNestingDepth);
1623+
++index_read_nesting_depth;
1624+
}
1625+
~IndexNestingGuard() {
1626+
--index_read_nesting_depth;
1627+
}
1628+
};
1629+
1630+
} // namespace
1631+
16111632
std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
1633+
IndexNestingGuard nesting_guard;
16121634
std::unique_ptr<Index> idx;
16131635
uint32_t h;
16141636
READ1(h);
@@ -3275,6 +3297,7 @@ static void read_binary_multi_hash_map(
32753297
}
32763298

32773299
std::unique_ptr<IndexBinary> read_index_binary_up(IOReader* f, int io_flags) {
3300+
IndexNestingGuard nesting_guard;
32783301
std::unique_ptr<IndexBinary> idx;
32793302
uint32_t h;
32803303
READ1(h);

0 commit comments

Comments
 (0)