vllm: Request-controlled mm_processor_kwargs.code_revision allows remote code execution
What fixes it
No source in this snapshot names a fixed version.
Description
Summary
An untrusted multimodal request can set mm_processor_kwargs.code_revision. When vLLM serves a model whose Hugging Face processor uses dynamic auto_map code and the operator enabled --trust-remote-code, this request value reaches AutoProcessor.from_pretrained and selects the Python revision imported by Transformers.
This lets a client replace the operator-reviewed processor module with code from another revision of the same model repository. The operator-selected model/data revision remains pinned; only the executed processor code changes.
Importing a Python module is arbitrary code execution in the vLLM API/renderer process. The PoC below demonstrates this with an import-time filesystem write. It is fully offline and does not require a GPU, model weights, or changes to a real Hub repository.
Affected snapshot
- Repository: [
vllm-project/vllm](https://github.com/vllm-project/vllm) - Verified commit: [
570735520942b6428ae98f1eb442c6d88adae55e](https://github.com/vllm-project/vllm/commit/570735520942b6428ae98f1eb442c6d88adae55e) - Verified with Transformers 5.15.0 and 5.16.1
- Affected release range has not been bisected
Affected configuration
The vulnerable path exists when all of the following are true:
- The served model's processor uses dynamic
auto_map.AutoProcessorcode. - The operator enabled
trust_remote_code. - The server can fetch the request-selected revision, or it is already cached.
- The attacker can create or otherwise control a revision in the served model repository. An example is a Hugging Face community pull-request ref when that repository permits them.
- A multimodal request reaches the processor path.
The default trust_remote_code=False configuration is not affected.
Attack surface
mm_processor_kwargs is accepted directly by public request models:
- [
TokenizeChatRequest.mm_processor_kwargs](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/entrypoints/serve/tokenize/protocol.py#L111-L114) - [
ChatCompletionRequest.mm_processor_kwargs](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/entrypoints/openai/chat_completion/protocol.py#L372-L375)
/tokenize is particularly relevant because vLLM's authentication middleware guards only /v1, /v2, /inference, and /cohere prefixes:
- [
AuthenticationMiddleware](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/entrypoints/serve/middleware/authenticate.py#L11-L62)
Consequently, /tokenize remains unauthenticated even when --api-key is configured, unless an external proxy protects it. The vLLM Security Guide documents /tokenize as an unprotected endpoint.
Root cause
The request value follows this path:
- [
OnlineRenderer.preprocess_chat](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/renderers/online_renderer.py#L406-L435) places requestmm_processor_kwargsin prompt extras. - Multimodal rendering forwards those kwargs into processor construction through [
MMProcessorInputs](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/renderers/base.py#L751-L760) and [_process_multimodal_async](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/renderers/base.py#L831-L847). - [
MultiModalConfig.merge_mm_processor_kwargs](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/config/multimodal.py#L506-L518) overlays inference values on startup values, so the request wins. - [
cached_get_processor_without_dynamic_kwargs](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/transformers_utils/processor.py#L344-L375) filters only recognized processor-call keys.code_revisionis retained as a processor-construction kwarg. - [
cached_processor_from_config](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/transformers_utils/processor.py#L380-L390) pinsrevisionandtrust_remote_code, but forwards the remaining kwargs without reservingcode_revision. - [
get_processor](https://github.com/vllm-project/vllm/blob/570735520942b6428ae98f1eb442c6d88adae55e/vllm/transformers_utils/processor.py#L205-L245) callsAutoProcessor.from_pretrained(..., revision=revision, trust_remote_code=trust_remote_code, **kwargs). - Transformers [
AutoProcessor.from_pretrained](https://github.com/huggingface/transformers/blob/v5.15.0/src/transformers/models/auto/processing_auto.py#L294-L302) usescode_revisionto retrieve and import the dynamic module through [get_class_from_dynamic_module](https://github.com/huggingface/transformers/blob/v5.15.0/src/transformers/dynamic_module_utils.py#L474-L575), then removes it before loading processor data. This is why code comes from the attacker revision while configuration/data remain operator-pinned.
Request-time processor arguments are not separated from processor-construction arguments, and code_revision is not reserved.
Security impact
- Confidentiality: High
- Integrity: High
- Availability: High
- Execution context: vLLM API/renderer process
Suggested CVSS v3.1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 Critical
This Base score assumes the affected configuration described above. The CVSS v3.1 specification scores a vulnerability in that configuration; deployment-specific authentication or network controls belong in Environmental scoring. In an affected deployment, an unauthenticated network client can select and execute arbitrary processor Python inside the vLLM API/renderer process, compromising confidentiality, integrity, and availability.
Suggested remediation
- Separate processor-construction arguments from untrusted processor-call arguments. An allowlist of supported request-time preprocessing controls is safest.
- Reject
code_revisionand other loader/source controls in requestmm_processor_kwargs, and passmodel_config.code_revisionexplicitly as the only permitted processor-code revision. - Add a regression test proving a request cannot change the loaded module revision while operator pins remain effective.
- Protect
/tokenizewith the configured authentication policy as defense in depth.
Expected safe behavior is either a 4xx response for reserved request keys or silently ignoring them before processor construction. Rejection is preferable because it makes attempted misuse visible.
A patch for this was merged here: https://github.com/vllm-project/vllm/pull/58830
Text from GitHub Advisory Database
Affected versions 1
- vllm, pip>= 0.7.3, < 0.31.0GitHub