Skip to content

Harden OnDiskInvertedLists mmap reader against malformed list sizes#5312

Closed
AgentGymLeader wants to merge 3 commits into
facebookresearch:mainfrom
AgentGymLeader:harden-ondisk-invlists-bounds
Closed

Harden OnDiskInvertedLists mmap reader against malformed list sizes#5312
AgentGymLeader wants to merge 3 commits into
facebookresearch:mainfrom
AgentGymLeader:harden-ondisk-invlists-bounds

Conversation

@AgentGymLeader

Copy link
Copy Markdown
Contributor

What

OnDiskInvertedListsIOHook::read_ArrayInvertedLists() builds each inverted list's offset by accumulating size * (sizeof(idx_t) + code_size) over the deserialized sizes vector. The per-list size values come straight from the serialized index and were not bounds-checked: the multiply could overflow size_t, and the running offset was never re-validated against the mapped file size inside the loop. The only existing guard (FAISS_THROW_IF_NOT(o <= totsize) just above the loop) runs once and validates only the starting offset.

As a result, a malformed index loaded via read_index(path, IO_FLAG_MMAP) could leave one or more lists[i].offset pointing past the end of the mmap'd region. get_codes() / get_ids() only guard against INVALID_OFFSET, so at search time the IVF scan reads from an out-of-range pointer — an out-of-bounds read.

Fix

Inside the offset-accumulation loop, this change:

  • uses mul_no_overflow for size * (sizeof(idx_t) + code_size),
  • uses add_no_overflow when advancing the running offset, and
  • re-validates o <= totsize on every iteration with a descriptive message.

This mirrors the deserialization hardening already present for the Array and Block inverted-list readers in faiss/impl/index_read.cpp (which use mul_no_overflow + per-list byte-limit checks), bringing the OnDisk mmap reader to parity. Well-formed indexes are unaffected — the guards only reject inputs whose declared list sizes do not fit the mapped file.

Verification

  • The changed translation unit passes clang++ -std=c++17 -fsyntax-only cleanly. The three helpers (mul_no_overflow, add_no_overflow, FAISS_THROW_IF_NOT_FMT) are already in scope via faiss/impl/FaissAssert.h and used throughout this file.
  • No behavioral change for valid indexes; the new checks only fire on malformed inputs.

Reported via coordinated disclosure (huntr).

Bring the OnDisk reader deserialization loop in read_ArrayInvertedLists()
to parity with the Array/Block reader guards already present in the codebase.

Previously the per-list accumulation used an unchecked multiply-then-add:

    o += l.size * (sizeof(idx_t) + ails->code_size);

This is vulnerable to two classes of malformed input:
1. Integer overflow in the multiplication (a large l.size wraps to a
   small byte count, causing subsequent mmap reads out of bounds).
2. Integer overflow in the addition (a crafted list sequence accumulates
   o past the mapped file size without triggering the single pre-loop guard).

The replacement uses mul_no_overflow() and add_no_overflow() (both declared
in faiss/impl/FaissAssert.h and already in scope via existing FAISS_THROW_*
usage in this TU), and adds a per-iteration FAISS_THROW_IF_NOT_FMT guard
validating the running offset against ails->totsize after each list, with
diagnostic output of list index, offset, byte count, and mapped file size.

The pre-loop guard FAISS_THROW_IF_NOT(o <= ails->totsize) is preserved;
the new per-iteration check provides finer-grained diagnostics alongside it.
@meta-cla

meta-cla Bot commented Jun 14, 2026

Copy link
Copy Markdown

Hi @AgentGymLeader!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

@meta-cla meta-cla Bot added the CLA Signed label Jun 14, 2026
@meta-codesync

meta-codesync Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

@alibeklfc has imported this pull request. If you are a Meta employee, you can view this in D108627881.

The mul_no_overflow() call exceeded the 80-column limit on one line;
clang-format-21 (the CI's version) splits it to one argument per line
under .clang-format BinPackArguments: false.
@meta-codesync meta-codesync Bot closed this in c428ca1 Jun 16, 2026
@meta-codesync meta-codesync Bot added the Merged label Jun 16, 2026
@meta-codesync

meta-codesync Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

@alibeklfc merged this pull request in c428ca1.

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.

1 participant