From ea094764f3436e3c6524622724c2d342a3eff235 Mon Sep 17 00:00:00 2001 From: Cosmin Truta Date: Sat, 8 Nov 2025 17:16:59 +0200 Subject: [PATCH] Fix a memory leak in function `png_set_quantize`; refactor Release the previously-allocated array `quantize_index` before reallocating it. This avoids leaking memory when the function `png_set_quantize` is called multiple times on the same `png_struct`. This function assumed single-call usage, but fuzzing revealed that repeated calls would overwrite the pointers without freeing the original allocations, leaking 256 bytes per call for `quantize_index` and additional memory for `quantize_sort` when histogram-based quantization is used. Also remove the array `quantize_sort` from the list of `png_struct` members and make it a local variable. This array is initialized, used and released exclusively inside the function `png_set_quantize`. Reported-by: Samsung-PENTEST Analyzed-by: degrigis Reviewed-by: John Bowler CVE: CVE-2025-64505 Upstream-Status: Backport [https://github.com/pnggroup/libpng/commit/ea094764f3436e3c6524622724c2d342a3eff235] Signed-off-by: Peter Marko --- pngrtran.c | 43 +++++++++++++++++++++++-------------------- pngstruct.h | 1 - 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/pngrtran.c b/pngrtran.c index 1809db704..4632dd521 100644 --- a/pngrtran.c +++ b/pngrtran.c @@ -440,6 +440,12 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, { int i; + /* Initialize the array to index colors. + * + * Be careful to avoid leaking memory. Applications are allowed to call + * this function more than once per png_struct. + */ + png_free(png_ptr, png_ptr->quantize_index); png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)num_palette); for (i = 0; i < num_palette; i++) @@ -454,15 +460,14 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, * Perhaps not the best solution, but good enough. */ - int i; + png_bytep quantize_sort; + int i, j; - /* Initialize an array to sort colors */ - png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + /* Initialize the local array to sort colors. */ + quantize_sort = (png_bytep)png_malloc(png_ptr, (png_alloc_size_t)num_palette); - - /* Initialize the quantize_sort array */ for (i = 0; i < num_palette; i++) - png_ptr->quantize_sort[i] = (png_byte)i; + quantize_sort[i] = (png_byte)i; /* Find the least used palette entries by starting a * bubble sort, and running it until we have sorted @@ -474,19 +479,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = num_palette - 1; i >= maximum_colors; i--) { int done; /* To stop early if the list is pre-sorted */ - int j; done = 1; for (j = 0; j < i; j++) { - if (histogram[png_ptr->quantize_sort[j]] - < histogram[png_ptr->quantize_sort[j + 1]]) + if (histogram[quantize_sort[j]] + < histogram[quantize_sort[j + 1]]) { png_byte t; - t = png_ptr->quantize_sort[j]; - png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; - png_ptr->quantize_sort[j + 1] = t; + t = quantize_sort[j]; + quantize_sort[j] = quantize_sort[j + 1]; + quantize_sort[j + 1] = t; done = 0; } } @@ -498,18 +502,18 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, /* Swap the palette around, and set up a table, if necessary */ if (full_quantize != 0) { - int j = num_palette; + j = num_palette; /* Put all the useful colors within the max, but don't * move the others. */ for (i = 0; i < maximum_colors; i++) { - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); palette[i] = palette[j]; } @@ -517,7 +521,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } else { - int j = num_palette; + j = num_palette; /* Move all the used colors inside the max limit, and * develop a translation table. @@ -525,13 +529,13 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, for (i = 0; i < maximum_colors; i++) { /* Only move the colors we need to */ - if ((int)png_ptr->quantize_sort[i] >= maximum_colors) + if ((int)quantize_sort[i] >= maximum_colors) { png_color tmp_color; do j--; - while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + while ((int)quantize_sort[j] >= maximum_colors); tmp_color = palette[j]; palette[j] = palette[i]; @@ -569,8 +573,7 @@ png_set_quantize(png_structrp png_ptr, png_colorp palette, } } } - png_free(png_ptr, png_ptr->quantize_sort); - png_ptr->quantize_sort = NULL; + png_free(png_ptr, quantize_sort); } else { diff --git a/pngstruct.h b/pngstruct.h index 084422bc1..fe5fa0415 100644 --- a/pngstruct.h +++ b/pngstruct.h @@ -413,7 +413,6 @@ struct png_struct_def #ifdef PNG_READ_QUANTIZE_SUPPORTED /* The following three members were added at version 1.0.14 and 1.2.4 */ - png_bytep quantize_sort; /* working sort array */ png_bytep index_to_palette; /* where the original index currently is in the palette */ png_bytep palette_to_index; /* which original index points to this