vLLM Will Import Your Attacker’s Processor If You Ask Nicely (CVE-2026-114053)
you told the model to trust remote code. so it did. then a multimodal request whispered mm_processor_kwargs.code_revision, and the unauthenticated /tokenize endpoint fetched a different Python revision of the same Hugging Face repo and imported it. operator-pinned weights stayed put. the code that runs did not. CVE-2026-114053. GHSA-h3rc-6mm3-gc2m. fixed in vLLM 0.31.0. the joke writes itself, but we will anyway.
code_revision probes at random inference endpoints. authorized lab and detection engineering only.what landed
- CVE-2026-114053 / GHSA-h3rc-6mm3-gc2m: request-controlled
mm_processor_kwargs.code_revisionreachesAutoProcessor.from_pretrained. - Affected: vLLM
>= 0.7.3and< 0.31.0. - Patched:
>= 0.31.0(PR #58830). - CVSS: CVE.org lists 9.2 (CVSS 4.0). Advisory suggests 9.8 in the affected config.
- CWE: CWE-94 (code injection), CWE-829 (untrusted control sphere).
the prerequisites (all of them)
this is not “every vLLM on the internet is owned.” the advisory is specific. you need the whole set:
- served model processor uses dynamic
auto_map.AutoProcessorcode. - operator enabled
trust_remote_code(the default isFalseand is safe). - server can fetch the request-selected revision, or it is already cached.
- attacker can create or control a revision in the same model repository (Hugging Face community PR refs are the textbook example).
- a multimodal request reaches the processor path.
miss any one and the chain breaks. hit all five and the API process runs their Python.

step 1: the request surface
mm_processor_kwargs is accepted on TokenizeChatRequest and ChatCompletionRequest. /tokenize matters because vLLM’s AuthenticationMiddleware only guards /v1, /v2, /inference, and /cohere. so even with --api-key set, /tokenize stays open unless something in front of it cares. the vLLM Security Guide already documents that. documenting a hole is not the same as closing it.
{
"messages": [{"role": "user", "content": [
{"type": "text", "text": "describe"},
{"type": "image_url", "image_url": {"url": "data:image/png;base64,AA=="}}
]}],
"mm_processor_kwargs": {"code_revision": "refs/pr/9"}
}

step 2: why the loader betrays you
the request value walks a polite little hallway:
OnlineRenderer.preprocess_chatstuffs requestmm_processor_kwargsinto prompt extras.- multimodal rendering forwards them into processor construction.
MultiModalConfig.merge_mm_processor_kwargsoverlays inference values on startup values. request wins.cached_get_processor_without_dynamic_kwargsfilters recognized call keys.code_revisionis a construction key, so it survives.cached_processor_from_configpinsrevisionandtrust_remote_code, then forwards the rest.get_processorcallsAutoProcessor.from_pretrained(..., revision=revision, trust_remote_code=..., **kwargs).- Transformers uses
code_revisionforget_class_from_dynamic_module, then drops it before loading processor data.
result: configuration and weights stay on the operator pin. the Python that executes comes from the attacker revision. importing a module is arbitrary code execution in the API/renderer process. the advisory PoC proves it with an import-time filesystem write. ours does the same offline.

step 3: impact in one breath
confidentiality, integrity, availability: all high. execution context is the vLLM API process, which usually sees prompts, keys, GPU hosts, and whatever else you glued to the inference box. “but we only let employees hit it” is cute until someone finds the tokenize URL on a shared cluster with trust_remote_code still on from last quarter’s demo.
why defenders care
- unauthenticated by default on
/tokenize: API key theater does not cover it. - trust_remote_code is a footgun: operators enable it for convenience, then forget the request path can pick a different revision.
- same-repo PR refs: you do not need to hijack the whole model. a community PR on a permissive repo is enough.
- data pin is not a code pin: pinning
revisionfor weights does not pin the processor module whencode_revisionis attacker-set.
controls that are not theater
- upgrade to vLLM
>= 0.31.0. the patch reserves/rejects requestcode_revision. - leave
trust_remote_code=Falseunless you have a written reason and a review process for every dynamic module. - put auth in front of
/tokenize(and every other non-/v1surface). do not trust the built-in middleware map alone. - pin both data
revisionand processorcode_revisionat server start. never accept loader keys from clients. - network: inference hosts are crown jewels. do not put them on the same VLAN as the snack machine.
- detect: alert on unexpected
code_revision/ Hub fetches from the vLLM process; alert on the API process writing outside its model cache.
sloth take
you asked the framework to trust remote code. a client asked it which remote code. it said yes to both. patch to 0.31.0, kill trust_remote_code unless you enjoy living like that, and stop leaving /tokenize on the porch with a bowl of candy.
sources
- GitHub Advisory GHSA-h3rc-6mm3-gc2m
- CVE-2026-114053
- vLLM Security Guide (unprotected
/tokenizenote) - Patch: vLLM PR #58830 (0.31.0)
