# vLLM CVE-2026-114053: when one request chooses which model code your server runs

> vLLM CVE-2026-114053 let a request pick which revision of a model's processor code to import. Who is exposed, the fix in 0.31.0, and how to lock it down.

- Published: 2026-10-29
- Author: Enforster AI (https://enforster.ai)
- Category: AI Security
- Tags: AI Security, Supply Chain, LLM, Access Control, Security Operations
- Canonical URL: https://blog.enforster.ai/vllm-cve-2026-114053-code-revision-rce

## Key takeaways

- CVE-2026-114053 lets a client set mm_processor_kwargs.code_revision in a multimodal request so vLLM imports processor code from a revision the operator never reviewed, running arbitrary Python in the API process.
- The flaw affects vLLM 0.7.3 up to but not including 0.31.0, and only deployments that enable trust_remote_code for a model whose processor uses dynamic auto_map code.
- VulnCheck rates CVE-2026-114053 CVSS 4.0 9.2 and CVSS 3.1 8.1; the CVE record was published on October 21, 2026.
- vLLM's --api-key protects only the /v1, /v2, /inference, and /cohere path prefixes, so /tokenize, the main path to this bug, stays unauthenticated unless a proxy blocks it.
- vLLM 0.31.0 rejects per-request mm_processor_kwargs and media_io_kwargs by default; operators must opt back in with --trust-request-mm-kwargs.

---

CVE-2026-114053 is a remote code execution flaw in vLLM, the open-source LLM inference server. When `trust_remote_code` is enabled, vLLM 0.7.3 through 0.30.x passes a client-supplied `mm_processor_kwargs.code_revision` value to Transformers, so a request can choose which revision of a model's processor code gets imported and executed. Upgrade to vLLM 0.31.0, and if you run any model with `trust_remote_code`, treat this as urgent.

The bug is worth understanding beyond the patch. It shows that pinning a model's revision does not pin all of its code, and that a "harmless" utility endpoint on an inference server can reach the most dangerous code path it has. This article reflects the advisory and CVE record as of October 29, 2026.

## What is CVE-2026-114053?

The vLLM project's [GitHub advisory GHSA-h3rc-6mm3-gc2m](https://github.com/vllm-project/vllm/security/advisories/GHSA-h3rc-6mm3-gc2m), published on October 6, 2026, is titled "Request-controlled `mm_processor_kwargs.code_revision` allows RCE". It affects the `vllm` PyPI package from 0.7.3 up to, but not including, 0.31.0. VulnCheck, acting as the CNA, published [CVE-2026-114053](https://www.cve.org/CVERecord?id=CVE-2026-114053) on October 21, 2026 with a CVSS 4.0 score of 9.2 and a CVSS 3.1 score of 8.1. It is classed as CWE-94 (code injection) and CWE-829 (inclusion of functionality from an untrusted control sphere).

None of the sources reviewed for this article report exploitation in the wild. That can change, so check the CVE record and CISA's KEV catalog before you deprioritize it.

## How does the attack work?

Some multimodal models ship their own Python processor class, declared through `auto_map.AutoProcessor` in the model repository. Transformers imports that code only when the caller passes `trust_remote_code=True`. Transformers also accepts a separate `code_revision` argument, which picks the revision the dynamic code is loaded from, independently of the `revision` used for weights and configuration.

According to the advisory, the request path looked like this:

1. A client sends a multimodal request with `mm_processor_kwargs` in the body.
2. vLLM merges those request values into the processor construction arguments, and request values override startup values.
3. The kwargs filter removes only keys it recognizes as processor-call arguments, so `code_revision` survives.
4. vLLM pins `revision` and `trust_remote_code` from the operator's configuration, but never reserves `code_revision`.
5. `AutoProcessor.from_pretrained` imports the processor module from the request-selected revision. Configuration and data stay on the operator's pinned revision; only the code moves.

Importing a Python module runs its top-level code, so the attacker's processor executes inside the vLLM API process with its credentials, network access, and GPU host.

### Where does the attacker's revision come from?

The attacker needs a revision in the served model's repository that they control. The advisory names Hugging Face community pull request refs as one example. Hub pull requests [do not use forks](https://huggingface.co/docs/hub/repositories-pull-requests-discussions): contributors push commits to a ref such as `refs/pr/42` stored directly on the source repository. Those refs are not fetched by default when you clone, but they can be fetched on demand, which is exactly what a request-selected revision does.

So a model you reviewed and pinned can still carry unreviewed code in its PR refs, written by anyone allowed to open a pull request.

## Who is exposed?

The advisory lists five preconditions, all of which must hold:

- The served model's processor uses dynamic `auto_map.AutoProcessor` code.
- The operator started vLLM with `trust_remote_code` enabled. The default, disabled, is not affected.
- The server can fetch the request-selected revision, or already has it cached.
- The attacker can create or control a revision in the model repository.
- A multimodal request reaches the processor code path.

The second condition is the quick filter. If none of your vLLM deployments pass `--trust-remote-code`, this CVE does not apply to them, though you should still upgrade.

> **An API key does not cover /tokenize:**
>
> The advisory names `/tokenize` as the most relevant entry point. vLLM's [security guide](https://docs.vllm.ai/en/latest/usage/security.html) states that `--api-key` protects only the `/v1`, `/v2`, `/inference`, and `/cohere` path prefixes, and lists `/tokenize` among the endpoints that need no key. A server started with an API key was still reachable by unauthenticated clients through this path.

`/v1/chat/completions` also accepts `mm_processor_kwargs`, so any authenticated user of the chat API could reach the same code path. In multi-tenant deployments, "authenticated" often means every customer.

## How do I check and fix my deployments?

Start with an inventory of where vLLM runs and how it was started.

```bash
# Installed version in each environment or image
pip show vllm | grep -i '^version'

# Find deployments that enable remote code
grep -rnE -- '--trust-remote-code|trust_remote_code[[:space:]]*=[[:space:]]*True' \
  deploy/ k8s/ helm/ docker/ 2>/dev/null
```

Then work through these steps in order:

1. **Upgrade to vLLM 0.31.0 or later.** The [0.31.0 release notes](https://github.com/vllm-project/vllm/releases/tag/v0.31.0) state that per-request `mm_processor_kwargs` and `media_io_kwargs` are now rejected by default. The server returns an error instead of forwarding them.
2. **Disable `trust_remote_code` where you can.** If your Transformers and vLLM versions support the model's architecture natively, it does not need remote code.
3. **Pin code explicitly where you cannot.** vLLM documents separate `--revision`, `--code-revision`, and `--tokenizer-revision` flags in its [engine arguments](https://docs.vllm.ai/en/latest/configuration/engine_args.html). Set each to a reviewed commit hash rather than a branch name.
4. **Put a reverse proxy in front of the API server** that allowlists only the endpoints your clients need. vLLM's security guide recommends this, and it is the only reliable way to close `/tokenize`, `/metrics`, and other unauthenticated paths.
5. **Rebuild images.** A pinned `vllm` in a requirements file or base image stays vulnerable until the image is rebuilt and redeployed.

A minimal nginx allowlist looks like this:

```nginx
location = /v1/chat/completions { proxy_pass http://vllm:8000; }
location = /v1/completions      { proxy_pass http://vllm:8000; }
location = /v1/models           { proxy_pass http://vllm:8000; }
location /                      { return 404; }
```

> **Keep the opt-in flag off:**
>
> 0.31.0 lets trusted deployments restore per-request overrides with `--trust-request-mm-kwargs`. Treat that flag like `trust_remote_code`: leave it off unless every client of the server is trusted, and record why it is on if you enable it.

### Should I look for signs of exploitation?

If you ran an affected version with `trust_remote_code` enabled and `/tokenize` reachable, assume the API process could have been compromised. Useful checks:

- Search proxy or gateway logs that capture request bodies for `code_revision`. Legitimate clients have little reason to send it.
- Inspect the host's Hugging Face cache for downloaded code from revisions you did not pin.
- Review outbound connections from inference hosts and rotate any credentials the process could read, such as Hub tokens and cloud keys.

## What is the broader lesson for AI infrastructure?

This is the same mistake as letting a request choose a SQL table name: a value that should come only from operator configuration was accepted from the client. The advisory's own recommended fix says as much. Separate processor-construction arguments from request-time arguments with an allowlist, reject loader and source controls in requests with a 4xx error, and treat the operator's `code_revision` as the only permitted one.

Three rules carry over to any inference stack:

- **Anything that selects code is configuration, never input.** Revisions, repository IDs, adapter paths, and plugin names should be fixed at startup or checked against an allowlist.
- **A revision pin is only as strong as every loader that honours it.** Pin weights, code, and tokenizer separately, and test that a request cannot move any of them.
- **Inference servers are not edge services.** Their built-in auth was designed for trusted networks. Put an authenticating, allowlisting proxy in front of every one.

If you load third-party models, our guide to [loading model weights safely](https://blog.enforster.ai/loading-third-party-model-weights-safely) covers pickle, `weights_only`, and the review process for `trust_remote_code`. For another recent example of how much damage a compromised component in the LLM serving path can do, see the [LiteLLM 1.107.0 compromise](https://blog.enforster.ai/litellm-1-107-0-supply-chain-compromise).
