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

Jimmy Christensen jimmy
Mon Sep 14 13:59:57 CEST 2009


On 2009-09-13 20:35, Reimar D?ffinger wrote:
> 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;
>
>

Thanks, will change to that in rev19.

>> + * @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.
>

Yes, it needs to be 0-terminated. Will change it to strcmp in rev19. 
Same goes for the variable type string. Changed for that aswell.

>> +    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...
>

True. Will remove the == case. Although if this was the case there would 
be no data to have in the actual image in which case == would also be 
wrong :)

>> +    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.
>
>

IMHO that's optimizing a bit too much. With the current implementation 
it would be much easier to have the decoder use channel names other than 
just R, G and B. If eg. I wanted to expand the decoder to also decoder 
left eye of a OpenSXR file (OpenEXR stereo file). Their names are 
left.R, left.G and left.B. Optimizing for only 1 character channel names 
seems a bit too much, especially since it's only in the header parser 
and is that much speed important.

You're right that I might not need channel_iter since I added 
current_channel_offset.

Removed channel_iter and redid the code similar to yours.

>> +    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 + ..."
>

Found it now and fixed in rev19.

>> +            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 ().

Fixed.

> 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)).

You're right. No file should have different channel types anyway, so 
will check for that instead and error out if there is a mismatch. Also 
added better error output.

> 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?

Fixed.

> I'd then extend the verification of xdelta to include
> if (xdelta>  avptk->size / current_channel_offset) error;
> and here do

Good point. Added.

> if (line_offset>  avpkt->size - xdelta * current_channel_offset) error;
>
> Simplifications may be possible though.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenEXR-rev19.diff
Type: text/x-patch
Size: 20685 bytes
Desc: not available
URL: <http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/attachments/20090914/55aab8e5/attachment.bin>



More information about the ffmpeg-devel mailing list