Skip to content

JPEG qtables iteration failure causes a NULL dereference in Image.save() #9917

Description

@marinelay

Summary

Image.save() first validates the JPEG qtables sequence and then reads it again during encoding.
If the sequence raises an exception when it is read again, Pillow crashes instead of propagating the exception.

Versions

Pillow 12.3.0, CPython 3.12.3, Ubuntu 24.04 x86_64, glibc 2.39.

Reproducer

from io import BytesIO
from PIL import Image

class SecondIterRaises(list):
    def __init__(self, values):
        super().__init__(values)
        self.calls = 0

    def __iter__(self):
        self.calls += 1
        if self.calls == 1:
            return super().__iter__()
        raise RuntimeError("second iteration failed")

Image.new("RGB", (1, 1)).save(
    BytesIO(),
    format="JPEG",
    qtables=SecondIterRaises([[1] * 64]),
)

Running it produces:

Segmentation fault (core dumped)

An ASan/UBSan build reports a null-pointer read in get_qtables_arrays() at src/encode.c:1131:

/usr/include/python3.12/object.h:220:16: runtime error:
member access within null pointer of type 'PyObject'

AddressSanitizer: SEGV on unknown address 0x000000000008
The signal is caused by a READ memory access.
    #0 Py_TYPE                   object.h:220:16
    #1 get_qtables_arrays       src/encode.c:1131:17
    #2 PyImaging_JpegEncoderNew src/encode.c:1234:15

I found this while fuzzing Python C extension modules for a small research project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions