fix(TOOLS-STRUCTURED-CORE): a NUL byte in structured_outputs.regex silently truncates the pattern instead of being rejected
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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