CVE-2025-64505 AI-generated analysis
platform/external/libpng, read transformations (pngrtran.c, png_set_quantize / png_do_quantize)
libpng png_set_quantize now allocates and identity-initialises the quantize_index lookup table to PNG_MAX_PALETTE_LENGTH (256) bytes instead of num_palette bytes, so out-of-range palette indices from a malformed PNG no longer read past the table in png_do_quantize; a companion commit frees the previous quantize_index before reallocating and makes quantize_sort a local variable to stop leaks on repeated calls.
Root cause
In pngrtran.c png_set_quantize, when full_quantize == 0, the lookup table png_ptr->quantize_index was allocated with only num_palette bytes and filled for i < num_palette. According to the commit message, the pixel processing path in png_do_quantize indexes this table with palette indices from the image data and does no runtime bounds check. A malformed PNG whose indices are >= num_palette therefore causes reads past the end of the allocation. Separately, per the leak-fix commit message, png_set_quantize overwrote png_ptr->quantize_index without freeing the earlier allocation, so memory leaked when an application called the function more than once on the same png_struct (the message also mentions quantize_sort).
How the fix works
- Commit da0c851e9072 (first), pngrtran.c:png_set_quantize: adds png_free(png_ptr, png_ptr->quantize_index) before the png_malloc in the full_quantize == 0 branch, so repeated calls do not leak. Turns quantize_sort from a png_struct member into a function-local png_bytep, allocated with (png_alloc_size_t)num_palette and freed with png_free(png_ptr, quantize_sort) at the end of the histogram branch. Moves the int j declaration up to the block scope. The commit message says the pngstruct.h change was reverted for the security backport to avoid an ABI change.
- Commit 411d9be12a3e (second), pngrtran.c:png_set_quantize: changes the quantize_index allocation from num_palette bytes to PNG_MAX_PALETTE_LENGTH (256) bytes, and the initialisation loop to i < PNG_MAX_PALETTE_LENGTH, so every entry from 0 to 255 holds the identity mapping. Any 8-bit index therefore stays inside the table, and indices >= num_palette map to themselves. No bounds check is added to the pixel loop; the commit message says this is deliberate, to avoid per-pixel overhead.
Completeness
- Covers the quantize_index table used in the full_quantize == 0 path. The table now spans the whole 8-bit index space, matching the oversizing of palette[], trans_alpha[] and riffled_palette[] described in the commit message.
- The png_do_quantize pixel loop is not changed and does no runtime bounds check. Safety depends on the table being 256 bytes.
- quantize_sort is still allocated with num_palette bytes. In the shown code it is indexed only by loop variables bounded by num_palette.
- In the full_quantize != 0 branch, the only change is the quantize_sort renaming from da0c851e9072. The material does not show how the RGB lookup table is set up or sized.
Behavior changes
- quantize_index is always a 256-byte allocation, whatever num_palette is.
- During quantization, palette indices >= num_palette now map to the same index value (identity) instead of reading past the allocation.
- Calling png_set_quantize repeatedly on one png_struct no longer leaks the previous quantize_index; quantize_sort is a local temporary freed within the histogram branch.
- No new error or warning is emitted. On Android 14, png_struct layout is unchanged because the pngstruct.h change was reverted.
Per major
- Android 14: same fix, two commits in android-security-14.0.0_r28: da0c851e9072 (leak fix, quantize_sort made local, pngstruct.h change reverted for ABI), then 411d9be12a3e (256-byte quantize_index). The android-security-14.0.0_r29 code excerpt shows both applied; the facts report 7/8 lines of the patched region present.
- Android 15: adapted fix: 299ab85302d7 in android-security-15.0.0_r16 changes only the quantize_index allocation to 256 bytes. Its base (3749ebfa6) already has the png_free line and uses the (png_alloc_size_t)num_palette allocation form. 8/8 lines present at android-security-15.0.0_r17.
- Android 16: adapted fix: 93147074acc1 in android-security-16.0.0_r7, identical diff to Android 15 against the same base (3749ebfa6). 8/8 lines present at android-security-16.0.0_r8.
- Android 17: same diff as Android 14's 411d9be12a3e: dc721fee982c in android-17.0.0_r1 (resolved via base) against blob 831d98d08, the same pre-image as on 14 after da0c851e9072. 7/8 lines present at android-security-17.0.0_r1.
Unknowns
- The png_do_quantize code that does the out-of-bounds read is not in the material; its indexing behaviour comes from the commit message.
- Whether any Android platform component calls png_set_quantize, and therefore whether this is reachable on a device, is not shown.
- The bulletin lists the type as DoS at Critical severity. The material does not show what impact was demonstrated.
- Why the patched region is 7/8 present on Android 14 and 17.
- How and when the png_free leak fix reached the Android 15/16 base.