GHSA-8397-8QF6-F2GM
Vulnerability from github – Published: 2026-10-28 21:15 – Updated: 2026-10-28 21:15Summary
The Phi-4 multimodal audio encoder (phi4mm_audio.py / phi4mm_utils.py) runs its conformer attention in eager mode by default: the official HF config of microsoft/Phi-4-multimodal-instruct does not set use_pt_scaled_dot_product_attention, so the default False applies (phi4mm_audio.py:166, :876; phi4mm_utils.py:1669). The eager path fully materializes the attention probability matrix of shape [n_chunks, 16, 500, 500] per layer — and then stores it on the module: self.attn = attn (phi4mm_utils.py:1802, a torch.jit.Attribute declared at :1693), keeping a live reference for every one of the 24 encoder layers.
The only gates on audio length are the server-side limits VLLM_MAX_AUDIO_CLIP_FILESIZE_MB=25 and VLLM_MAX_AUDIO_DECODE_DURATION_S=600 (envs.py:83-84) — i.e. a 10-minute clip is a legal input by default. A 600 s clip becomes ~60,000 mel frames, which the encoder unfolds into 500-frame chunks (phi4mm_audio.py:996, max_seq_len = 500), giving n_chunks ≈ 120. Each layer therefore materializes a 120×16×500×500 matrix (~1–2 GiB depending on dtype), and the module attribute keeps all completed layers' matrices alive, so GPU memory climbs monotonically within a single request until an allocation fails.
Verified end-to-end on the official vllm/vllm-openai:v0.31.0 image: one 19.2 MB silent 600 s WAV → worker torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.29 GiB ... 501.75 MiB free ... 22.09 GiB allocated by PyTorch inside get_audio_features (phi4mm_audio.py:1279 → :1332 → phi4mm_utils.py:1386) → EngineCore encountered a fatal error → API EngineDeadError → whole server down; /health refuses connections. Total time from request to dead service: ~6 seconds.
A secondary effect: because self.attn persists on the module after the request ends, the matrices of the last audio request stay resident indefinitely (observed +50 MiB pinned after a single 5 s control request); they are only overwritten by the next audio request, so the resident footprint equals the largest audio processed since startup.
Details
1. Root cause. MultiHeadedAttention.forward_attention in eager mode computes attn = masked_softmax(scores, mask) and unconditionally executes self.attn = attn (phi4mm_utils.py:1795-1802). The attribute is a leftover inference/debug hook (used by torch.jit.Attribute(None, Tensor | None) at :1693); in serving it serves no purpose but pins the full [batch·chunks, heads, T, T] tensor per layer past both the layer's forward and the request's lifetime. The SDP attention branch (use_pt_scaled_dot_product_attention=True) never materializes this matrix — but the HF config does not enable it and the code default is False.
2. Why one request suffices. The conformer chunks long audio to max_seq_len = 500 frames (phi4mm_audio.py:996). Duration is bounded only by VLLM_MAX_AUDIO_DECODE_DURATION_S=600, so n_chunks reaches ~120 for a maximal clip. With 16 heads and fp32 scores, one layer's attention tensor is ~1.8 GiB (the observed failing allocation was 2.29 GiB, consistent with the scores+bias materialization). The encoder has num_blocks=24, attention_heads=16, attention_dim=1024, time_reduction=8 (HF config). Weights plus a handful of retained per-layer matrices exhaust the ~2.3 GiB headroom that --gpu-memory-utilization 0.9 leaves on a 24 GB card — mid-forward, on the first request.
4. Deployment reality. --trust-remote-code is the documented, standard way to serve this model (the HF model card instructs it), so the finding applies to default deployments. The crash is deterministic, not load-dependent: scheduler stats at death show a single running request and near-zero KV cache usage.
Impact
Any unauthenticated client that can reach the chat/completions API of a default deployment serving Phi-4-multimodal-instruct (or any model reusing this audio tower) can kill the entire engine with one request carrying a perfectly legal 10-minute audio clip (silent noise suffices — no crafted content needed). Full denial of service, requiring a process restart. On GPUs with more headroom (e.g. 80 GB at default utilization) a single request may survive, but the eager path still materializes GiB-scale per-layer matrices and retains the last request's tensors indefinitely, so a few sequential max-length requests — or normal traffic following one — achieve the same result.
Relation to prior art: the only public Phi-4-mm advisory we found, GHSA-vc6m-hm49-g9qg, concerns a CPU-side quadratic-cost path — different subsystem, different mechanism. GitHub search for "phi4mm audio OOM/memory" (31 results, 2026-10-08) shows no report of this attention-retention issue; main (2026-10-08) is unchanged.
Suggested remediation (any one breaks the chain; 1+2 recommended):
- Remove
self.attn = attnfrom the serving path (or gate it behind an explicit debug/output_attentionsflag) so attention matrices are freed when the layer returns. - Default
use_pt_scaled_dot_product_attentiontoTrue(flash/SDPA kernels never materialize the[B, H, T, T]matrix); keep eager only as an opt-in. - Additionally bound the encoder input (frames or duration) for this model so the eager path cannot reach GiB-scale single-layer allocations even if enabled.
This advisory was fixed in MR60614.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "vllm"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.32.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-115913"
],
"database_specific": {
"cwe_ids": [
"CWE-770"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-28T21:15:41Z",
"nvd_published_at": "2026-10-26T17:17:19Z",
"severity": "MODERATE"
},
"details": "### Summary\n\nThe Phi-4 multimodal audio encoder (`phi4mm_audio.py` / `phi4mm_utils.py`) runs its conformer attention in **eager mode by default**: the official HF config of `microsoft/Phi-4-multimodal-instruct` does not set `use_pt_scaled_dot_product_attention`, so the default `False` applies (`phi4mm_audio.py:166`, `:876`; `phi4mm_utils.py:1669`). The eager path fully materializes the attention probability matrix of shape `[n_chunks, 16, 500, 500]` per layer \u2014 and then **stores it on the module**: `self.attn = attn` (`phi4mm_utils.py:1802`, a `torch.jit.Attribute` declared at `:1693`), keeping a live reference for every one of the 24 encoder layers.\n\nThe only gates on audio length are the server-side limits `VLLM_MAX_AUDIO_CLIP_FILESIZE_MB=25` and `VLLM_MAX_AUDIO_DECODE_DURATION_S=600` (`envs.py:83-84`) \u2014 i.e. a 10-minute clip is a **legal input by default**. A 600 s clip becomes ~60,000 mel frames, which the encoder unfolds into 500-frame chunks (`phi4mm_audio.py:996`, `max_seq_len = 500`), giving `n_chunks \u2248 120`. Each layer therefore materializes a `120\u00d716\u00d7500\u00d7500` matrix (~1\u20132 GiB depending on dtype), and the module attribute keeps all completed layers\u0027 matrices alive, so GPU memory climbs monotonically **within a single request** until an allocation fails.\n\nVerified end-to-end on the official `vllm/vllm-openai:v0.31.0` image: one 19.2 MB silent 600 s WAV \u2192 worker `torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.29 GiB ... 501.75 MiB free ... 22.09 GiB allocated by PyTorch` inside `get_audio_features` (`phi4mm_audio.py:1279` \u2192 `:1332` \u2192 `phi4mm_utils.py:1386`) \u2192 `EngineCore encountered a fatal error` \u2192 API `EngineDeadError` \u2192 whole server down; `/health` refuses connections. Total time from request to dead service: **~6 seconds**.\n\nA secondary effect: because `self.attn` persists on the module after the request ends, the matrices of the last audio request stay resident indefinitely (observed +50 MiB pinned after a single 5 s control request); they are only overwritten by the next audio request, so the resident footprint equals the largest audio processed since startup.\n\n### Details\n\n**1. Root cause.** `MultiHeadedAttention.forward_attention` in eager mode computes `attn = masked_softmax(scores, mask)` and unconditionally executes `self.attn = attn` (`phi4mm_utils.py:1795-1802`). The attribute is a leftover inference/debug hook (used by `torch.jit.Attribute(None, Tensor | None)` at `:1693`); in serving it serves no purpose but pins the full `[batch\u00b7chunks, heads, T, T]` tensor per layer past both the layer\u0027s forward and the request\u0027s lifetime. The SDP attention branch (`use_pt_scaled_dot_product_attention=True`) never materializes this matrix \u2014 but the HF config does not enable it and the code default is `False`.\n\n**2. Why one request suffices.** The conformer chunks long audio to `max_seq_len = 500` frames (`phi4mm_audio.py:996`). Duration is bounded only by `VLLM_MAX_AUDIO_DECODE_DURATION_S=600`, so `n_chunks` reaches ~120 for a maximal clip. With 16 heads and fp32 scores, one layer\u0027s attention tensor is ~1.8 GiB (the observed failing allocation was 2.29 GiB, consistent with the scores+bias materialization). The encoder has `num_blocks=24`, `attention_heads=16`, `attention_dim=1024`, `time_reduction=8` (HF config). Weights plus a handful of retained per-layer matrices exhaust the ~2.3 GiB headroom that `--gpu-memory-utilization 0.9` leaves on a 24 GB card \u2014 mid-forward, on the first request.\n\n**4. Deployment reality.** `--trust-remote-code` is the documented, standard way to serve this model (the HF model card instructs it), so the finding applies to default deployments. The crash is deterministic, not load-dependent: scheduler stats at death show a single running request and near-zero KV cache usage.\n\n\n### Impact\n\nAny unauthenticated client that can reach the chat/completions API of a default deployment serving `Phi-4-multimodal-instruct` (or any model reusing this audio tower) can kill the entire engine with **one request** carrying a perfectly legal 10-minute audio clip (silent noise suffices \u2014 no crafted content needed). Full denial of service, requiring a process restart. On GPUs with more headroom (e.g. 80 GB at default utilization) a single request may survive, but the eager path still materializes GiB-scale per-layer matrices and retains the last request\u0027s tensors indefinitely, so a few sequential max-length requests \u2014 or normal traffic following one \u2014 achieve the same result.\n\nRelation to prior art: the only public Phi-4-mm advisory we found, GHSA-vc6m-hm49-g9qg, concerns a CPU-side quadratic-cost path \u2014 different subsystem, different mechanism. GitHub search for \"phi4mm audio OOM/memory\" (31 results, 2026-10-08) shows no report of this attention-retention issue; `main` (2026-10-08) is unchanged.\n\n**Suggested remediation** (any one breaks the chain; 1+2 recommended):\n\n1. Remove `self.attn = attn` from the serving path (or gate it behind an explicit debug/`output_attentions` flag) so attention matrices are freed when the layer returns.\n2. Default `use_pt_scaled_dot_product_attention` to `True` (flash/SDPA kernels never materialize the `[B, H, T, T]` matrix); keep eager only as an opt-in.\n3. Additionally bound the encoder input (frames or duration) for this model so the eager path cannot reach GiB-scale single-layer allocations even if enabled.\n\nThis advisory was fixed in [MR60614](https://github.com/vllm-project/vllm/pull/60614).",
"id": "GHSA-8397-8qf6-f2gm",
"modified": "2026-10-28T21:15:41Z",
"published": "2026-10-28T21:15:41Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/vllm-project/vllm/security/advisories/GHSA-8397-8qf6-f2gm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-115913"
},
{
"type": "WEB",
"url": "https://github.com/vllm-project/vllm/pull/60614"
},
{
"type": "WEB",
"url": "https://github.com/vllm-project/vllm/commit/3d52578e13454de0bb8352cac988347cdcc89928"
},
{
"type": "PACKAGE",
"url": "https://github.com/vllm-project/vllm"
},
{
"type": "WEB",
"url": "https://github.com/vllm-project/vllm/releases/tag/v0.32.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "vLLM: Phi-4 multimodal audio encoder materializes and retains full attention matrices: a single max-length audio OOM-crashes the vLLM engine"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.