blob: cb3bc50919bea99c87c533b9b63f8eb8391ac418
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 | #include "libavutil/intreadwrite.h" |
50 | |
51 | #include "libavcodec/avcodec.h" |
52 | #include "libavformat/avformat.h" |
53 | |
54 | static void error(const char *err) |
55 | { |
56 | fprintf(stderr, "%s", err); |
57 | exit(1); |
58 | } |
59 | |
60 | static AVCodec *c = NULL; |
61 | static AVCodec *AVCodecInitialize(enum AVCodecID codec_id) |
62 | { |
63 | AVCodec *res; |
64 | av_register_all(); |
65 | av_log_set_level(AV_LOG_PANIC); |
66 | res = avcodec_find_decoder(codec_id); |
67 | if (!res) |
68 | error("Failed to find decoder"); |
69 | return res; |
70 | } |
71 | |
72 | #if defined(FUZZ_FFMPEG_VIDEO) |
73 | #define decode_handler avcodec_decode_video2 |
74 | #elif defined(FUZZ_FFMPEG_AUDIO) |
75 | #define decode_handler avcodec_decode_audio4 |
76 | #elif defined(FUZZ_FFMPEG_SUBTITLE) |
77 | static int subtitle_handler(AVCodecContext *avctx, void *frame, |
78 | int *got_sub_ptr, AVPacket *avpkt) |
79 | { |
80 | AVSubtitle sub; |
81 | int ret = avcodec_decode_subtitle2(avctx, &sub, got_sub_ptr, avpkt); |
82 | if (ret >= 0 && *got_sub_ptr) |
83 | avsubtitle_free(&sub); |
84 | return ret; |
85 | } |
86 | |
87 | #define decode_handler subtitle_handler |
88 | #else |
89 | #error "Specify encoder type" // To catch mistakes |
90 | #endif |
91 | |
92 | // Class to handle buffer allocation and resize for each frame |
93 | typedef struct FuzzDataBuffer { |
94 | size_t size_; |
95 | uint8_t *data_; |
96 | } FuzzDataBuffer; |
97 | |
98 | void FDBCreate(FuzzDataBuffer *FDB) { |
99 | FDB->size_ = 0x1000; |
100 | FDB->data_ = av_malloc(FDB->size_); |
101 | if (!FDB->data_) |
102 | error("Failed memory allocation"); |
103 | } |
104 | |
105 | void FDBDesroy(FuzzDataBuffer *FDB) { av_free(FDB->data_); } |
106 | |
107 | void FDBRealloc(FuzzDataBuffer *FDB, size_t size) { |
108 | size_t needed = size + FF_INPUT_BUFFER_PADDING_SIZE; |
109 | av_assert0(needed > size); |
110 | if (needed > FDB->size_) { |
111 | av_free(FDB->data_); |
112 | FDB->size_ = needed; |
113 | FDB->data_ = av_malloc(FDB->size_); |
114 | if (!FDB->data_) |
115 | error("Failed memory allocation"); |
116 | } |
117 | } |
118 | |
119 | void FDBPrepare(FuzzDataBuffer *FDB, AVPacket *dst, const uint8_t *data, |
120 | size_t size) |
121 | { |
122 | FDBRealloc(FDB, size); |
123 | memcpy(FDB->data_, data, size); |
124 | size_t padd = FDB->size_ - size; |
125 | if (padd > FF_INPUT_BUFFER_PADDING_SIZE) |
126 | padd = FF_INPUT_BUFFER_PADDING_SIZE; |
127 | memset(FDB->data_ + size, 0, padd); |
128 | av_init_packet(dst); |
129 | dst->data = FDB->data_; |
130 | dst->size = size; |
131 | } |
132 | |
133 | // Ensure we don't loop forever |
134 | const uint32_t maxiteration = 8096; |
135 | |
136 | static const uint64_t FUZZ_TAG = 0x4741542D5A5A5546ULL; |
137 | |
138 | int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
139 | const uint64_t fuzz_tag = FUZZ_TAG; |
140 | FuzzDataBuffer buffer; |
141 | const uint8_t *last = data; |
142 | const uint8_t *end = data + size; |
143 | uint32_t it = 0; |
144 | |
145 | if (!c) |
146 | c = AVCodecInitialize(FFMPEG_CODEC); // Done once. |
147 | |
148 | AVCodecContext* ctx = avcodec_alloc_context3(NULL); |
149 | if (!ctx) |
150 | error("Failed memory allocation"); |
151 | |
152 | ctx->max_pixels = 4096 * 4096; //To reduce false positive OOM and hangs |
153 | |
154 | int res = avcodec_open2(ctx, c, NULL); |
155 | if (res < 0) |
156 | return res; |
157 | |
158 | FDBCreate(&buffer); |
159 | int got_frame; |
160 | AVFrame *frame = av_frame_alloc(); |
161 | if (!frame) |
162 | error("Failed memory allocation"); |
163 | |
164 | // Read very simple container |
165 | AVPacket avpkt; |
166 | while (data < end && it < maxiteration) { |
167 | // Search for the TAG |
168 | while (data + sizeof(fuzz_tag) < end) { |
169 | if (data[0] == (fuzz_tag & 0xFF) && AV_RN64(data) == fuzz_tag) |
170 | break; |
171 | data++; |
172 | } |
173 | if (data + sizeof(fuzz_tag) > end) |
174 | data = end; |
175 | |
176 | FDBPrepare(&buffer, &avpkt, last, data - last); |
177 | data += sizeof(fuzz_tag); |
178 | last = data; |
179 | |
180 | // Iterate through all data |
181 | while (avpkt.size > 0 && it++ < maxiteration) { |
182 | av_frame_unref(frame); |
183 | int ret = decode_handler(ctx, frame, &got_frame, &avpkt); |
184 | |
185 | if (it > 20) |
186 | ctx->error_concealment = 0; |
187 | |
188 | if (ret <= 0 || ret > avpkt.size) |
189 | break; |
190 | if (ctx->codec_type != AVMEDIA_TYPE_AUDIO) |
191 | ret = avpkt.size; |
192 | avpkt.data += ret; |
193 | avpkt.size -= ret; |
194 | } |
195 | } |
196 | |
197 | av_init_packet(&avpkt); |
198 | avpkt.data = NULL; |
199 | avpkt.size = 0; |
200 | |
201 | do { |
202 | got_frame = 0; |
203 | decode_handler(ctx, frame, &got_frame, &avpkt); |
204 | } while (got_frame == 1 && it++ < maxiteration); |
205 | |
206 | av_frame_free(&frame); |
207 | avcodec_free_context(&ctx); |
208 | av_freep(&ctx); |
209 | FDBDesroy(&buffer); |
210 | return 0; |
211 | } |
212 |