Name and purpose are more appropriate there since the code isn't an ideal example.
Showing the changes to tools (1 of 1 file). Show all
Unified · Split
| @@ -0,0 +1,208 @@ | ||
| 1 | +/* | |
| 2 | + * This file is part of FFmpeg. | |
| 3 | + * | |
| 4 | + * FFmpeg is free software; you can redistribute it and/or | |
| 5 | + * modify it under the terms of the GNU Lesser General Public | |
| 6 | + * License as published by the Free Software Foundation; either | |
| 7 | + * version 2.1 of the License, or (at your option) any later version. | |
| 8 | + * | |
| 9 | + * FFmpeg is distributed in the hope that it will be useful, | |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 12 | + * Lesser General Public License for more details. | |
| 13 | + * | |
| 14 | + * You should have received a copy of the GNU Lesser General Public | |
| 15 | + * License along with FFmpeg; if not, write to the Free Software | |
| 16 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| 17 | + */ | |
| 18 | + | |
| 19 | +/* Targeted fuzzer that targets specific codecs depending on two | |
| 20 | + compile-time flags. | |
| 21 | + INSTRUCTIONS: | |
| 22 | + | |
| 23 | + * Get the very fresh clang, e.g. see http://libfuzzer.info#versions | |
| 24 | + * Get and build libFuzzer: | |
| 25 | + svn co http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer | |
| 26 | + ./Fuzzer/build.sh | |
| 27 | + * build ffmpeg for fuzzing: | |
| 28 | + FLAGS="-fsanitize=address -fsanitize-coverage=trace-pc-guard,trace-cmp -g" CC="clang $FLAGS" CXX="clang++ $FLAGS" ./configure --disable-yasm | |
| 29 | + make clean && make -j | |
| 30 | + * build the fuzz target. | |
| 31 | + Choose the value of FFMPEG_CODEC (e.g. AV_CODEC_ID_DVD_SUBTITLE) and | |
| 32 | + choose one of FUZZ_FFMPEG_VIDEO, FUZZ_FFMPEG_AUDIO, FUZZ_FFMPEG_SUBTITLE. | |
| 33 | + 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 | |
| 34 | + * create a corpus directory and put some samples there (empty dir is ok too): | |
| 35 | + mkdir CORPUS && cp some-files CORPUS | |
| 36 | + | |
| 37 | + * Run fuzzing: | |
| 38 | + ./target_dec_fuzzer -max_len=100000 CORPUS | |
| 39 | + | |
| 40 | + More info: | |
| 41 | + http://libfuzzer.info | |
| 42 | + http://tutorial.libfuzzer.info | |
| 43 | + https://github.com/google/oss-fuzz | |
| 44 | + http://lcamtuf.coredump.cx/afl/ | |
| 45 | + https://security.googleblog.com/2016/08/guided-in-process-fuzzing-of-chrome.html | |
| 46 | +*/ | |
| 47 | + | |
| 48 | +#include "libavutil/avassert.h" | |
| 49 | + | |
| 50 | +#include "libavcodec/avcodec.h" | |
| 51 | +#include "libavformat/avformat.h" | |
| 52 | + | |
| 53 | +static void error(const char *err) | |
| 54 | +{ | |
| 55 | + fprintf(stderr, "%s", err); | |
| 56 | + exit(1); | |
| 57 | +} | |
| 58 | + | |
| 59 | +static AVCodec *c = NULL; | |
| 60 | +static AVCodec *AVCodecInitialize(enum AVCodecID codec_id) | |
| 61 | +{ | |
| 62 | + AVCodec *res; | |
| 63 | + av_register_all(); | |
| 64 | + av_log_set_level(AV_LOG_PANIC); | |
| 65 | + res = avcodec_find_decoder(codec_id); | |
| 66 | + if (!res) | |
| 67 | + error("Failed to find decoder"); | |
| 68 | + return res; | |
| 69 | +} | |
| 70 | + | |
| 71 | +#if defined(FUZZ_FFMPEG_VIDEO) | |
| 72 | +#define decode_handler avcodec_decode_video2 | |
| 73 | +#elif defined(FUZZ_FFMPEG_AUDIO) | |
| 74 | +#define decode_handler avcodec_decode_audio4 | |
| 75 | +#elif defined(FUZZ_FFMPEG_SUBTITLE) | |
| 76 | +static int subtitle_handler(AVCodecContext *avctx, void *frame, | |
| 77 | + int *got_sub_ptr, AVPacket *avpkt) | |
| 78 | +{ | |
| 79 | + AVSubtitle sub; | |
| 80 | + int ret = avcodec_decode_subtitle2(avctx, &sub, got_sub_ptr, avpkt); | |
| 81 | + if (ret >= 0 && *got_sub_ptr) | |
| 82 | + avsubtitle_free(&sub); | |
| 83 | + return ret; | |
| 84 | +} | |
| 85 | + | |
| 86 | +#define decode_handler subtitle_handler | |
| 87 | +#else | |
| 88 | +#error "Specify encoder type" // To catch mistakes | |
| 89 | +#endif | |
| 90 | + | |
| 91 | +// Class to handle buffer allocation and resize for each frame | |
| 92 | +typedef struct FuzzDataBuffer { | |
| 93 | + size_t size_; | |
| 94 | + uint8_t *data_; | |
| 95 | +} FuzzDataBuffer; | |
| 96 | + | |
| 97 | +void FDBCreate(FuzzDataBuffer *FDB) { | |
| 98 | + FDB->size_ = 0x1000; | |
| 99 | + FDB->data_ = av_malloc(FDB->size_); | |
| 100 | + if (!FDB->data_) | |
| 101 | + error("Failed memory allocation"); | |
| 102 | +} | |
| 103 | + | |
| 104 | +void FDBDesroy(FuzzDataBuffer *FDB) { av_free(FDB->data_); } | |
| 105 | + | |
| 106 | +void FDBRealloc(FuzzDataBuffer *FDB, size_t size) { | |
| 107 | + size_t needed = size + FF_INPUT_BUFFER_PADDING_SIZE; | |
| 108 | + av_assert0(needed > size); | |
| 109 | + if (needed > FDB->size_) { | |
| 110 | + av_free(FDB->data_); | |
| 111 | + FDB->size_ = needed; | |
| 112 | + FDB->data_ = av_malloc(FDB->size_); | |
| 113 | + if (!FDB->data_) | |
| 114 | + error("Failed memory allocation"); | |
| 115 | + } | |
| 116 | +} | |
| 117 | + | |
| 118 | +void FDBPrepare(FuzzDataBuffer *FDB, AVPacket *dst, const uint8_t *data, | |
| 119 | + size_t size) | |
| 120 | +{ | |
| 121 | + FDBRealloc(FDB, size); | |
| 122 | + memcpy(FDB->data_, data, size); | |
| 123 | + size_t padd = FDB->size_ - size; | |
| 124 | + if (padd > FF_INPUT_BUFFER_PADDING_SIZE) | |
| 125 | + padd = FF_INPUT_BUFFER_PADDING_SIZE; | |
| 126 | + memset(FDB->data_ + size, 0, padd); | |
| 127 | + av_init_packet(dst); | |
| 128 | + dst->data = FDB->data_; | |
| 129 | + dst->size = size; | |
| 130 | +} | |
| 131 | + | |
| 132 | +// Ensure we don't loop forever | |
| 133 | +const uint32_t maxiteration = 8096; | |
| 134 | + | |
| 135 | +static const uint64_t FUZZ_TAG = 0x4741542D5A5A5546ULL; | |
| 136 | + | |
| 137 | +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | |
| 138 | + const uint64_t fuzz_tag = FUZZ_TAG; | |
| 139 | + FuzzDataBuffer buffer; | |
| 140 | + const uint8_t *last = data; | |
| 141 | + const uint8_t *end = data + size; | |
| 142 | + uint32_t it = 0; | |
| 143 | + | |
| 144 | + if (!c) | |
| 145 | + c = AVCodecInitialize(FFMPEG_CODEC); // Done once. | |
| 146 | + | |
| 147 | + AVCodecContext* ctx = avcodec_alloc_context3(NULL); | |
| 148 | + if (!ctx) | |
| 149 | + error("Failed memory allocation"); | |
| 150 | + | |
| 151 | + ctx->max_pixels = 4096 * 4096; //To reduce false positive OOM and hangs | |
| 152 | + | |
| 153 | + int res = avcodec_open2(ctx, c, NULL); | |
| 154 | + if (res < 0) | |
| 155 | + return res; | |
| 156 | + | |
| 157 | + FDBCreate(&buffer); | |
| 158 | + int got_frame; | |
| 159 | + AVFrame *frame = av_frame_alloc(); | |
| 160 | + if (!frame) | |
| 161 | + error("Failed memory allocation"); | |
| 162 | + | |
| 163 | + // Read very simple container | |
| 164 | + AVPacket avpkt; | |
| 165 | + while (data < end && it < maxiteration) { | |
| 166 | + // Search for the TAG | |
| 167 | + while (data + sizeof(fuzz_tag) < end) { | |
| 168 | + if (data[0] == (fuzz_tag & 0xFF) && *(const uint64_t *)(data) == fuzz_tag) | |
| 169 | + break; | |
| 170 | + data++; | |
| 171 | + } | |
| 172 | + if (data + sizeof(fuzz_tag) > end) | |
| 173 | + data = end; | |
| 174 | + | |
| 175 | + FDBPrepare(&buffer, &avpkt, last, data - last); | |
| 176 | + data += sizeof(fuzz_tag); | |
| 177 | + last = data; | |
| 178 | + | |
| 179 | + // Iterate through all data | |
| 180 | + while (avpkt.size > 0 && it++ < maxiteration) { | |
| 181 | + av_frame_unref(frame); | |
| 182 | + int ret = decode_handler(ctx, frame, &got_frame, &avpkt); | |
| 183 | + | |
| 184 | + if (it > 20) | |
| 185 | + ctx->error_concealment = 0; | |
| 186 | + | |
| 187 | + if (ret <= 0 || ret > avpkt.size) | |
| 188 | + break; | |
| 189 | + avpkt.data += ret; | |
| 190 | + avpkt.size -= ret; | |
| 191 | + } | |
| 192 | + } | |
| 193 | + | |
| 194 | + av_init_packet(&avpkt); | |
| 195 | + avpkt.data = NULL; | |
| 196 | + avpkt.size = 0; | |
| 197 | + | |
| 198 | + do { | |
| 199 | + got_frame = 0; | |
| 200 | + decode_handler(ctx, frame, &got_frame, &avpkt); | |
| 201 | + } while (got_frame == 1 && it++ < maxiteration); | |
| 202 | + | |
| 203 | + av_frame_free(&frame); | |
| 204 | + avcodec_free_context(&ctx); | |
| 205 | + av_freep(&ctx); | |
| 206 | + FDBDesroy(&buffer); | |
| 207 | + return 0; | |
| 208 | +} | |