avcodec/rv34: Simplify and factor get_slice_offset() code
authorMichael Niedermayer <michael@niedermayer.cc>
Fri, 24 Feb 2017 11:46:28 +0000 (12:46 +0100)
committerMichael Niedermayer <michael@niedermayer.cc>
Fri, 24 Feb 2017 13:12:12 +0000 (14:12 +0100)
This also fixes several integer overflows by checking each value before
use.
Fixes: 662/clusterfuzz-testcase-4898131432964096

Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/targets/ffmpeg
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
libavcodec/rv34.c

index b5802d4..be49804 100644 (file)
@@ -1591,10 +1591,13 @@ int ff_rv34_decode_update_thread_context(AVCodecContext *dst, const AVCodecConte
     return ff_mpeg_update_thread_context(dst, src);
 }
 
-static int get_slice_offset(AVCodecContext *avctx, const uint8_t *buf, int n)
+static int get_slice_offset(AVCodecContext *avctx, const uint8_t *buf, int n, int slice_count, int buf_size)
 {
-    if(avctx->slice_count) return avctx->slice_offset[n];
-    else                   return AV_RL32(buf + n*8 - 4) == 1 ? AV_RL32(buf + n*8) :  AV_RB32(buf + n*8);
+    if (n < slice_count) {
+        if(avctx->slice_count) return avctx->slice_offset[n];
+        else                   return AV_RL32(buf + n*8 - 4) == 1 ? AV_RL32(buf + n*8) :  AV_RB32(buf + n*8);
+    } else
+        return buf_size;
 }
 
 static int finish_frame(AVCodecContext *avctx, AVFrame *pict)
@@ -1652,6 +1655,7 @@ int ff_rv34_decode_frame(AVCodecContext *avctx,
     const uint8_t *slices_hdr = NULL;
     int last = 0;
     int faulty_b = 0;
+    int offset;
 
     /* no supplementary picture */
     if (buf_size == 0) {
@@ -1674,13 +1678,13 @@ int ff_rv34_decode_frame(AVCodecContext *avctx,
     }else
         slice_count = avctx->slice_count;
 
+    offset = get_slice_offset(avctx, slices_hdr, 0, slice_count, buf_size);
     //parse first slice header to check whether this frame can be decoded
-    if(get_slice_offset(avctx, slices_hdr, 0) < 0 ||
-       get_slice_offset(avctx, slices_hdr, 0) > buf_size){
+    if(offset < 0 || offset > buf_size){
         av_log(avctx, AV_LOG_ERROR, "Slice offset is invalid\n");
         return AVERROR_INVALIDDATA;
     }
-    init_get_bits(&s->gb, buf+get_slice_offset(avctx, slices_hdr, 0), (buf_size-get_slice_offset(avctx, slices_hdr, 0))*8);
+    init_get_bits(&s->gb, buf+offset, (buf_size-offset)*8);
     if(r->parse_slice_header(r, &r->s.gb, &si) < 0 || si.start){
         av_log(avctx, AV_LOG_ERROR, "First slice header is incorrect\n");
         return AVERROR_INVALIDDATA;
@@ -1783,40 +1787,32 @@ int ff_rv34_decode_frame(AVCodecContext *avctx,
         return AVERROR_INVALIDDATA;
 
     for(i = 0; i < slice_count; i++){
-        int offset = get_slice_offset(avctx, slices_hdr, i);
+        int offset  = get_slice_offset(avctx, slices_hdr, i  , slice_count, buf_size);
+        int offset1 = get_slice_offset(avctx, slices_hdr, i+1, slice_count, buf_size);
         int size;
-        if(i+1 == slice_count)
-            size = buf_size - offset;
-        else
-            size = get_slice_offset(avctx, slices_hdr, i+1) - offset;
 
-        if(offset < 0 || offset > buf_size){
+        if(offset < 0 || offset > offset1 || offset1 > buf_size){
             av_log(avctx, AV_LOG_ERROR, "Slice offset is invalid\n");
             break;
         }
+        size = offset1 - offset;
 
         r->si.end = s->mb_width * s->mb_height;
         s->mb_num_left = r->s.mb_x + r->s.mb_y*r->s.mb_width - r->si.start;
 
         if(i+1 < slice_count){
-            if (get_slice_offset(avctx, slices_hdr, i+1) < 0 ||
-                get_slice_offset(avctx, slices_hdr, i+1) > buf_size) {
+            int offset2 = get_slice_offset(avctx, slices_hdr, i+2, slice_count, buf_size);
+            if (offset2 < offset1 || offset2 > buf_size) {
                 av_log(avctx, AV_LOG_ERROR, "Slice offset is invalid\n");
                 break;
             }
-            init_get_bits(&s->gb, buf+get_slice_offset(avctx, slices_hdr, i+1), (buf_size-get_slice_offset(avctx, slices_hdr, i+1))*8);
+            init_get_bits(&s->gb, buf+offset1, (buf_size-offset1)*8);
             if(r->parse_slice_header(r, &r->s.gb, &si) < 0){
-                if(i+2 < slice_count)
-                    size = get_slice_offset(avctx, slices_hdr, i+2) - offset;
-                else
-                    size = buf_size - offset;
+                size = offset2 - offset;
             }else
                 r->si.end = si.start;
         }
-        if (size < 0 || size > buf_size - offset) {
-            av_log(avctx, AV_LOG_ERROR, "Slice size is invalid\n");
-            break;
-        }
+        av_assert0 (size >= 0 && size <= buf_size - offset);
         last = rv34_decode_slice(r, r->si.end, buf + offset, size);
         if(last)
             break;