{"id":"PYSEC-2026-3406","summary":"vLLM: image EXIF Rotation & PNG tRNS Transparency Not Normalized, Causing Mismatch Between Model Input and Expectations","details":"## Summary\n\nIssue 1: EXIF orientation not normalized → The image orientation processed by the model differs from how humans view it, introducing interpretation bias.\n\nIssue 2: PNG tRNS not explicitly flattened before converting to RGB → After conversion, transparent/semi-transparent pixels are rendered unexpectedly, making otherwise subtle overlay elements visible and distorting the input content. (This attack is similar to AlphaDog: RGBA handling is already correct in vLLM, but since tRNS permits RGB images, the correct processing path isn’t taken.)\n\nIssue 3 : Pillow only loads the first frame when loading APNG or GIF files.\n\n---\n\n## Root Cause\n\n* **Rotation**: After opening an image, `ImageOps.exif_transpose` is not called to normalize EXIF orientation.\n* **Transparency**: Only **RGBA→RGB** is flattened with a background; PNGs carrying **`tRNS`** in **`P`/`L`/`RGB + tRNS`** and other non-RGBA modes take the `image.convert(\"RGB\")` path, which implicitly discards/remaps transparency semantics.\n\n---\n\n## Affected Code\n\n\nhttps://github.com/vllm-project/vllm/blob/16b37f3119918c1e5a39f303e0d0892c65c07a90/vllm/multimodal/image.py#L77-L84\n\nhttps://github.com/vllm-project/vllm/blob/16b37f3119918c1e5a39f303e0d0892c65c07a90/vllm/multimodal/image.py#L37-L43\n\nhttps://github.com/vllm-project/vllm/blob/16b37f3119918c1e5a39f303e0d0892c65c07a90/vllm/multimodal/image.py#L26-L34\n\u003e Current state: `ImageOps.exif_transpose` is not used. (Although the `rescale_image_size` function ([https://github.com/vllm-project/vllm/blob/main/vllm/multimodal/image.py#L14](https://github.com/vllm-project/vllm/blob/main/vllm/multimodal/image.py#L14)) exists and includes a `transpose` parameter, I’ve found that it doesn’t seem to be called anywhere outside the `test` directory.）\n\n\u003e **Call order**: `_convert_image_mode` runs first; if the conditions are met, `convert_image_mode` is called.\n\u003e \n\u003e **Issue**: Only the “RGBA → RGB” path is explicitly flattened. `P`, `L`, or `RGB` with `tRNS` all fall back to `image.convert(\"RGB\")`. For PNGs that include `tRNS`, `convert(\"RGB\")` directly produces 24-bit RGB, leading to:\n\u003e \n\u003e * **`P` mode**: The transparent index becomes an actual RGB color (often black, white, or an undefined background), so transparency is lost.\n\u003e * **`L/LA` and `RGB + tRNS`**: `convert(\"RGB\")` doesn’t composite against a chosen background first, so elements that relied on transparency to be hidden or softened become solid.\n\n\n## Impact & Scope\n\n* **Impact**: Pixels the model sees can diverge from operator expectations (due to orientation or transparency handling), potentially altering downstream reasoning.\n* **Scope**: The image I/O and mode-conversion paths in `vllm/multimodal/image.py`. The existing **RGBA→RGB** flattening is correct; the issues center on **missing EXIF normalization** and **non-RGBA `tRNS` not being explicitly composited**.\n\n## Case\nEXIF： http://qiniu.funxingzuo.top/exif_orient_180.jpg\ntRNS:  http://qiniu.funxingzuo.top/hello.png\n\n## Fix\n\nA fix for this vulnerability was merged here: https://github.com/vllm-project/vllm/pull/44974","aliases":["CVE-2026-12491","GHSA-8jr5-v98p-w75m"],"modified":"2026-07-13T16:33:32.323602606Z","published":"2026-07-13T15:46:18.584877Z","references":[{"type":"WEB","url":"https://github.com/vllm-project/vllm/security/advisories/GHSA-8jr5-v98p-w75m"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-12491"},{"type":"WEB","url":"https://github.com/vllm-project/vllm/pull/44974"},{"type":"WEB","url":"https://github.com/vllm-project/vllm/commit/cf1c90672404548aa3bc51f92c4745576a65ee26"},{"type":"WEB","url":"https://access.redhat.com/security/cve/CVE-2026-12491"},{"type":"WEB","url":"https://bugzilla.redhat.com/show_bug.cgi?id=2489786"},{"type":"PACKAGE","url":"https://github.com/vllm-project/vllm"},{"type":"PACKAGE","url":"https://pypi.org/project/vllm"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-8jr5-v98p-w75m"}],"affected":[{"package":{"name":"vllm","ecosystem":"PyPI","purl":"pkg:pypi/vllm"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0.11.0"},{"last_affected":"0.23.0"}]}],"versions":["0.11.0","0.11.1","0.11.2","0.12.0","0.13.0","0.14.0","0.14.1","0.15.0","0.15.1","0.16.0","0.17.0","0.17.1","0.18.0","0.18.1","0.19.0","0.19.1","0.20.0","0.20.1","0.20.2","0.21.0","0.22.0","0.22.1","0.23.0"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/vllm/PYSEC-2026-3406.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:L"}]}