fix: Prevent double-freeing `png_struct` members on allocation failure Clear the pointers inside `png_struct` after `png_free`, following the existing idiom in `png_read_buffer`, to ensure that a subsequent free will be a no-op. Several read-side functions free a `png_struct` member and allocate a replacement without clearing the pointer in between. When that allocation fails, `png_malloc` calls `png_error`, which longjmps out before the assignment, leaving the member pointing at freed memory. The application's cleanup path (`png_destroy_read_struct`, then `png_read_destroy`) then frees it a second time. The same defect occurs at five members across four functions: - `big_row_buf` and `big_prev_row` in `png_read_start_row`; - `palette` in `png_set_PLTE`; - `trans_alpha` in `png_set_tRNS`; - `quantize_index` in `png_set_quantize`. This is robustness hardening, not a fix for untrusted input. Arming the double-free needs a prior successful pass through the same site, and PNG content alone cannot deliver one: duplicate PLTE and tRNS chunks are rejected before their setters run, and within a single decode the row dimensions never grow, so the row-buffer reallocation guard cannot re-fire. The trigger is an application that causes a setter to run twice on one `png_struct` or reuses the struct across decodes, and then meets an allocation failure. This is a cherry-pick of commit a22696be0aadb185de33c152cc81df899eeefe6a from branch 'libpng18'. Co-authored-by: Cosmin Truta <ctruta@gmail.com> Signed-off-by: Anthony Hurtado <amhurtado@pm.me> Signed-off-by: Cosmin Truta <ctruta@gmail.com>
diff --git a/AUTHORS b/AUTHORS index 6726fc6..0aaba0c 100644 --- a/AUTHORS +++ b/AUTHORS
@@ -7,6 +7,7 @@ * Adam Richter * Alexander Smorkalov * Andreas Dilger + * Anthony Hurtado * Chris Blume * Cosmin Truta * Dave Martindale
diff --git a/pngrtran.c b/pngrtran.c index 0ac8df7..28e6127 100644 --- a/pngrtran.c +++ b/pngrtran.c
@@ -514,6 +514,7 @@ * this function more than once per png_struct. */ png_free(png_ptr, png_ptr->quantize_index); + png_ptr->quantize_index = NULL; png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH); for (i = 0; i < PNG_MAX_PALETTE_LENGTH; i++)
diff --git a/pngrutil.c b/pngrutil.c index 3a35fe9..8934684 100644 --- a/pngrutil.c +++ b/pngrutil.c
@@ -4604,6 +4604,7 @@ { png_free(png_ptr, png_ptr->big_row_buf); png_free(png_ptr, png_ptr->big_prev_row); + png_ptr->big_row_buf = png_ptr->big_prev_row = NULL; if (png_ptr->interlaced != 0) png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr,
diff --git a/pngset.c b/pngset.c index 513c51e..f86af0f 100644 --- a/pngset.c +++ b/pngset.c
@@ -811,6 +811,7 @@ * regardless of num_palette. */ png_free(png_ptr, png_ptr->palette); + png_ptr->palette = NULL; png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); info_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, @@ -1223,6 +1224,7 @@ * functions (e.g. png_do_expand_palette). */ png_free(png_ptr, png_ptr->trans_alpha); + png_ptr->trans_alpha = NULL; png_ptr->trans_alpha = png_voidcast(png_bytep, png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); memset(png_ptr->trans_alpha, 0xff, PNG_MAX_PALETTE_LENGTH);