Fixes: vmixdec.c:132:34: runtime error: signed integer overflow: -2147483648 * 1856 cannot be represented in type 'int' Fixes: vmixdec.c:119:20: runtime error: signed integer overflow: -1256 + -2147483648 cannot be represented in type 'int' Fixes: vmixdec.c:137:36: runtime error: signed integer overflow: 2147483416 * 16 cannot be represented in type 'int' Fixes: 59843/clusterfuzz-testcase-minimized-ffmpeg_AV_CODEC_ID_VMIX_fuzzer-4857434624360448 Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
		
			
				
	
	
		
			289 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			289 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * vMix decoder
 | |
|  * Copyright (c) 2023 Paul B Mahol
 | |
|  *
 | |
|  * This file is part of FFmpeg.
 | |
|  *
 | |
|  * FFmpeg is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * FFmpeg is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with FFmpeg; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| 
 | |
| #include "libavutil/avassert.h"
 | |
| #include "libavutil/intreadwrite.h"
 | |
| #include "libavutil/mem_internal.h"
 | |
| 
 | |
| #include "avcodec.h"
 | |
| #include "codec_internal.h"
 | |
| #include "decode.h"
 | |
| #define CACHED_BITSTREAM_READER !ARCH_X86_32
 | |
| #include "golomb.h"
 | |
| #include "get_bits.h"
 | |
| #include "idctdsp.h"
 | |
| #include "thread.h"
 | |
| 
 | |
| typedef struct SliceContext {
 | |
|     const uint8_t *dc_ptr;
 | |
|     const uint8_t *ac_ptr;
 | |
|     unsigned dc_size;
 | |
|     unsigned ac_size;
 | |
| } SliceContext;
 | |
| 
 | |
| typedef struct VMIXContext {
 | |
|     int nb_slices;
 | |
| 
 | |
|     int16_t factors[64];
 | |
|     uint8_t scan[64];
 | |
| 
 | |
|     SliceContext slices[255];
 | |
| 
 | |
|     IDCTDSPContext idsp;
 | |
| } VMIXContext;
 | |
| 
 | |
| static const uint8_t quality[25] = {
 | |
|      1,  2,  3,  4,  5,  6,  7,  8, 10, 12, 14, 16,
 | |
|     18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 52, 56, 64,
 | |
| };
 | |
| 
 | |
| static const uint8_t quant[64] = {
 | |
|     16, 16, 19, 22, 22, 26, 26, 27,
 | |
|     16, 16, 22, 22, 26, 27, 27, 29,
 | |
|     19, 22, 26, 26, 27, 29, 29, 35,
 | |
|     22, 24, 27, 27, 29, 32, 34, 38,
 | |
|     26, 27, 29, 29, 32, 35, 38, 46,
 | |
|     27, 29, 34, 34, 35, 40, 46, 56,
 | |
|     29, 34, 34, 37, 40, 48, 56, 69,
 | |
|     34, 37, 38, 40, 48, 58, 69, 83,
 | |
| };
 | |
| 
 | |
| static av_cold int decode_init(AVCodecContext *avctx)
 | |
| {
 | |
|     VMIXContext *s = avctx->priv_data;
 | |
| 
 | |
|     avctx->bits_per_raw_sample = 8;
 | |
|     avctx->pix_fmt = AV_PIX_FMT_YUV422P;
 | |
| 
 | |
|     avctx->coded_width = FFALIGN(avctx->width, 16);
 | |
|     avctx->coded_height = FFALIGN(avctx->height, 16);
 | |
| 
 | |
|     ff_idctdsp_init(&s->idsp, avctx);
 | |
|     ff_permute_scantable(s->scan, ff_zigzag_direct,
 | |
|                          s->idsp.idct_permutation);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static inline int get_se_golomb_vmix(GetBitContext *gb)
 | |
| {
 | |
|     unsigned int buf = get_ue_golomb_long(gb);
 | |
|     int sign = (buf & 1) - 1;
 | |
|     return ((buf >> 1) ^ (~sign));
 | |
| }
 | |
| 
 | |
| static int decode_dcac(AVCodecContext *avctx,
 | |
|                        GetBitContext *dc_gb, GetBitContext *ac_gb,
 | |
|                        unsigned *dcrun, unsigned *acrun,
 | |
|                        AVFrame *frame, int width, int by, int plane)
 | |
| {
 | |
|     const ptrdiff_t linesize = frame->linesize[plane];
 | |
|     uint8_t *dst = frame->data[plane] + by * linesize;
 | |
|     unsigned dc_run = *dcrun, ac_run = *acrun;
 | |
|     LOCAL_ALIGNED_32(int16_t, block, [64]);
 | |
|     VMIXContext *s = avctx->priv_data;
 | |
|     const int16_t *factors = s->factors;
 | |
|     const uint8_t *scan = s->scan;
 | |
|     const int add = plane ? 0 : 1024;
 | |
|     int i, dc_v = 0, ac_v = 0, dc = 0;
 | |
| 
 | |
|     for (int y = 0; y < 2; y++) {
 | |
|         for (int x = 0; x < width; x += 8) {
 | |
|             memset(block, 0, sizeof(*block)*64);
 | |
| 
 | |
|             if (dc_run > 0) {
 | |
|                 dc_run--;
 | |
|             } else {
 | |
|                 dc_v = get_se_golomb_vmix(dc_gb);
 | |
|                 dc += (unsigned)dc_v;
 | |
|                 if (!dc_v)
 | |
|                     dc_run = get_ue_golomb_long(dc_gb);
 | |
|             }
 | |
| 
 | |
|             for (int n = 0; n < 64; n++) {
 | |
|                 if (ac_run > 0) {
 | |
|                     ac_run--;
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 ac_v = get_se_golomb_vmix(ac_gb);
 | |
|                 i = scan[n];
 | |
|                 block[i] = ((unsigned)ac_v * factors[i]) >> 4;
 | |
|                 if (!ac_v)
 | |
|                     ac_run = get_ue_golomb_long(ac_gb);
 | |
|             }
 | |
| 
 | |
|             block[0] = dc + add;
 | |
|             s->idsp.idct_put(dst + x, linesize, block);
 | |
|         }
 | |
| 
 | |
|         dst += 8 * linesize;
 | |
|     }
 | |
| 
 | |
|     *dcrun = dc_run;
 | |
|     *acrun = ac_run;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int decode_slice(AVCodecContext *avctx, AVFrame *frame,
 | |
|                         const uint8_t *dc_src, unsigned dc_slice_size,
 | |
|                         const uint8_t *ac_src, unsigned ac_slice_size,
 | |
|                         int by)
 | |
| {
 | |
|     unsigned dc_run = 0, ac_run = 0;
 | |
|     GetBitContext dc_gb, ac_gb;
 | |
|     int ret;
 | |
| 
 | |
|     ret = init_get_bits8(&dc_gb, dc_src, dc_slice_size);
 | |
|     if (ret < 0)
 | |
|         return ret;
 | |
| 
 | |
|     ret = init_get_bits8(&ac_gb, ac_src, ac_slice_size);
 | |
|     if (ret < 0)
 | |
|         return ret;
 | |
| 
 | |
|     for (int p = 0; p < 3; p++) {
 | |
|         const int rshift = !!p;
 | |
|         ret = decode_dcac(avctx, &dc_gb, &ac_gb,
 | |
|                           &dc_run, &ac_run, frame,
 | |
|                           frame->width >> rshift, by, p);
 | |
|         if (ret < 0)
 | |
|             return ret;
 | |
| 
 | |
|         if (get_bits_left(&dc_gb) < 0)
 | |
|             return AVERROR_INVALIDDATA;
 | |
|         if (get_bits_left(&ac_gb) < 0)
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         align_get_bits(&dc_gb);
 | |
|         align_get_bits(&ac_gb);
 | |
|     }
 | |
| 
 | |
|     if (get_bits_left(&dc_gb) > 0)
 | |
|         return AVERROR_INVALIDDATA;
 | |
|     if (get_bits_left(&ac_gb) > 0)
 | |
|         return AVERROR_INVALIDDATA;
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int decode_slices(AVCodecContext *avctx, void *arg,
 | |
|                          int n, int thread_nb)
 | |
| {
 | |
|     VMIXContext *s = avctx->priv_data;
 | |
|     const uint8_t *dc_slice_ptr = s->slices[n].dc_ptr;
 | |
|     const uint8_t *ac_slice_ptr = s->slices[n].ac_ptr;
 | |
|     unsigned dc_slice_size = s->slices[n].dc_size;
 | |
|     unsigned ac_slice_size = s->slices[n].ac_size;
 | |
|     AVFrame *frame = arg;
 | |
| 
 | |
|     return decode_slice(avctx, frame, dc_slice_ptr, dc_slice_size,
 | |
|                         ac_slice_ptr, ac_slice_size, n * 16);
 | |
| }
 | |
| 
 | |
| static int decode_frame(AVCodecContext *avctx,
 | |
|                         AVFrame *frame, int *got_frame,
 | |
|                         AVPacket *avpkt)
 | |
| {
 | |
|     VMIXContext *s = avctx->priv_data;
 | |
|     unsigned offset = 3, q;
 | |
|     int ret;
 | |
| 
 | |
|     if (avpkt->size <= 7)
 | |
|         return AVERROR_INVALIDDATA;
 | |
| 
 | |
|     if (avpkt->data[0] != 0x01)
 | |
|         return AVERROR_INVALIDDATA;
 | |
| 
 | |
|     q = av_clip(99 - av_clip(avpkt->data[1], 0, 99), 0, FF_ARRAY_ELEMS(quality) - 1);
 | |
|     for (int n = 0; n < 64; n++)
 | |
|         s->factors[n] = quant[n] * quality[q];
 | |
| 
 | |
|     s->nb_slices = avpkt->data[2];
 | |
|     if (!s->nb_slices || s->nb_slices > (avctx->height + 15) / 16)
 | |
|         return AVERROR_INVALIDDATA;
 | |
| 
 | |
|     for (int n = 0; n < s->nb_slices; n++) {
 | |
|         unsigned slice_size;
 | |
| 
 | |
|         if (offset + 4 > avpkt->size)
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         slice_size = AV_RL32(avpkt->data + offset);
 | |
|         if (slice_size > avpkt->size)
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         if (avpkt->size - slice_size - 4LL < offset)
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         s->slices[n].dc_size = slice_size;
 | |
|         s->slices[n].dc_ptr = avpkt->data + offset + 4;
 | |
|         offset += slice_size + 4;
 | |
|     }
 | |
| 
 | |
|     for (int n = 0; n < s->nb_slices; n++) {
 | |
|         unsigned slice_size;
 | |
| 
 | |
|         if (offset + 4 > avpkt->size)
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         slice_size = AV_RL32(avpkt->data + offset);
 | |
|         if (slice_size > avpkt->size)
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         if (avpkt->size - slice_size - 4LL < offset)
 | |
|             return AVERROR_INVALIDDATA;
 | |
| 
 | |
|         s->slices[n].ac_size = slice_size;
 | |
|         s->slices[n].ac_ptr = avpkt->data + offset + 4;
 | |
|         offset += slice_size + 4;
 | |
|     }
 | |
| 
 | |
|     ret = ff_thread_get_buffer(avctx, frame, 0);
 | |
|     if (ret < 0)
 | |
|         return ret;
 | |
| 
 | |
|     avctx->execute2(avctx, decode_slices, frame, NULL, s->nb_slices);
 | |
| 
 | |
|     frame->pict_type = AV_PICTURE_TYPE_I;
 | |
|     frame->flags |= AV_FRAME_FLAG_KEY;
 | |
| 
 | |
|     *got_frame = 1;
 | |
| 
 | |
|     return avpkt->size;
 | |
| }
 | |
| 
 | |
| const FFCodec ff_vmix_decoder = {
 | |
|     .p.name           = "vmix",
 | |
|     CODEC_LONG_NAME("vMix Video"),
 | |
|     .p.type           = AVMEDIA_TYPE_VIDEO,
 | |
|     .p.id             = AV_CODEC_ID_VMIX,
 | |
|     .priv_data_size   = sizeof(VMIXContext),
 | |
|     .init             = decode_init,
 | |
|     FF_CODEC_DECODE_CB(decode_frame),
 | |
|     .p.capabilities   = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS |
 | |
|                         AV_CODEC_CAP_SLICE_THREADS,
 | |
| };
 |