mudler / mudler/vllm.cpp

fix(TOOLS-STRUCTURED-CORE): a NUL byte in structured_outputs.regex silently truncates the pattern instead of being rejected

Open Beginner friendly
#2,701 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
423
Forks
53
Avg merge
20h 26m
Merged PRs (30d)
310

Description

Row: TOOLS-STRUCTURED-CORE (.agents/engine-matrix.md:160)

Found by wave PORTQ-5 (#2679) re-deriving PORT-NOW entry [163], upstream 38f097fab8 vllm#51796. Nothing was executed.

What upstream did

Two NUL-byte rejections for structured_outputs.regex: one in SamplingParams.__post_init__ raising VLLMValidationError before backend selection (so "auto" mode gives a clean 400 rather than a silent fallback), and one inside validate_xgrammar_grammar before the pattern reaches xgrammar's native regex converter.

Against this tree

Half A is a real gap. StructuredOutputsParams::Verify() (src/vllm/sampling_params.cpp:196-213) only counts constraint fields — it never inspects regex's contents. include/vllm/sampling_params.h:105 is a plain std::optional<std::string> regex;.

Half B is surface-absent. No validate_xgrammar_grammar analogue exists anywhere; src/vllm/v1/structured_output/backend_xgrammar.cpp is 110 lines with no validation entry point at all. That validator does exist upstream at the pin (git show 5559679229:vllm/v1/structured_output/backend_xgrammar.py:272), so its absence here is a pre-pin hole and not this commit's to own.

Why it is worth fixing even though nothing can currently deliver an embedded NUL

State this plainly, because it weakens the case: the only shipped carrier of the field is the C ABI (include/vllm.h:744, const char* structured_regex;), converted at src/capi/vllm_c.cpp:249 with std::string(c.structured_regex) — NUL-terminated, so an embedded NUL cannot arrive that way. The OpenAI HTTP path never populates regex at all. Today this is a defensive mirror with no reachable trigger through a production entry point.

It is still a live defect one layer down. The native regex lowering reads the pattern with

char Peek() const { return pos_ < s_.size() ? s_[pos_] : '\0'; }

(src/vllm/v1/structured_output/backend_native.cpp:149), so an embedded NUL is indistinguishable from end-of-input and would silently truncate the pattern rather than error — a wrong grammar accepted, not a refusal.

Size

~6 lines in StructuredOutputsParams::Verify() plus one test. If a validator layer is ever ported (half B), the same check belongs there too.

Not established

Whether any in-tree C++ caller sets regex from untrusted bytes. That would turn this from defensive to live.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/vllm/sampling_params.cpp, inspecting StructuredOutputsParams::Verify() alongside the regex field in include/vllm/sampling_params.h. Read the native pattern handling in src/vllm/v1/structured_output/backend_native.cpp, then add the focused test mentioned by the issue. Done means embedded NUL bytes are rejected rather than treated as end-of-input.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.