From patchwork Fri Oct 28 21:02:21 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Niedermayer X-Patchwork-Id: 1218 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.133 with SMTP id o127csp1282215vsd; Fri, 28 Oct 2016 14:02:33 -0700 (PDT) X-Received: by 10.28.145.85 with SMTP id t82mr559716wmd.40.1477688553888; Fri, 28 Oct 2016 14:02:33 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id d69si12037760wmd.45.2016.10.28.14.02.33; Fri, 28 Oct 2016 14:02:33 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EB01E689F3B; Sat, 29 Oct 2016 00:02:27 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from vie01a-dmta-pe02-1.mx.upcmail.net (vie01a-dmta-pe02-1.mx.upcmail.net [62.179.121.157]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 9C4D5689E5C for ; Sat, 29 Oct 2016 00:02:21 +0300 (EEST) Received: from [172.31.216.43] (helo=vie01a-pemc-psmtp-pe01) by vie01a-dmta-pe02.mx.upcmail.net with esmtp (Exim 4.87) (envelope-from ) id 1c0EIB-0000rx-Rl for ffmpeg-devel@ffmpeg.org; Fri, 28 Oct 2016 23:02:23 +0200 Received: from localhost ([213.47.41.20]) by vie01a-pemc-psmtp-pe01 with SMTP @ mailcloud.upcmail.net id 192N1u00X0S5wYM0192Plu; Fri, 28 Oct 2016 23:02:23 +0200 X-SourceIP: 213.47.41.20 From: Michael Niedermayer To: FFmpeg development discussions and patches Date: Fri, 28 Oct 2016 23:02:21 +0200 Message-Id: <20161028210221.5722-1-michael@niedermayer.cc> X-Mailer: git-send-email 2.10.1 Subject: [FFmpeg-devel] [PATCH] doc/examples: add fuzz target for individual ffmpeg APIs for in-process fuzzing with libFuzzer, AFL, and similar fuzzing engines. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Thomas Garnier MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Thomas Garnier Signed-off-by: Michael Niedermayer --- doc/examples/decoder_targeted.c | 183 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 doc/examples/decoder_targeted.c diff --git a/doc/examples/decoder_targeted.c b/doc/examples/decoder_targeted.c new file mode 100644 index 0000000..72e2c51 --- /dev/null +++ b/doc/examples/decoder_targeted.c @@ -0,0 +1,183 @@ +/* Targeted fuzzer that targets specific codecs depending on two + compile-time flags. + INSTRUCTIONS: + + * Get the very fresh clang, e.g. see http://libfuzzer.info#versions + * Get and build libFuzzer: + svn co http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer + ./Fuzzer/build.sh + * build ffmpeg for fuzzing: + FLAGS="-fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp -g" CC="clang $FLAGS" CXX="clang++ $FLAGS" ./configure --disable-yasm + make clean && make -j + * 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 doc/examples/decoder_targeted.c -o decoder_targeted -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 + * create a corpus directory and put some samples there (empty dir is ok too): + mkdir CORPUS && cp some-files CORPUS + + * Run fuzzing: + ./decoder_targeted -max_len=100000 CORPUS + + More info: + http://libfuzzer.info + http://tutorial.libfuzzer.info + https://github.com/google/oss-fuzz + http://lcamtuf.coredump.cx/afl/ + https://security.googleblog.com/2016/08/guided-in-process-fuzzing-of-chrome.html +*/ + +#include "libavutil/avassert.h" + +#include "libavcodec/avcodec.h" +#include "libavformat/avformat.h" + +static void error(const char *err) +{ + fprintf(stderr, "%s", err); + exit(1); +} + +static AVCodec *c = NULL; +static AVCodec *AVCodecInitialize(enum AVCodecID codec_id) +{ + AVCodec *res; + av_register_all(); + av_log_set_level(AV_LOG_PANIC); + res = avcodec_find_decoder(codec_id); + if (!res) + error("Failed to find decoder"); + return res; +} + +#if defined(FUZZ_FFMPEG_VIDEO) +#define decode_handler avcodec_decode_video2 +#elif defined(FUZZ_FFMPEG_AUDIO) +#define decode_handler avcodec_decode_audio4 +#elif defined(FUZZ_FFMPEG_SUBTITLE) +static int subtitle_handler(AVCodecContext *avctx, void *frame, + int *got_sub_ptr, AVPacket *avpkt) +{ + AVSubtitle sub; + int ret = avcodec_decode_subtitle2(avctx, &sub, got_sub_ptr, avpkt); + if (ret >= 0 && *got_sub_ptr) + avsubtitle_free(&sub); + return ret; +} + +#define decode_handler subtitle_handler +#else +#error "Specify encoder type" // To catch mistakes +#endif + +// Class to handle buffer allocation and resize for each frame +typedef struct FuzzDataBuffer { + size_t size_; + uint8_t *data_; +} FuzzDataBuffer; + +void FDBCreate(FuzzDataBuffer *FDB) { + FDB->size_ = 0x1000; + FDB->data_ = av_malloc(FDB->size_); + if (!FDB->data_) + error("Failed memory allocation"); +} + +void FDBDesroy(FuzzDataBuffer *FDB) { av_free(FDB->data_); } + +void FDBRealloc(FuzzDataBuffer *FDB, size_t size) { + size_t needed = size + FF_INPUT_BUFFER_PADDING_SIZE; + av_assert0(needed > size); + if (needed > FDB->size_) { + av_free(FDB->data_); + FDB->size_ = needed; + FDB->data_ = av_malloc(FDB->size_); + if (!FDB->data_) + error("Failed memory allocation"); + } +} + +void FDBPrepare(FuzzDataBuffer *FDB, AVPacket *dst, const uint8_t *data, + size_t size) +{ + FDBRealloc(FDB, size); + memcpy(FDB->data_, data, size); + size_t padd = FDB->size_ - size; + if (padd > FF_INPUT_BUFFER_PADDING_SIZE) + padd = FF_INPUT_BUFFER_PADDING_SIZE; + memset(FDB->data_ + size, 0, padd); + av_init_packet(dst); + dst->data = FDB->data_; + dst->size = size; +} + +// Ensure we don't loop forever +const uint32_t maxiteration = 8096; + +static const uint64_t FUZZ_TAG = 0x4741542D5A5A5546ULL; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + const uint64_t fuzz_tag = FUZZ_TAG; + FuzzDataBuffer buffer; + const uint8_t *last = data; + const uint8_t *end = data + size; + uint32_t it = 0; + + if (!c) + c = AVCodecInitialize(FFMPEG_CODEC); // Done once. + + AVCodecContext* ctx = avcodec_alloc_context3(NULL); + if (!ctx) + error("Failed memory allocation"); + int res = avcodec_open2(ctx, c, NULL); + if (res < 0) + return res; + + FDBCreate(&buffer); + int got_frame; + AVFrame *frame = av_frame_alloc(); + if (!frame) + error("Failed memory allocation"); + + // Read very simple container + AVPacket avpkt; + while (data < end && it < maxiteration) { + // Search for the TAG + while (data + sizeof(fuzz_tag) < end) { + if (data[0] == (fuzz_tag & 0xFF) && *(const uint64_t *)(data) == fuzz_tag) + break; + data++; + } + if (data + sizeof(fuzz_tag) > end) + data = end; + + FDBPrepare(&buffer, &avpkt, last, data - last); + data += sizeof(fuzz_tag); + last = data; + + // Iterate through all data + while (avpkt.size > 0 && it++ < maxiteration) { + av_frame_unref(frame); + int ret = decode_handler(ctx, frame, &got_frame, &avpkt); + if (ret <= 0 || ret > avpkt.size) + break; + avpkt.data += ret; + avpkt.size -= ret; + } + } + + av_init_packet(&avpkt); + avpkt.data = NULL; + avpkt.size = 0; + + do { + got_frame = 0; + decode_handler(ctx, frame, &got_frame, &avpkt); + } while (got_frame == 1 && it++ < maxiteration); + + av_frame_free(&frame); + avcodec_free_context(&ctx); + av_freep(&ctx); + FDBDesroy(&buffer); + return 0; +}