Skip to content

Heap buffer over-read in `png_image_read_direct_scaled` (regression from CVE-2025-65018 fix)

Moderate
ctruta published GHSA-mmq5-27w3-rxpp Jan 12, 2026

Package

libpng

Affected versions

>= 1.6.51, <= 1.6.53

Patched versions

1.6.54

Description

Summary

Heap buffer over-read in the libpng simplified API function png_image_finish_read when processing interlaced 16-bit PNGs with 8-bit output format and non-minimal row stride. This is a regression introduced by the fix for CVE-2025-65018.

Technical Details

Vulnerability Mechanism

  1. Application uses simplified API with interlaced 16-bit PNG and 8-bit output.
  2. Application provides row_stride larger than actual row width (for alignment) or negative (for bottom-up layout).
  3. The png_image_read_direct_scaled() function (introduced in CVE-2025-65018 fix) uses stride instead of row width for memcpy size.
  4. Buffer over-read occurs because local_row is sized for actual row width, not stride.

Root Cause

The new png_image_read_direct_scaled() function contains:

memcpy(output_row, local_row, (size_t)row_bytes);

This violates the established pattern in pngread.c where:

  • Stride (row_bytes) is used for pointer advancement
  • Width (png_get_rowbytes()) is used for data bounds/copy sizes

Attack Scenarios

Padded Stride (over-read):

row_stride = 144 bytes (aligned)
row_width  = 128 bytes (actual data)
Over-read  = 16 bytes per row

Negative Stride (crash):

row_stride = -128
(size_t)(-128) = 18,446,744,073,709,551,488
Result: memcpy attempts to read ~18 exabytes → crash

Impact

Attack Vector

Prerequisites:

  • Application uses simplified API (png_image_*).
  • Application processes untrusted PNG files.
  • Application uses non-minimal row stride (common for alignment or bottom-up layouts).

Attack: Craft interlaced 16-bit PNG + application uses padded/negative stride ==> heap over-read or crash

Consequences

  • Information Disclosure (Low): Over-read may expose adjacent heap data.
  • Denial of Service (High): Negative stride causes deterministic crash.

Exploitability

  • Attack Complexity: Low (common API usage patterns).
  • User Interaction: Required (victim opens PNG file).
  • Privileges Required: None.

Affected Software

Applications using simplified API with non-default row stride:

  • Image viewers with memory-aligned buffers.
  • Graphics frameworks using bottom-up pixel layouts.
  • Game engines with specific texture alignment requirements.

Note: Applications using default stride (row_stride = 0 or row_stride = PNG_IMAGE_ROW_STRIDE(image)) are NOT affected.

Fix

Implementation

@@ -3138,9 +3138,11 @@ png_image_read_direct_scaled(png_voidp argument)
    png_imagep image = display->image;
    png_structrp png_ptr = image->opaque->png_ptr;
+   png_inforp info_ptr = image->opaque->info_ptr;
    png_bytep local_row = png_voidcast(png_bytep, display->local_row);
    png_bytep first_row = png_voidcast(png_bytep, display->first_row);
    ptrdiff_t row_bytes = display->row_bytes;
+   size_t copy_bytes = png_get_rowbytes(png_ptr, info_ptr);
    int passes;

@@ -3170,7 +3172,7 @@ png_image_read_direct_scaled(png_voidp argument)
          png_read_row(png_ptr, local_row, NULL);

          /* Copy from local_row to user buffer. */
-         memcpy(output_row, local_row, (size_t)row_bytes);
+         memcpy(output_row, local_row, copy_bytes);
          output_row += row_bytes;
       }
    }

Key Changes

  1. Add info_ptr declaration to access row width via png_get_rowbytes().
  2. Add copy_bytes variable for actual data size.
  3. Use copy_bytes for memcpy size (data bounds).
  4. Keep row_bytes for pointer advancement (navigation).

This follows the established pattern throughout pngread.c.

Detection

Vulnerable Code Pattern

Applications are vulnerable if they:

  1. Process interlaced 16-bit PNGs via simplified API
  2. Use non-default row stride
/* Vulnerable pattern */
ptrdiff_t stride = (row_width + 15) & ~15;  /* Aligned stride */
png_image_finish_read(&image, NULL, buffer, stride, NULL);  /* VULNERABLE */

/* Also vulnerable */
ptrdiff_t stride = -row_width;  /* Bottom-up layout */
png_image_finish_read(&image, NULL, last_row, stride, NULL);  /* VULNERABLE */

Testing

Create interlaced 16-bit test PNG:

convert -depth 16 -size 32x32 -interlace PNG xc:red test.png

Run with AddressSanitizer:

clang -fsanitize=address -g test_program.c -lpng -o test
./test test.png

Expected:

  • Vulnerable versions (1.6.51-1.6.53): ASan reports heap-buffer-overflow READ
  • Fixed versions (1.6.54+): Clean execution

Timeline

Date Event
2025-11-19 CVE-2025-65018 fix committed (introduces regression)
2025-11-21 libpng 1.6.51 released
2025-12-03 libpng 1.6.52 released
2025-12-05 libpng 1.6.53 released
2026-01-06 @simecek reports regression with proposed fix (GitHub Issue #778)
2026-01-07 Vulnerability and proposed fix confirmed
2026-01-09 CVE requested
2026-01-12 libpng 1.6.54 released

Relationship to CVE-2025-65018

This vulnerability is a regression introduced by the fix for CVE-2025-65018:

CVE-2025-65018 (GH-755) This Issue (GH-778)
Type Heap buffer overflow (WRITE) Heap buffer over-read (READ)
CWE CWE-787 CWE-125
Location png_combine_row() png_image_read_direct_scaled()
Trigger Interlaced 16-bit PNG, any stride Interlaced 16-bit PNG, non-default stride
Introduced libpng 1.6.0 libpng 1.6.51 (by CVE-2025-65018 fix)
Impact Write overflow, potential RCE Read overflow, info leak + DoS

References

  • Issue: #778
  • Fix: e4f7ad4ea2
  • Related CVE: CVE-2025-65018
  • Related (regression-introducing) commit: 218612d

Credits

  • Vulnerability discovery: Petr Simecek, Stanislav Fort, Pavel Kohout
  • Proposed fix: Petr Simecek
  • Verification and release: Cosmin Truta (libpng maintainer)

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Unchanged
Confidentiality
Low
Integrity
None
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:H

CVE ID

CVE-2026-22695

Weaknesses

Out-of-bounds Read

The product reads data past the end, or before the beginning, of the intended buffer. Learn more on MITRE.

Credits