[Bugfix] Reject NUL byte in structured_outputs.regex (#51796)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Junhao Shen
2026-08-14 00:08:36 +00:00
committed by GitHub
co-authored by Claude Opus 4.8
parent f80b66f548
commit 38f097fab8
3 changed files with 50 additions and 0 deletions
@@ -70,3 +70,36 @@ def test_degenerate_structured_outputs_rejected(structured_outputs, match):
StructuredOutputsConfig(),
tokenizer=object(),
)
@pytest.mark.parametrize(
"regex",
[
"\x00", # a lone leading NUL
"\x00\x01\x02\x1f", # a NUL followed by other control chars
"[0-9]\x00", # an embedded NUL
],
)
def test_regex_with_nul_byte_rejected(regex):
"""A NUL byte is never meaningful in a structured-outputs regex and is not
handled by xgrammar's native regex converter. It must be rejected at request
validation in every backend mode (a clean 400), instead of reaching that
native code or silently falling back to another backend in the default
'auto' mode."""
params = SamplingParams(structured_outputs=StructuredOutputsParams(regex=regex))
# Rejected before backend selection, so it is a 400 even in 'auto' mode
# (which would otherwise catch the error and fall back to another backend).
with pytest.raises(VLLMValidationError, match="NUL"):
params._validate_structured_outputs(
_StubModelConfig(is_diffusion=False),
StructuredOutputsConfig(),
tokenizer=object(),
)
# The xgrammar backend also rejects it directly (defense in depth), before
# the pattern reaches the native from_regex call.
from vllm.v1.structured_output.backend_xgrammar import validate_xgrammar_grammar
with pytest.raises(ValueError, match="NUL"):
validate_xgrammar_grammar(params)
+9
View File
@@ -1029,6 +1029,15 @@ class SamplingParams(
"structured_outputs.json_object must be True if set; omit "
"structured_outputs to disable structured outputs"
)
# Reject a regex containing a NUL byte early, in every backend mode. A
# NUL is never meaningful in a regex pattern and is not handled by the
# regex-to-grammar conversion. Checked here, before backend selection,
# so it is a clean 400 rather than a silent fallback in the default
# "auto" mode.
if self.structured_outputs.regex and "\x00" in self.structured_outputs.regex:
raise VLLMValidationError(
"structured_outputs.regex must not contain a NUL character ('\\x00')"
)
from vllm.v1.structured_output.backend_guidance import (
has_guidance_unsupported_json_features,
@@ -284,6 +284,14 @@ def validate_xgrammar_grammar(sampling_params: SamplingParams) -> None:
so_params = sampling_params.structured_outputs
if so_params.regex:
# A NUL byte is never meaningful in a regex pattern and is not handled
# by xgrammar's native regex converter. Reject it here, before the
# pattern reaches that native code; the try/except below does not cover
# this case.
if "\x00" in so_params.regex:
raise ValueError(
"structured_outputs.regex must not contain a NUL character ('\\x00')"
)
try:
compile_regex_with_timeout(
xgr.Grammar.from_regex,