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 | } | ||