dwa_uncompress() addresses the three colour channels by a fixed slot index derived from nb_channels, while decode_block() extracts the planes through channel_offsets[]. When the channel list moves B, G or R off the slots dwa_uncompress() writes, the colour data ends up in the wrong planes, and a slot that no branch writes is left as it was.
An RGBA image carrying one more channel, here a channel named depth, decodes with the colour planes shifted by one and one plane left with whatever the plane buffer held. Storing the same channel list with ZIP decodes correctly, so this is dwa_uncompress() and not the header parsing. Decoding reports success. 7896cc67 checks the lower end of the same assumption by rejecting fewer than three channels; this is the upper end. It is also the second of the two assumptions named in the public description of CVE-2025-59733, whose pixel type half was addressed in 0469d68ac. The patch is attached as git format-patch output.
From 09c05809b506aa23ab62430be8fe9fd3067fdbe6 Mon Sep 17 00:00:00 2001 From: Nexory <[email protected]> Date: Sat, 22 Aug 2026 15:51:48 +0200 Subject: [PATCH 2/2] avcodec/exr: Check the DWA channel layout dwa_uncompress() addresses the three colour channels by a fixed slot index derived from nb_channels, while decode_block() extracts the planes through channel_offsets[]. When the channel list moves B, G or R off the slots dwa_uncompress() writes, the colour data ends up in the wrong planes, and a slot that no branch writes is left as it was. An RGBA image carrying one more channel, here a channel named depth, decodes with the colour planes shifted by one and one plane left with whatever the plane buffer held. Storing the same channel list with ZIP decodes correctly, so this is dwa_uncompress() and not the header parsing. Decoding reports success. 7896cc67 checks the lower end of the same assumption by rejecting fewer than three channels; this is the upper end. It is also the second of the two assumptions named in the public description of CVE-2025-59733, whose pixel type half was addressed in 0469d68ac. Signed-off-by: Nexory <[email protected]> --- libavcodec/exr.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libavcodec/exr.c b/libavcodec/exr.c index cbd80d75..6cb9e4aa 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -2107,6 +2107,9 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, if (s->compression == EXR_DWAA || s->compression == EXR_DWAB) { + const int ps = s->pixel_type == EXR_HALF ? 2 : 4; + const int o = s->nb_channels == 4; + for (int i = 0; i<s->nb_channels; i++) { EXRChannel *channel = &s->channels[i]; if (channel->pixel_type != s->pixel_type) { @@ -2114,6 +2117,21 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, return AVERROR_PATCHWELCOME; } } + + /* dwa_uncompress() addresses the colour channels by a fixed slot index + * instead of by channel_offsets[], so it only decodes a channel list + * that puts B, G and R on the slots it writes. Any other list ends up + * in the wrong planes, and a slot that no branch writes is left as it + * was. Lists shorter than three channels are left alone: they are + * reported as Gray DWA, and a missing channel list by the switch + * below. */ + if (s->nb_channels >= 3 && + (s->channel_offsets[2] != (o + 0) * ps || + s->channel_offsets[1] != (o + 1) * ps || + s->channel_offsets[0] != (o + 2) * ps)) { + avpriv_request_sample(s->avctx, "DWA channel layout"); + return AVERROR_PATCHWELCOME; + } } switch (s->pixel_type) { -- 2.47.3
_______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
