Skip to content

HEIF Heap OOB Read

Moderate
lgritz published GHSA-jjm9-9m4m-c8p2 Jul 15, 2024

Package

OpenImageIO

Affected versions

< 2.5.13.0

Patched versions

>= 2.5.13.1

Description

Bug class: Heap OOB read
Reproduction steps:

  1. oiiotool poc.heic -o /tmp/out.jpeg

poc (download from google drive)

Environment:

  • OS: Ubuntu 22.04
  • OIIO version: master

There is a bug in the heif input functionality of OpenImageIO. Specifically, in HeifInput::seek_subimage() (src). A ImageSpec m_spec is constructed with width and height obtained as follows:

m_spec = ImageSpec(m_ihandle.get_width(), m_ihandle.get_height(), bits / 8,
                   TypeUInt8);

This uses the libheif ImageHandle::get_width/height() method, which returns the dimension of the heif image handle. These dimensions are later used in calls to HeifInput::read_native_scanline() to read the decoded image's plane buffers:

    const uint8_t* hdata = m_himage.get_plane(heif_channel_interleaved,
                                              &ystride);
    if (!hdata) {
        errorfmt("Unknown read error");
        return false;
    }
    hdata += (y - m_spec.y) * ystride;
    memcpy(data, hdata, m_spec.width * m_spec.pixel_bytes());

However, there is no guarantee that the dimensions returned by ImageHandle::get_width/height() match the size of the decoded image's planes. Instead, the decoded image's dimensions should be obtained with Image::get_width/height(channel) (src). The original intention of this usage pattern is to accommodate transformations in the heif file -- the dimensions from the ImageHandle are that of the untransformed image, while the dimensions from the Image are that of the transformed image. However, since the dimensions returned from ImageHandle are obtained by parsing the ispe (Image spatial Extents), it is possible for an attacker to forge the ispe values, which would create an ImageSpec with incorrect dimensions. In such a case, there would be an OOB read on the decoded plane data if the incorrect dimensions in the ImageSpec are trusted.

This is indeed the case in ImageInput::read_image(). Using this C++ example, I triggered an ASan fault with this poc. I crafted this poc by using a regular heic file and forging the values of its ispe dimensions from (1440, 960) to (14400, 9600). It is possible to get arbitrary OOB reads by specially crafting the heif file. This poc also crashes oiiotool. This specific command will crash it: oiiotool poc.heic -o out.jpeg (but really any operation that reads the entire image will work).

In the worst case, this can lead to an information disclosure vulnerability, particularly for programs that directly use the ImageInput APIs.

The fix should be quite simple: Create the ImageSpec as follows, using the Image::get_width/height() methods as mentioned earlier.

m_spec = ImageSpec(m_himage.get_width(heif_channel_interleaved),
                   m_himage.get_height(heif_channel_interleaved),
                   bits / 8, TypeUInt8);

Severity

Moderate

CVE ID

CVE-2024-40630

Weaknesses

Credits