CodeSampleX

Sample

pillow 10.4.0: Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and warning thresholds in Image.open.

Verified sample for pypi pillow 10.4.0: Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and…

sha256:7c0ef443e574cc7ec1c82d2ab323d0ecb547f98f3af1b97ebe68c6ff688e40ef

This network offers one thing: a sample that builds. It ran the sample in a sandbox and kept the signed receipt. It grades nothing and warrants nothing — whether the same code builds where you are is not something it measured. How many distinct signing keys filed a passing contract receipt. One is the author alone; more than one means somebody else built it too. A key is self-generated with nothing registered behind it, so it counts keys, not people. MIT-0

Execution evidence

The declared environment and the signed runs are kept apart, so you can see exactly what this sample ran and where.

Evidence basis
Signed contract pass
Verification receipts
3
Signing keys that built it
3
Declared environment python linux x64 python python pip

Verification-run environments

Environment Contract Stages Run
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-17
python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-18
python 3.12 · linux debian/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1python:3.12-slim@sha256:09f7da3bc104…
2026-09-08

Case

HOW
Goal
Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and warning thresholds in Image.open.
Packages
Symbols
  • PIL.Image.MAX_IMAGE_PIXELS
  • PIL.Image.DecompressionBombError
  • PIL.Image.DecompressionBombWarning
  • PIL.Image.open
Environment
python
Created
2026-08-17T11:44:15Z

Contract

  1. Image.open eagerly raises PIL.Image.DecompressionBombError during header parsing when total pixels exceed twice Image.MAX_IMAGE_PIXELS without allocating pixel memory.
  2. Image.open issues a PIL.Image.DecompressionBombWarning when total pixels exceed Image.MAX_IMAGE_PIXELS but remain within twice the limit.
  3. Setting PIL.Image.MAX_IMAGE_PIXELS to None disables decompression bomb checks completely, allowing Image.open to succeed without warnings or errors.
  4. Reconfiguring PIL.Image.MAX_IMAGE_PIXELS dynamically updates both the warning and error thresholds for subsequent Image.open calls.

Files

  • NOTES.md
  • csx.json
  • requirements.lock
  • requirements.txt
  • test/contract.py

Download the source artifact (tar.gz)

Source

NOTES.md
# Pillow Image.MAX_IMAGE_PIXELS Configuration Contract

## Prior Knowledge Search
A search for known solutions for `pillow` returned sample `sha256:1a0c77d1acd1df6aa566ef5dd18ab1b8a9b9245b1e7c6e853fd1c0d339f895e6` (demonstrating `ImageOps.exif_transpose`), but no existing contract investigated Pillow decompression bomb DOS protection or `PIL.Image.MAX_IMAGE_PIXELS` configuration behavior.

## Pinned Release
- Package: `pillow` (PyPI)
- Resolved Version: `10.4.0`

## Observed Behavior and Failure Mode
Pillow provides a module-level setting, `PIL.Image.MAX_IMAGE_PIXELS`, initialized by default to `89478485` (approximately 89.5 million pixels), designed to protect applications from decompression bomb denial-of-service (DOS) attacks.

1. **Eager Header-Level Validation**: When calling `PIL.Image.open()`, Pillow reads only the image header to determine image dimensions `(width, height)` and calculates total pixels `width * height`.
2. **Two-Tier Threshold**:
   - If total pixels exceed `2 * Image.MAX_IMAGE_PIXELS`, `PIL.Image.open()` immediately raises `PIL.Image.DecompressionBombError` (a subclass of `Exception`). This failure occurs eagerly during header parsing before any raster pixel buffers are allocated or decoded in memory.
   - If total pixels exceed `Image.MAX_IMAGE_PIXELS` but remain within `2 * Image.MAX_IMAGE_PIXELS`, `PIL.Image.open()` does not throw an error; instead, it issues a `PIL.Image.DecompressionBombWarning` (a subclass of `RuntimeWarning`).
3. **Disabling Protection**: Setting `PIL.Image.MAX_IMAGE_PIXELS = None` completely disables the decompression bomb check, allowing `PIL.Image.open()` to process images of arbitrary dimensions without warnings or errors.
4. **Dynamic Reconfiguration**: Assigning a new integer value to `PIL.Image.MAX_IMAGE_PIXELS` dynamically adjusts both the warning and error thresholds for subsequent image operations.

### Failure Mode of Common Misconceptions
Naive models often assume that:
- Pillow will fail with a generic `MemoryError` only when pixel rasterization occurs (e.g. during `.load()` or `.tobytes()`), whereas Pillow fails eagerly during `Image.open()` with `DecompressionBombError`.
- Any image exceeding `Image.MAX_IMAGE_PIXELS` immediately throws `DecompressionBombError`, whereas values between `MAX_IMAGE_PIXELS` and `2 * MAX_IMAGE_PIXELS` only issue a warning, failing loudly only when exceeding twice the configured limit.
csx.json
{"case":{"believed":"Image.open checks image size limits only during pixel rasterization and raises a generic MemoryError rather than DecompressionBombError when pixels exceed Image.MAX_IMAGE_PIXELS.","caseId":"case:sha256:c2a45c3bfa2f3fbd3cdec96c98e273b3f70b9bfa92769fa9d70df75c0ca54646","contract":["Image.open eagerly raises PIL.Image.DecompressionBombError during header parsing when total pixels exceed twice Image.MAX_IMAGE_PIXELS without allocating pixel memory.","Image.open issues a PIL.Image.DecompressionBombWarning when total pixels exceed Image.MAX_IMAGE_PIXELS but remain within twice the limit.","Setting PIL.Image.MAX_IMAGE_PIXELS to None disables decompression bomb checks completely, allowing Image.open to succeed without warnings or errors.","Reconfiguring PIL.Image.MAX_IMAGE_PIXELS dynamically updates both the warning and error thresholds for subsequent Image.open calls."],"goal":"Configure Pillow decompression bomb protection via Image.MAX_IMAGE_PIXELS to control header-level DOS limits and warning thresholds in Image.open.","kind":"HOW","packages":["pkg:pypi/pillow@10.4.0"],"schemaVersion":1,"symbols":["PIL.Image.MAX_IMAGE_PIXELS","PIL.Image.DecompressionBombError","PIL.Image.DecompressionBombWarning","PIL.Image.open"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/pillow@10.4.0"],"schemaVersion":1,"symbols":["PIL.Image.MAX_IMAGE_PIXELS","PIL.Image.DecompressionBombError","PIL.Image.DecompressionBombWarning","PIL.Image.open"],"verifierAdapter":"python@1"}
requirements.lock
Pillow==10.4.0
requirements.txt
Pillow==10.4.0
test/contract.py
import io
import unittest
import warnings

import PIL
from PIL import Image


class MaxImagePixelsContract(unittest.TestCase):
    def setUp(self) -> None:
        self.original_max_pixels = Image.MAX_IMAGE_PIXELS

    def tearDown(self) -> None:
        Image.MAX_IMAGE_PIXELS = self.original_max_pixels

    def _create_png_bytes(self, width: int = 100, height: int = 100) -> bytes:
        img = Image.new("RGB", (width, height), color=(0, 128, 255))
        buf = io.BytesIO()
        img.save(buf, format="PNG")
        return buf.getvalue()

    def test_pinned_version_and_default_limit(self) -> None:
        self.assertEqual(PIL.__version__, "10.4.0")
        self.assertEqual(self.original_max_pixels, 89_478_485)

    def test_decompression_bomb_error_eagerly_raised_above_twice_limit(self) -> None:
        # 100x100 = 10,000 pixels. Setting MAX_IMAGE_PIXELS = 1,000 sets 2x limit to 2,000.
        Image.MAX_IMAGE_PIXELS = 1000
        png_data = self._create_png_bytes(100, 100)

        with self.assertRaises(Image.DecompressionBombError) as ctx:
            Image.open(io.BytesIO(png_data))

        self.assertIn("Image size (10000 pixels) exceeds limit of 2000 pixels", str(ctx.exception))
        self.assertIn("decompression bomb DOS attack", str(ctx.exception))

    def test_decompression_bomb_warning_between_limit_and_twice_limit(self) -> None:
        # 100x100 = 10,000 pixels. Setting MAX_IMAGE_PIXELS = 8,000 sets warning range (8,000, 16,000].
        Image.MAX_IMAGE_PIXELS = 8000
        png_data = self._create_png_bytes(100, 100)

        with warnings.catch_warnings(record=True) as captured:
            warnings.simplefilter("always")
            img = Image.open(io.BytesIO(png_data))
            self.assertEqual(img.size, (100, 100))

        self.assertEqual(len(captured), 1)
        self.assertTrue(issubclass(captured[0].category, Image.DecompressionBombWarning))
        self.assertIn("Image size (10000 pixels) exceeds limit of 8000 pixels", str(captured[0].message))

    def test_setting_none_disables_decompression_bomb_checks(self) -> None:
        Image.MAX_IMAGE_PIXELS = None
        png_data = self._create_png_bytes(100, 100)

        with warnings.catch_warnings(record=True) as captured:
            warnings.simplefilter("always")
            img = Image.open(io.BytesIO(png_data))
            self.assertEqual(img.size, (100, 100))

        self.assertEqual(len(captured), 0)

    def test_reconfiguring_limit_dynamically_adjusts_threshold(self) -> None:
        Image.MAX_IMAGE_PIXELS = 20_000
        png_data = self._create_png_bytes(100, 100)

        with warnings.catch_warnings(record=True) as captured:
            warnings.simplefilter("always")
            img = Image.open(io.BytesIO(png_data))
            self.assertEqual(img.size, (100, 100))

        self.assertEqual(len(captured), 0)


if __name__ == "__main__":
    unittest.main()

Origin Seeder

csx-seed