Skip to content

Reject vocab_size < 2 for beam search to fix OOB in BeamSearch_Cpu::SelectTop - #2272

Merged
David Fan (jiafatom) merged 1 commit into
mainfrom
fix/beamsearch-vocab-size-selecttop
Jul 10, 2026
Merged

Reject vocab_size < 2 for beam search to fix OOB in BeamSearch_Cpu::SelectTop#2272
David Fan (jiafatom) merged 1 commit into
mainfrom
fix/beamsearch-vocab-size-selecttop

Conversation

@jiafatom

Copy link
Copy Markdown
Contributor

Summary

Fixes a heap out-of-bounds read/write (CWE-787 / CWE-125) reachable from a malicious genai_config.json.

BeamSearch_Cpu::SelectTop builds an index array of total_elements = num_beams * vocab_size entries and partial_sorts it to surface the top top_k = 2 * num_beams candidates:

const size_t top_k = 2 * params_->search.num_beams;
const size_t total_elements = num_beams * vocab_size;
assert(total_elements >= top_k);              // compiled out under NDEBUG
select_top_idx_.resize(total_elements);
std::partial_sort(begin, begin + top_k, end, ...);  // middle past end when top_k > total_elements

When vocab_size == 1 (and num_beams >= 2), total_elements = num_beams < top_k = 2*num_beams, so begin + top_k points past the end of select_top_idx_. std::partial_sort heapifies [begin, begin+top_k), reading and move-writing slots beyond the allocation. The only guard was an assert, which is compiled out in the shipped release builds (NDEBUG). The path is reached from the normal generation loop (GenerateNextToken -> SelectTop).

Fix

  • src/generators.cpp: reject beam search (num_beams > 1) with vocab_size < 2 in the Generator::Generator validator (primary fix, at creation). total_elements >= top_k reduces exactly to vocab_size >= 2.
  • src/search.cpp: replace the compiled-out assert(total_elements >= top_k) with a runtime check that throws, so release builds are protected as defense-in-depth.
  • test/sampling_tests.cpp: add BeamSearchVocabSizeTooSmallThrowsCpu, overlaying vocab_size = 1 with num_beams = 2 and asserting OgaGenerator::Create throws.

Note: the greedy path (GreedySearch_Cpu::SelectTop, num_beams == 1) uses std::max_element and is unaffected.

Testing

New regression test follows the existing SamplingTests overlay pattern. Local build isn't available in my environment (root-owned build dir / jiafa-dev container needed); relying on CI to build and run the C++ unit tests.

@jiafatom
David Fan (jiafatom) requested a review from a team as a code owner July 6, 2026 18:03
Copilot AI review requested due to automatic review settings July 6, 2026 18:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens beam search configuration validation to prevent an out-of-bounds access in BeamSearch_Cpu::SelectTop when a malicious (or invalid) genai_config.json sets vocab_size too small for the top_k = 2 * num_beams selection logic.

Changes:

  • Add generator-creation validation to reject num_beams > 1 when vocab_size < 2.
  • Add a runtime invariant check in BeamSearch_Cpu::SelectTop (replacing a release-stripped assert) to prevent std::partial_sort from operating with an invalid middle iterator.
  • Add a regression test asserting OgaGenerator::Create throws for num_beams = 2 with vocab_size = 1.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/generators.cpp Adds upfront validation to reject beam search configs with vocab_size < 2.
src/search.cpp Adds defense-in-depth runtime check guarding partial_sort against top_k > total_elements.
test/sampling_tests.cpp Adds regression coverage for the invalid vocab_size + beam search combination.

BeamSearch_Cpu::SelectTop partial_sorts an index array of num_beams*vocab_size
entries and asks for the top 2*num_beams (top_k). When vocab_size == 1 (and
num_beams >= 2), total_elements < top_k, so the partial_sort middle iterator
points past the end of select_top_idx_, causing an out-of-bounds read/write
(heap-buffer-overflow, CWE-787/CWE-125). The only guard was an assert() that is
compiled out under NDEBUG in release builds.

Fix:
- Reject beam search (num_beams > 1) with vocab_size < 2 in the
  Generator::Generator validator.
- Replace the compiled-out assert in SelectTop with a runtime check that throws,
  so release builds are protected as defense-in-depth.
- Add a CPU regression test overlaying vocab_size = 1 with num_beams = 2.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jiafatom
David Fan (jiafatom) force-pushed the fix/beamsearch-vocab-size-selecttop branch from caa9057 to 78acea6 Compare July 7, 2026 23:06
@jiafatom
David Fan (jiafatom) enabled auto-merge (squash) July 10, 2026 02:52
@jiafatom
David Fan (jiafatom) merged commit 7480c21 into main Jul 10, 2026
63 of 68 checks passed
@jiafatom
David Fan (jiafatom) deleted the fix/beamsearch-vocab-size-selecttop branch July 10, 2026 06:27
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