Skip to content

GHSA-qvg3-h654-xq3j

CVE Information

Use-after-free of zlib input in png_read_end after incomplete zTXt, iTXt or iCCP decompression

Summary

libpng's sequential reader can read through a dangling pointer when an application calls png_read_end after png_read_info without first starting to read the image rows. A crafted zTXt, iTXt or iCCP chunk can make libpng abandon decompression while zlib still expects more input (for example, with an invalid window size in the zlib header, with decompressed data too large for libpng to accept, or with an ICC profile that fails validation), which leaves a stale input pointer and a non-zero input count in libpng's zlib stream. png_read_end then resumes decompression from that pointer instead of reading the IDAT data.

  • zTXt and iTXt: the pointer refers to libpng's chunk read buffer. If a later chunk before IDAT is read into that buffer without being decompressed, and needs more space (tEXt or pCAL, for example), the buffer is freed, and the read is a heap use-after-free.
  • iCCP: the pointer refers to local arrays of png_handle_iCCP, and the read is a stack use-after-return.

Severity

This is a medium-severity vulnerability.

The dangling pointer is only read, and the decompressed bytes go to a scratch buffer that is discarded, so no data is disclosed or corrupted. The worst outcome is a crash in the heap variant. It requires the freed memory to be inaccessible when png_read_end runs, as when the allocator has returned it to the operating system and nothing has mapped that range again. The stack variant reads stack memory, which normally remains mapped.

Exploitation requires a crafted PNG file and an application that uses the affected call sequence. That sequence is rare in practice, because png_read_end called this way does not skip the unread image data; it parses that data as chunks, and fails with a libpng error on well-formed files. The stale read happens before that error.

Affected versions

libpng 1.6.0 through 1.6.58, and the unreleased libpng 1.8.0 development branch. Fixed in libpng 1.6.59. Versions before 1.6.0 are not affected, because their png_read_end does not decompress.

Affected applications

An application is affected if it uses the sequential read API and calls png_read_end before any of png_read_update_info, png_start_read_image, png_read_row, png_read_rows or png_read_image.

An application is not affected if it reads the image rows before calling png_read_end, or if it uses png_read_png, the simplified API (png_image_*) or the progressive reader (png_process_data).

Reproduction

A PNG file triggers the heap variant if, before IDAT, it has a zTXt chunk whose compressed data starts with an invalid zlib window size, followed by a longer tEXt or pCAL chunk. An application that calls png_read_info and then png_read_end on this file reads freed memory, which AddressSanitizer reports as a heap-use-after-free in png_zlib_inflate.

Workaround

Upgrade to libpng 1.6.59.

If that is not possible, update the affected applications to avoid calling png_read_end when the image rows are not read. If the chunks after the image data are needed, call png_start_read_image before png_read_end; png_read_end then normally decompresses and discards the image data, and reports "Too much image data" as a benign error.

In either case, applications that call png_read_end without reading the image rows should be prepared to handle a libpng error from it.

Credits

Reported independently by Ze Sheng, O2Lab & AISLE and \@JasonHonKL.

References

  • Commit aa77ef38c17ab2fc1b41bec09fb973c6a386641d: fix on branch libpng16.
  • Commit 6c7783b151377f591c83955e23da50991ad1a600: fix on branch libpng18.
  • Issue #855: independent report of the iCCP variant.