BREW-AIDER-CVE-2026-59204 (GHSA-VJC4-5QP5-M44J)

Vulnerability from osv_homebrew – Published: 2026-08-13 16:35 – Updated: 2026-09-09 23:40 – Source website
VLAI
Summary
Pillow JPEG2000 tiled decode retains a growing scratch buffer and can be used for denial of service
Details

Summary

src/libImaging/Jpeg2KDecode.c:853 accumulates total_component_width across every tile in a JPEG2000 image instead of recomputing it per tile. That accumulated value is then used in the tile_bytes calculation at src/libImaging/Jpeg2KDecode.c:868, which can make the decoder grow state->buffer via realloc at src/libImaging/Jpeg2KDecode.c:876 up to roughly one full image's decompressed size even when each tile is small. A crafted tiled JPEG2000 file can therefore force substantially higher transient memory usage and trigger out-of-memory failures during decoding. Based on current evidence, the supported impact is denial of service, not memory corruption.

Details

  • Location: src/libImaging/Jpeg2KDecode.c:853
  • Root cause: total_component_width is initialized only once before the tile loop and keeps growing across tiles. It is then used to derive tile_bytes, so later tiles are treated as if they had the combined component width of all earlier tiles.
  • Dangerous operation: tile_bytes is promoted into tile_info.data_size, then state->buffer is grown with realloc at src/libImaging/Jpeg2KDecode.c:876.
  • Reachability: any attacker-controlled JPEG2000 image with many tiles reaches this path during normal Image.open(...).load() decoding.

PoC

The attached helper script and testcase were used: exercise_j2k_tile_realloc.zip

Generate the testcase:

pythonexercise_j2k_tile_realloc.py make poc_3664_rgba_tile1832.jp2 \
  --size 3664 --tile 1832

Expected geometry from the helper:

  • image size: 3664 x 3664
  • mode: RGBA
  • tile size: 1832 x 1832 (2x2 tiles)
  • image_bytes=53699584
  • uncapped RSS observed:
  • vulnerable build: maxrss_kb=180264
  • fixed comparison build: maxrss_kb=138404

Load it with the current vulnerable build:

python exercise_j2k_tile_realloc.py load poc_3664_rgba_tile1832.jp2

Load it again under a 160 MB address-space cap:

python exercise_j2k_tile_realloc.py load poc_3664_rgba_tile1832.jp2 --limit-mb 160

Impact

Conservative impact: denial of service through memory exhaustion during JPEG2000 decoding.


{
  "affected": [
    {
      "ecosystem_specific": {
        "fix": null,
        "range_state": "affected",
        "resource": "pillow",
        "resource_purl": "pkg:pypi/pillow@12.1.1",
        "upstream_fixed_in": "12.3.0"
      },
      "package": {
        "ecosystem": "Homebrew",
        "name": "aider",
        "purl": "pkg:brew/aider"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "database_specific": {
    "confidence": "high",
    "source": "matched",
    "strategy": "registry",
    "upstream_evidence": [
      {
        "ecosystem": "PyPI",
        "key": "pkg:pypi/pillow@12.1.1",
        "name": "pillow",
        "resource": "pillow",
        "strategy": "registry",
        "subject_version": "12.1.1"
      }
    ]
  },
  "details": "### Summary\n`src/libImaging/Jpeg2KDecode.c:853` accumulates `total_component_width` across every tile in a JPEG2000 image instead of recomputing it per tile. That accumulated value is then used in the `tile_bytes` calculation at `src/libImaging/Jpeg2KDecode.c:868`, which can make the decoder grow `state-\u003ebuffer` via `realloc` at `src/libImaging/Jpeg2KDecode.c:876` up to roughly one full image\u0027s decompressed size even when each tile is small. A crafted tiled JPEG2000 file can therefore force substantially higher transient memory usage and trigger out-of-memory failures during decoding. Based on current evidence, the supported impact is denial of service, not memory corruption.\n\n### Details\n- Location: `src/libImaging/Jpeg2KDecode.c:853`\n- Root cause: `total_component_width` is initialized only once before the tile loop and keeps growing across tiles. It is then used to derive `tile_bytes`, so later tiles are treated as if they had the combined component width of all earlier tiles.\n- Dangerous operation: `tile_bytes` is promoted into `tile_info.data_size`, then `state-\u003ebuffer` is grown with `realloc` at `src/libImaging/Jpeg2KDecode.c:876`.\n- Reachability: any attacker-controlled JPEG2000 image with many tiles reaches this path during normal `Image.open(...).load()` decoding.\n\n\n### PoC\nThe attached helper script and testcase were used:\n[exercise_j2k_tile_realloc.zip](https://github.com/user-attachments/files/28099912/exercise_j2k_tile_realloc.zip)\n\n\nGenerate the testcase:\n\n```bash\npythonexercise_j2k_tile_realloc.py make poc_3664_rgba_tile1832.jp2 \\\n  --size 3664 --tile 1832\n```\n\nExpected geometry from the helper:\n\n- image size: `3664 x 3664`\n- mode: `RGBA`\n- tile size: `1832 x 1832` (`2x2` tiles)\n- `image_bytes=53699584`\n- uncapped RSS observed:\n  - vulnerable build: `maxrss_kb=180264`\n  - fixed comparison build: `maxrss_kb=138404`\n\nLoad it with the current vulnerable build:\n\n```bash\npython exercise_j2k_tile_realloc.py load poc_3664_rgba_tile1832.jp2\n```\n\nLoad it again under a 160 MB address-space cap:\n\n```bash\npython exercise_j2k_tile_realloc.py load poc_3664_rgba_tile1832.jp2 --limit-mb 160\n```\n\n### Impact\nConservative impact: denial of service through memory exhaustion during JPEG2000 decoding.",
  "id": "BREW-aider-CVE-2026-59204",
  "modified": "2026-09-09T23:40:56Z",
  "published": "2026-08-13T16:35:12Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/python-pillow/Pillow/security/advisories/GHSA-vjc4-5qp5-m44j"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-59204"
    },
    {
      "type": "WEB",
      "url": "https://github.com/python-pillow/Pillow/pull/9704"
    },
    {
      "type": "WEB",
      "url": "https://github.com/python-pillow/Pillow/commit/13ada41172142f2fd9f0906f615a00ea623a11ca"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/python-pillow/Pillow"
    },
    {
      "type": "WEB",
      "url": "https://github.com/python-pillow/Pillow/releases/tag/12.3.0"
    }
  ],
  "schema_version": "1.7.3",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Pillow JPEG2000 tiled decode retains a growing scratch buffer and can be used for denial of service",
  "upstream": [
    "GHSA-vjc4-5qp5-m44j",
    "BIT-pillow-2026-59204",
    "CVE-2026-59204",
    "PYSEC-2026-3496"
  ]
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

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.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

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.


Loading…