X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=tools%2Ftarget_dec_fuzzer.c;h=fad44a4101cc9e59621ddcb179f8fbab4caf8081;hb=420cedd49745b284c35d97b936b71ff79b43bdf7;hp=b131a5f2b62eb5e9a1902b026f5baa1207733d44;hpb=87a7fc692d8bff99879dc4a27d89e70272b5a79f;p=ffmpeg diff --git a/tools/target_dec_fuzzer.c b/tools/target_dec_fuzzer.c index b131a5f2b62..fad44a4101c 100644 --- a/tools/target_dec_fuzzer.c +++ b/tools/target_dec_fuzzer.c @@ -30,7 +30,7 @@ * build the fuzz target. Choose the value of FFMPEG_CODEC (e.g. AV_CODEC_ID_DVD_SUBTITLE) and choose one of FUZZ_FFMPEG_VIDEO, FUZZ_FFMPEG_AUDIO, FUZZ_FFMPEG_SUBTITLE. - clang -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp tools/target_dec_fuzzer.c -o target_dec_fuzzer -I. -DFFMPEG_CODEC=AV_CODEC_ID_MPEG1VIDEO -DFUZZ_FFMPEG_VIDEO ../../libfuzzer/libFuzzer.a -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample -Wl,--as-needed -Wl,-z,noexecstack -Wl,--warn-common -Wl,-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil:libavresample -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -ldl -lxcb -lxcb-shm -lxcb -lxcb-xfixes -lxcb -lxcb-shape -lxcb -lX11 -lasound -lm -lbz2 -lz -pthread + clang -fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp tools/target_dec_fuzzer.c -o target_dec_fuzzer -I. -DFFMPEG_CODEC=AV_CODEC_ID_MPEG1VIDEO -DFUZZ_FFMPEG_VIDEO ../../libfuzzer/libFuzzer.a -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavutil -Llibpostproc -Llibswscale -Llibswresample -Wl,--as-needed -Wl,-z,noexecstack -Wl,--warn-common -Wl,-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -ldl -lxcb -lxcb-shm -lxcb -lxcb-xfixes -lxcb -lxcb-shape -lxcb -lX11 -lasound -lm -lbz2 -lz -pthread * create a corpus directory and put some samples there (empty dir is ok too): mkdir CORPUS && cp some-files CORPUS @@ -88,11 +88,22 @@ static int subtitle_handler(AVCodecContext *avctx, void *frame, return ret; } +static int audio_video_handler(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, const AVPacket *dummy) +{ + int ret = avcodec_receive_frame(avctx, frame); + *got_frame = ret >= 0; + return ret; +} + // Ensure we don't loop forever const uint32_t maxiteration = 8096; -const uint64_t maxpixels_per_frame = 4096 * 4096; +uint64_t maxpixels_per_frame = 4096 * 4096; uint64_t maxpixels; +uint64_t maxsamples_per_frame = 256*1024*32; +uint64_t maxsamples; + static const uint64_t FUZZ_TAG = 0x4741542D5A5A5546ULL; int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { @@ -101,11 +112,14 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { const uint8_t *end = data + size; uint32_t it = 0; uint64_t ec_pixels = 0; + uint64_t nb_samples = 0; int (*decode_handler)(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, const AVPacket *avpkt) = NULL; AVCodecParserContext *parser = NULL; - + uint64_t keyframes = 0; + uint64_t flushpattern = -1; + AVDictionary *opts = NULL; if (!c) { #ifdef FFMPEG_DECODER @@ -113,42 +127,89 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { #define DECODER_SYMBOL(CODEC) DECODER_SYMBOL0(CODEC) extern AVCodec DECODER_SYMBOL(FFMPEG_DECODER); codec_list[0] = &DECODER_SYMBOL(FFMPEG_DECODER); - avcodec_register(&DECODER_SYMBOL(FFMPEG_DECODER)); + +#if FFMPEG_DECODER == tiff || FFMPEG_DECODER == tdsc + extern AVCodec DECODER_SYMBOL(mjpeg); + codec_list[1] = &DECODER_SYMBOL(mjpeg); +#endif c = &DECODER_SYMBOL(FFMPEG_DECODER); #else - avcodec_register_all(); c = AVCodecInitialize(FFMPEG_CODEC); // Done once. #endif av_log_set_level(AV_LOG_PANIC); } switch (c->type) { - case AVMEDIA_TYPE_AUDIO : decode_handler = avcodec_decode_audio4; break; - case AVMEDIA_TYPE_VIDEO : decode_handler = avcodec_decode_video2; break; + case AVMEDIA_TYPE_AUDIO : + case AVMEDIA_TYPE_VIDEO : decode_handler = audio_video_handler ; break; case AVMEDIA_TYPE_SUBTITLE: decode_handler = subtitle_handler ; break; } + switch (c->id) { + case AV_CODEC_ID_APE: maxsamples_per_frame /= 256; break; + } maxpixels = maxpixels_per_frame * maxiteration; + maxsamples = maxsamples_per_frame * maxiteration; switch (c->id) { - // Allows a small input to generate gigantic output - case AV_CODEC_ID_BINKVIDEO: maxpixels /= 32; break; - case AV_CODEC_ID_DIRAC: maxpixels /= 8192; break; - case AV_CODEC_ID_MSRLE: maxpixels /= 16; break; - case AV_CODEC_ID_QTRLE: maxpixels /= 16; break; - case AV_CODEC_ID_SANM: maxpixels /= 16; break; - case AV_CODEC_ID_GIF: maxpixels /= 16; break; - // Performs slow frame rescaling in C - case AV_CODEC_ID_GDV: maxpixels /= 512; break; - // Postprocessing in C - case AV_CODEC_ID_HNM4_VIDEO:maxpixels /= 128; break; - // Cliping in C, generally slow even with small input - case AV_CODEC_ID_INDEO4: maxpixels /= 128; break; - case AV_CODEC_ID_LSCR: maxpixels /= 16; break; - case AV_CODEC_ID_MOTIONPIXELS:maxpixels /= 256; break; - case AV_CODEC_ID_SNOW: maxpixels /= 128; break; - case AV_CODEC_ID_TRUEMOTION2: maxpixels /= 1024; break; + case AV_CODEC_ID_AGM: maxpixels /= 1024; break; + case AV_CODEC_ID_ARBC: maxpixels /= 1024; break; + case AV_CODEC_ID_BINKVIDEO: maxpixels /= 32; break; + case AV_CODEC_ID_CFHD: maxpixels /= 128; break; + case AV_CODEC_ID_COOK: maxsamples /= 1<<20; break; + case AV_CODEC_ID_DIRAC: maxpixels /= 8192; break; + case AV_CODEC_ID_DST: maxsamples /= 1<<20; break; + case AV_CODEC_ID_DVB_SUBTITLE: av_dict_set_int(&opts, "compute_clut", -2, 0); break; + case AV_CODEC_ID_DXV: maxpixels /= 32; break; + case AV_CODEC_ID_FFWAVESYNTH: maxsamples /= 16384; break; + case AV_CODEC_ID_FLAC: maxsamples /= 1024; break; + case AV_CODEC_ID_FLV1: maxpixels /= 1024; break; + case AV_CODEC_ID_G2M: maxpixels /= 1024; break; + case AV_CODEC_ID_GDV: maxpixels /= 512; break; + case AV_CODEC_ID_GIF: maxpixels /= 16; break; + case AV_CODEC_ID_H264: maxpixels /= 256; break; + case AV_CODEC_ID_HAP: maxpixels /= 128; break; + case AV_CODEC_ID_HEVC: maxpixels /= 16384; break; + case AV_CODEC_ID_HNM4_VIDEO: maxpixels /= 128; break; + case AV_CODEC_ID_IFF_ILBM: maxpixels /= 128; break; + case AV_CODEC_ID_INDEO4: maxpixels /= 128; break; + case AV_CODEC_ID_INTERPLAY_ACM: maxsamples /= 16384; break; + case AV_CODEC_ID_LAGARITH: maxpixels /= 1024; break; + case AV_CODEC_ID_LSCR: maxpixels /= 16; break; + case AV_CODEC_ID_MOTIONPIXELS:maxpixels /= 256; break; + case AV_CODEC_ID_MP4ALS: maxsamples /= 65536; break; + case AV_CODEC_ID_MSA1: maxpixels /= 16384; break; + case AV_CODEC_ID_MSRLE: maxpixels /= 16; break; + case AV_CODEC_ID_MSS2: maxpixels /= 16384; break; + case AV_CODEC_ID_MSZH: maxpixels /= 128; break; + case AV_CODEC_ID_OPUS: maxsamples /= 16384; break; + case AV_CODEC_ID_PNG: maxpixels /= 128; break; + case AV_CODEC_ID_APNG: maxpixels /= 128; break; + case AV_CODEC_ID_QTRLE: maxpixels /= 16; break; + case AV_CODEC_ID_PAF_VIDEO: maxpixels /= 16; break; + case AV_CODEC_ID_RASC: maxpixels /= 16; break; + case AV_CODEC_ID_SANM: maxpixels /= 16; break; + case AV_CODEC_ID_SCPR: maxpixels /= 32; break; + case AV_CODEC_ID_SCREENPRESSO:maxpixels /= 64; break; + case AV_CODEC_ID_SMACKVIDEO: maxpixels /= 64; break; + case AV_CODEC_ID_SNOW: maxpixels /= 128; break; + case AV_CODEC_ID_TGV: maxpixels /= 32; break; + case AV_CODEC_ID_THEORA: maxpixels /= 1024; break; + case AV_CODEC_ID_TRUEMOTION2: maxpixels /= 1024; break; + case AV_CODEC_ID_TSCC: maxpixels /= 1024; break; + case AV_CODEC_ID_VC1IMAGE: maxpixels /= 8192; break; + case AV_CODEC_ID_VMNC: maxpixels /= 8192; break; + case AV_CODEC_ID_VP4: maxpixels /= 4096; break; + case AV_CODEC_ID_VP7: maxpixels /= 256; break; + case AV_CODEC_ID_VP9: maxpixels /= 4096; break; + case AV_CODEC_ID_WAVPACK: maxsamples /= 1024; break; + case AV_CODEC_ID_WMV3IMAGE: maxpixels /= 8192; break; + case AV_CODEC_ID_WS_VQA: maxpixels /= 16384; break; + case AV_CODEC_ID_WMALOSSLESS: maxsamples /= 1024; break; + case AV_CODEC_ID_ZEROCODEC: maxpixels /= 128; break; } + maxsamples_per_frame = FFMIN(maxsamples_per_frame, maxsamples); + maxpixels_per_frame = FFMIN(maxpixels_per_frame , maxpixels); AVCodecContext* ctx = avcodec_alloc_context3(c); AVCodecContext* parser_avctx = avcodec_alloc_context3(NULL); @@ -157,11 +218,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (ctx->max_pixels == 0 || ctx->max_pixels > maxpixels_per_frame) ctx->max_pixels = maxpixels_per_frame; //To reduce false positive OOM and hangs - ctx->refcounted_frames = 1; //To reduce false positive timeouts and focus testing on the refcounted API + + ctx->max_samples = maxsamples_per_frame; if (size > 1024) { GetByteContext gbc; int extradata_size; + int flags; size -= 1024; bytestream2_init(&gbc, data + size, 1024); ctx->width = bytestream2_get_le32(&gbc); @@ -169,13 +232,51 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ctx->bit_rate = bytestream2_get_le64(&gbc); ctx->bits_per_coded_sample = bytestream2_get_le32(&gbc); // Try to initialize a parser for this codec, note, this may fail which just means we test without one - if (bytestream2_get_byte(&gbc) & 1) + flags = bytestream2_get_byte(&gbc); + if (flags & 1) parser = av_parser_init(c->id); + if (flags & 2) + ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; + if (flags & 4) { + ctx->err_recognition = AV_EF_AGGRESSIVE | AV_EF_COMPLIANT | AV_EF_CAREFUL; + if (flags & 8) + ctx->err_recognition |= AV_EF_EXPLODE; + } + if ((flags & 0x10) && c->id != AV_CODEC_ID_H264) + ctx->flags2 |= AV_CODEC_FLAG2_FAST; + + if (flags & 0x40) + av_force_cpu_flags(0); extradata_size = bytestream2_get_le32(&gbc); - ctx->sample_rate = bytestream2_get_le32(&gbc); + ctx->sample_rate = bytestream2_get_le32(&gbc) & 0x7FFFFFFF; ctx->channels = (unsigned)bytestream2_get_le32(&gbc) % FF_SANE_NB_CHANNELS; + ctx->block_align = bytestream2_get_le32(&gbc) & 0x7FFFFFFF; + ctx->codec_tag = bytestream2_get_le32(&gbc); + if (c->codec_tags) { + int n; + for (n = 0; c->codec_tags[n] != FF_CODEC_TAGS_END; n++); + ctx->codec_tag = c->codec_tags[ctx->codec_tag % n]; + } + keyframes = bytestream2_get_le64(&gbc); + ctx->request_channel_layout = bytestream2_get_le64(&gbc); + + ctx->idct_algo = bytestream2_get_byte(&gbc) % 25; + flushpattern = bytestream2_get_le64(&gbc); + + if (flags & 0x20) { + switch (ctx->codec_id) { + case AV_CODEC_ID_AC3: + case AV_CODEC_ID_EAC3: + av_dict_set_int(&opts, "cons_noisegen", bytestream2_get_byte(&gbc) & 1, 0); + av_dict_set_int(&opts, "heavy_compr", bytestream2_get_byte(&gbc) & 1, 0); + av_dict_set_int(&opts, "target_level", (int)(bytestream2_get_byte(&gbc) % 32) - 31, 0); + av_dict_set_int(&opts, "dmix_mode", (int)(bytestream2_get_byte(&gbc) % 4) - 1, 0); + break; + } + } + if (extradata_size < size) { ctx->extradata = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); @@ -189,24 +290,24 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { ctx->width = ctx->height = 0; } - int res = avcodec_open2(ctx, c, NULL); + int res = avcodec_open2(ctx, c, &opts); if (res < 0) { avcodec_free_context(&ctx); av_free(parser_avctx); av_parser_close(parser); + av_dict_free(&opts); return 0; // Failure of avcodec_open2() does not imply that a issue was found } parser_avctx->codec_id = ctx->codec_id; int got_frame; AVFrame *frame = av_frame_alloc(); - if (!frame) + AVPacket *avpkt = av_packet_alloc(); + AVPacket *parsepkt = av_packet_alloc(); + if (!frame || !avpkt || !parsepkt) error("Failed memory allocation"); // Read very simple container - AVPacket avpkt, parsepkt; - av_init_packet(&avpkt); - av_init_packet(&parsepkt); while (data < end && it < maxiteration) { // Search for the TAG while (data + sizeof(fuzz_tag) < end) { @@ -217,80 +318,116 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (data + sizeof(fuzz_tag) > end) data = end; - res = av_new_packet(&parsepkt, data - last); + res = av_new_packet(parsepkt, data - last); if (res < 0) error("Failed memory allocation"); - memcpy(parsepkt.data, last, data - last); + memcpy(parsepkt->data, last, data - last); + parsepkt->flags = (keyframes & 1) * AV_PKT_FLAG_DISCARD + (!!(keyframes & 2)) * AV_PKT_FLAG_KEY; + keyframes = (keyframes >> 2) + (keyframes<<62); data += sizeof(fuzz_tag); last = data; - while (parsepkt.size > 0) { + while (parsepkt->size > 0) { + int decode_more; if (parser) { - av_init_packet(&avpkt); - int ret = av_parser_parse2(parser, parser_avctx, &avpkt.data, &avpkt.size, - parsepkt.data, parsepkt.size, - parsepkt.pts, parsepkt.dts, parsepkt.pos); - if (avpkt.data == parsepkt.data) { - avpkt.buf = av_buffer_ref(parsepkt.buf); - if (!avpkt.buf) + int ret = av_parser_parse2(parser, parser_avctx, &avpkt->data, &avpkt->size, + parsepkt->data, parsepkt->size, + parsepkt->pts, parsepkt->dts, parsepkt->pos); + if (avpkt->data == parsepkt->data) { + avpkt->buf = av_buffer_ref(parsepkt->buf); + if (!avpkt->buf) error("Failed memory allocation"); } else { - if (av_packet_make_refcounted(&avpkt) < 0) + if (av_packet_make_refcounted(avpkt) < 0) error("Failed memory allocation"); } - parsepkt.data += ret; - parsepkt.size -= ret; - parsepkt.pos += ret; - avpkt.pts = parser->pts; - avpkt.dts = parser->dts; - avpkt.pos = parser->pos; + parsepkt->data += ret; + parsepkt->size -= ret; + parsepkt->pos += ret; + avpkt->pts = parser->pts; + avpkt->dts = parser->dts; + avpkt->pos = parser->pos; if ( parser->key_frame == 1 || (parser->key_frame == -1 && parser->pict_type == AV_PICTURE_TYPE_I)) - avpkt.flags |= AV_PKT_FLAG_KEY; - avpkt.flags |= parsepkt.flags & AV_PKT_FLAG_DISCARD; + avpkt->flags |= AV_PKT_FLAG_KEY; + avpkt->flags |= parsepkt->flags & AV_PKT_FLAG_DISCARD; } else { - av_packet_move_ref(&avpkt, &parsepkt); + av_packet_move_ref(avpkt, parsepkt); } + if (!(flushpattern & 7)) + avcodec_flush_buffers(ctx); + flushpattern = (flushpattern >> 3) + (flushpattern << 61); + + if (ctx->codec_type != AVMEDIA_TYPE_SUBTITLE) { + int ret = avcodec_send_packet(ctx, avpkt); + decode_more = ret >= 0; + if(!decode_more) { + ec_pixels += (ctx->width + 32LL) * (ctx->height + 32LL); + if (it > 20 || ec_pixels > 4 * ctx->max_pixels) + ctx->error_concealment = 0; + if (ec_pixels > maxpixels) + goto maximums_reached; + } + } else + decode_more = 1; + // Iterate through all data - while (avpkt.size > 0 && it++ < maxiteration) { + while (decode_more && it++ < maxiteration) { av_frame_unref(frame); - int ret = decode_handler(ctx, frame, &got_frame, &avpkt); + int ret = decode_handler(ctx, frame, &got_frame, avpkt); - ec_pixels += ctx->width * ctx->height; + ec_pixels += (ctx->width + 32LL) * (ctx->height + 32LL); if (it > 20 || ec_pixels > 4 * ctx->max_pixels) ctx->error_concealment = 0; if (ec_pixels > maxpixels) goto maximums_reached; - if (ret <= 0 || ret > avpkt.size) + if (ctx->codec_type == AVMEDIA_TYPE_AUDIO && + frame->nb_samples == 0 && !got_frame && + (avpkt->flags & AV_PKT_FLAG_DISCARD)) + nb_samples += ctx->max_samples; + + nb_samples += frame->nb_samples; + if (nb_samples > maxsamples) + goto maximums_reached; + + if (ret <= 0 || ret > avpkt->size) break; - if (ctx->codec_type != AVMEDIA_TYPE_AUDIO) - ret = avpkt.size; - avpkt.data += ret; - avpkt.size -= ret; + + if (ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { + avpkt->data += ret; + avpkt->size -= ret; + decode_more = avpkt->size > 0; + } else + decode_more = ret >= 0; } - av_packet_unref(&avpkt); + av_packet_unref(avpkt); } - av_packet_unref(&parsepkt); + av_packet_unref(parsepkt); } maximums_reached: - av_packet_unref(&avpkt); + av_packet_unref(avpkt); + + if (ctx->codec_type != AVMEDIA_TYPE_SUBTITLE) + avcodec_send_packet(ctx, NULL); do { got_frame = 0; av_frame_unref(frame); - decode_handler(ctx, frame, &got_frame, &avpkt); + decode_handler(ctx, frame, &got_frame, avpkt); } while (got_frame == 1 && it++ < maxiteration); - fprintf(stderr, "pixels decoded: %"PRId64", iterations: %d\n", ec_pixels, it); + fprintf(stderr, "pixels decoded: %"PRId64", samples decoded: %"PRId64", iterations: %d\n", ec_pixels, nb_samples, it); av_frame_free(&frame); avcodec_free_context(&ctx); avcodec_free_context(&parser_avctx); av_parser_close(parser); - av_packet_unref(&parsepkt); + av_packet_free(&avpkt); + av_packet_free(&parsepkt); + av_dict_free(&opts); return 0; }