CVE-2025-64720 AI-generated analysis
platform/external/libpng, read transformations (pngrtran.c), simplified read API compositing
In libpng pngrtran.c png_init_read_transformations, the palette tRNS compositing loop now branches on PNG_FLAG_OPTIMIZE_ALPHA. When the flag is set, each palette component is only premultiplied by its alpha (with gamma correction) and is not composited against the background. Palette values then keep the premultiplied invariant component <= alpha, which png_image_read_composite relies on for its png_sRGB_base[]/png_sRGB_delta[] table lookups.
Root cause
In png_init_read_transformations (pngrtran.c), the palette path for entries with 0 < trans_alpha[i] < 0xff always applied background compositing through png_composite(w, v, trans_alpha[i], back_1.*) and stored gamma_from_1[w] into palette[i]. It did this even when PNG_FLAG_OPTIMIZE_ALPHA was set. In that mode the rest of the pipeline expects palette entries to be pure premultiplied RGB (component <= alpha), with background compositing left to png_image_read_composite. Composited values can exceed alpha, so png_image_read_composite computed indices beyond the PNG_sRGB_FROM_LINEAR lookup tables png_sRGB_base[] and png_sRGB_delta[]. This mechanism comes from the commit message; the consumer code is not in the material.
How the fix works
- pngrtran.c:png_init_read_transformations: inside the palette loop, in the else branch for trans_alpha[i] != 0 and != 0xff, the fix adds `if ((png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0)`. In that branch, each of red, green and blue goes through: component = gamma_to_1[palette[i].c]; component = (component * trans_alpha[i] + 128) / 255; palette[i].c = gamma_from_1[component]. This only premultiplies: the value is linearised before and re-encoded after, and there is no background term.
- pngrtran.c:png_init_read_transformations: the original png_composite-based code (v = gamma_to_1[...]; png_composite(w, v, trans_alpha[i], back_1.*); palette[i].* = gamma_from_1[w]) moves unchanged into the else branch, so behaviour without PNG_FLAG_OPTIMIZE_ALPHA is preserved.
- Note: the commit message gives the rounding as `+ 127) / 255`, but the code uses `+ 128) / 255`. The code is what ships.
Completeness
- Covers only the palette path for partially transparent tRNS entries (0 < trans_alpha[i] < 0xff) in png_init_read_transformations. The commit names this path as the source of the out-of-range premultiplied values.
- The trans_alpha[i] == 0 case directly above (palette[i] = back) is not changed and still applies whatever the PNG_FLAG_OPTIMIZE_ALPHA setting. The material does not show whether the downstream consumer handles alpha 0 safely.
- The commit changes only pngrtran.c. No bounds check or clamping was added on the png_image_read_composite side. The fix relies on producing in-range values.
- The trailing `else` of the `if (i < num_trans && trans_alpha[i] != 0xff)` test (entries that are not partially transparent) is not modified, and its body is not shown.
Behavior changes
- For palette images with partial tRNS alpha read with PNG_FLAG_OPTIMIZE_ALPHA set, palette entries are now premultiplied RGB with no background applied. Per the commit message, background compositing happens later in png_image_read_composite, so decoded pixel values change in this configuration.
- No change for reads without PNG_FLAG_OPTIMIZE_ALPHA: the original png_composite calculation is kept verbatim.
- No new error or warning paths are added.
Per major
- Android 14: Same fix: commit ba5981bebfba in android-security-14.0.0_r28, a single hunk in pngrtran.c at line 1701 (blob 03ffef132 -> 38d1306f1). The code context at android-security-14.0.0_r29 shows the fix in place; the resolver reports 6/8 region lines present.
- Android 15: Same fix: commit 2a900c24d056 in android-security-15.0.0_r16, identical hunk content at line 1697 (blob 5fd5b7330 -> 6a5a401a0). The resolver reports 6/8 region lines present at android-security-15.0.0_r17.
- Android 16: Same fix: commit 2113a245b832 in android-security-16.0.0_r7, diff identical to Android 15 (same blob ids, line 1697). The resolver reports 6/8 region lines present at android-security-16.0.0_r8.
- Android 17: Same fix: commit 20c02c533b25 in android-17.0.0_r1 (base), diff identical to Android 14 (blob 03ffef132 -> 38d1306f1, line 1701). The resolver reports 6/8 region lines present at android-security-17.0.0_r1.
Unknowns
- The consumer code in png_image_read_composite and the sizes of the png_sRGB_base[]/png_sRGB_delta[] tables are not in the material. The out-of-bounds mechanism is taken from the commit message.
- The material does not show which Android components call the simplified libpng API with PNG_ALPHA_OPTIMIZED, so actual on-device reachability is unknown.
- Why the resolver reports 6/8 rather than 8/8 region lines at the latest tags, when the code context shows the fix applied.