[FFmpeg-devel] [PATCH] OpenEXR decoder rev-17

Reimar Döffinger Reimar.Doeffinger
Sun Sep 13 20:35:04 CEST 2009


On Sun, Sep 13, 2009 at 05:58:48PM +0200, Jimmy Christensen wrote:
Replace

> +static inline uint16_t exr_flt2uint(uint32_t v)
> +{
> +    int exp = v >> 23;
> +    if (v & 0x80000000)
> +        return 0;
> +    if (exp <= 127+7-24) // we would shift out all bits anyway
> +        return 0;

by e.g.

> +static inline uint16_t exr_flt2uint(int32_t v)
> +{
> +    int exp = v >> 23;
> +    // "HACK": negative values result in exp < 0, so clipping them to 0
> +    // is also handled by this condition, avoids explicit check for sign bit.
> +    if (exp <= 127+7-24) // we would shift out all bits anyway
> +        return 0;


> + * @return zero if variable is invalid

That is what had me confused in my previous comments: It is 0 both when
it is invalid (due to type mismatch) and when the name just does not match.

> +    if (buf_end - buf >= minimum_length && !strncmp(buf, value_name, strlen(value_name))) {
> +        buf += strlen(value_name)+1;

Hmm.. Are you sure that strncmp is correct and you shouldn't actually be
using strcmp? It looks to me like the string in buf must be
0-terminated, then strcmp would be correct.

> +    unsigned int variable_buffer_data_size = bytestream_get_le32(buf);
> +    if (buf_end - *buf <= variable_buffer_data_size) {
> +        av_log(NULL, AV_LOG_ERROR, "Incomplete header\n");
> +        return 0;
> +    }

Why is == an error? At least the variable size data still fits in in the
== case...

> +    const uint8_t bytes_per_color_table[3] = {
> +        1, 2, 4
> +    };
> +    unsigned int current_bits_per_color_id = 0;
> +
> +    if (!strcmp(*buf, "R"))
> +        s->red_channel = channel_iter;
> +    if (!strcmp(*buf, "G"))
> +        s->green_channel = channel_iter;
> +    if (!strcmp(*buf, "B"))
> +        s->blue_channel = channel_iter;
> +
> +    while (bytestream_get_byte(buf) && *buf < channel_list_end)
> +        continue; /* skip */
> +
> +    current_bits_per_color_id = bytestream_get_le32(buf);
> +    if (current_bits_per_color_id > 2)
> +        return -1;
> +
> +    if (channel_iter == s->red_channel   ||
> +        channel_iter == s->green_channel ||
> +        channel_iter == s->blue_channel) {
> +        if (channel_iter == s->red_channel) {
> +            s->bits_per_color_id  = current_bits_per_color_id;
> +            s->channel_offsets[0] = *current_channel_offset;
> +        }
> +        if (channel_iter == s->green_channel)
> +            s->channel_offsets[1] = *current_channel_offset;
> +        if (channel_iter == s->blue_channel)
> +            s->channel_offsets[2] = *current_channel_offset;
> +    }
> +    *current_channel_offset += bytes_per_color_table[current_bits_per_color_id];
> +    *buf += 12;
> +    return 1;

You misunderstood my comment on that. All those comparisons against
channel_iter are completely obfuscating things.
I guess you could do something like:

int channel_index = -1;
if (!buf[1]) { // only handle 1-character channel strings so far
    switch (buf[0]) {
    case 'R': channel_index++;
    case 'G': channel_index++;
    case 'B': channel_index++;
    }
}
[... while an current_bits_per_color_id as above ...]
if (channel_index >= 0) {
    s->bits_per_color_id  = current_bits_per_color_id;
    s->channel_offsets[channel_index] = *current_channel_offset;
}
*current_channel_offset[... rest identical...]

You do not need channel_iter, nor do you need s->red_channel etc.,
though you might want to know if a channel is missing.
You can either do that with a bit mask, or you could initialize
channel_offsets to something certainly invalid like -1.


> +    if (buf_end - buf < 10) {
> +            while (channel_list_end >= buf + 19) {
> +        if (buf_end - buf > 9) {
> +            if (buf_end - buf >= 5) {

Does this clarify the inconsistency I pointed out before?
The checks generally are "end - position > ..."  but that one is
of the kind "end > position + ..."

> +            const uint64_t line_offset = bytestream_get_le64(&buf) + 8;
> +            // Check if the buffer has the required bytes needed from the offset
> +            if ((buf_end - avpkt->data) < line_offset + xdelta * current_channel_offset) {

Pointless ().
But somewhere along the line you changed get_le32 to get_le64 and now
the right hand side can overflow.
Another problem is that line_offset + xdelta * current_channel_offset is
not even the right term, given that a malicious file could give r, g and
b different bit depths.
In that case the correct term would have to be
line_offset + xdelta * (FFMAX3(s->channel_offsets[0],
s->channel_offsets[1], s->channel_offsets[2]) + (s->bits_per_color_id ==
2 ? 4 : 2)).
I'd suggest to just verify that all bits_per_color_id are identical at
the point where you read that value from the file.
Also it seems to me that buf_end - avpkt->data would be simpler written
as avpkt->size?
I'd then extend the verification of xdelta to include
if (xdelta > avptk->size / current_channel_offset) error;
and here do
if (line_offset > avpkt->size - xdelta * current_channel_offset) error;

Simplifications may be possible though.



More information about the ffmpeg-devel mailing list