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#include "config.h"
20#include "libavutil/avassert.h"
21#include "libavutil/avstring.h"
22
23#include "libavcodec/avcodec.h"
24#include "libavcodec/bytestream.h"
25#include "libavformat/avformat.h"
26
27
28typedef struct IOContext {
29    int64_t pos;
30    int64_t filesize;
31    uint8_t *fuzz;
32    int fuzz_size;
33} IOContext;
34
35int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
36
37int64_t interrupt_counter;
38static int interrupt_cb(void *ctx)
39{
40    interrupt_counter --;
41    return interrupt_counter < 0;
42}
43
44static void error(const char *err)
45{
46    fprintf(stderr, "%s", err);
47    exit(1);
48}
49
50static int io_read(void *opaque, uint8_t *buf, int buf_size)
51{
52    IOContext *c = opaque;
53    int size = FFMIN(buf_size, c->fuzz_size);
54
55    if (!c->fuzz_size) {
56        c->filesize = FFMIN(c->pos, c->filesize);
57        return AVERROR_EOF;
58    }
59    if (c->pos > INT64_MAX - size)
60        return AVERROR(EIO);
61
62    memcpy(buf, c->fuzz, size);
63    c->fuzz      += size;
64    c->fuzz_size -= size;
65    c->pos       += size;
66    c->filesize   = FFMAX(c->filesize, c->pos);
67
68    return size;
69}
70
71static int64_t io_seek(void *opaque, int64_t offset, int whence)
72{
73    IOContext *c = opaque;
74
75    if (whence == SEEK_CUR) {
76        if (offset > INT64_MAX - c->pos)
77            return -1;
78        offset += c->pos;
79    } else if (whence == SEEK_END) {
80        if (offset > INT64_MAX - c->filesize)
81            return -1;
82        offset += c->filesize;
83    } else if (whence == AVSEEK_SIZE) {
84        return c->filesize;
85    }
86    if (offset < 0 || offset > c->filesize)
87        return -1;
88    if (IO_FLAT) {
89        c->fuzz      += offset - c->pos;
90        c->fuzz_size -= offset - c->pos;
91    }
92    c->pos = offset;
93    return 0;
94}
95
96// Ensure we don't loop forever
97const uint32_t maxiteration = 8096;
98const int maxblocks= 50000;
99
100static const uint64_t FUZZ_TAG = 0x4741542D5A5A5546ULL;
101
102int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
103    const uint64_t fuzz_tag = FUZZ_TAG;
104    uint32_t it = 0;
105    AVFormatContext *avfmt = avformat_alloc_context();
106    AVPacket *pkt;
107    char filename[1025] = {0};
108    AVIOContext *fuzzed_pb = NULL;
109    uint8_t *io_buffer;
110    int io_buffer_size = 32768;
111    int64_t filesize   = size;
112    IOContext opaque;
113    static int c;
114    int seekable = 0;
115    int ret;
116    AVInputFormat *fmt = NULL;
117#ifdef FFMPEG_DEMUXER
118#define DEMUXER_SYMBOL0(DEMUXER) ff_##DEMUXER##_demuxer
119#define DEMUXER_SYMBOL(DEMUXER) DEMUXER_SYMBOL0(DEMUXER)
120    extern AVInputFormat DEMUXER_SYMBOL(FFMPEG_DEMUXER);
121    fmt = &DEMUXER_SYMBOL(FFMPEG_DEMUXER);
122#endif
123
124    if (!c) {
125        av_log_set_level(AV_LOG_PANIC);
126        c=1;
127    }
128
129    if (!avfmt)
130        error("Failed avformat_alloc_context()");
131
132    if (IO_FLAT) {
133        seekable = 1;
134        io_buffer_size = size;
135    } else if (size > 2048) {
136        int flags;
137        char extension[64];
138
139        GetByteContext gbc;
140        memcpy (filename, data + size - 1024, 1024);
141        bytestream2_init(&gbc, data + size - 2048, 1024);
142        size -= 2048;
143
144        io_buffer_size = bytestream2_get_le32(&gbc) & 0xFFFFFFF;
145        flags          = bytestream2_get_byte(&gbc);
146        seekable       = flags & 1;
147        filesize       = bytestream2_get_le64(&gbc) & 0x7FFFFFFFFFFFFFFF;
148
149        if ((flags & 2) && strlen(filename) < sizeof(filename) / 2) {
150            const AVInputFormat *avif = NULL;
151            void *avif_iter = NULL;
152            int avif_count = 0;
153            while ((avif = av_demuxer_iterate(&avif_iter))) {
154                if (avif->extensions)
155                    avif_count ++;
156            }
157            avif_count =  bytestream2_get_le32(&gbc) % avif_count;
158
159            avif_iter = NULL;
160            while ((avif = av_demuxer_iterate(&avif_iter))) {
161                if (avif->extensions)
162                    if (!avif_count--)
163                        break;
164            }
165            av_strlcpy(extension, avif->extensions, sizeof(extension));
166            if (strchr(extension, ','))
167                *strchr(extension, ',') = 0;
168            av_strlcatf(filename, sizeof(filename), ".%s", extension);
169        }
170
171        interrupt_counter = bytestream2_get_le32(&gbc);
172        avfmt->interrupt_callback.callback = interrupt_cb;
173    }
174
175    // HLS uses a loop with sleep, we thus must breakout or we timeout
176    if (fmt && !strcmp(fmt->name, "hls"))
177        interrupt_counter &= 31;
178
179    if (!io_buffer_size || size / io_buffer_size > maxblocks)
180        io_buffer_size = size;
181
182    pkt = av_packet_alloc();
183    if (!pkt)
184        error("Failed to allocate pkt");
185
186    io_buffer = av_malloc(io_buffer_size);
187    if (!io_buffer)
188        error("Failed to allocate io_buffer");
189
190    opaque.filesize = filesize;
191    opaque.pos      = 0;
192    opaque.fuzz     = data;
193    opaque.fuzz_size= size;
194    fuzzed_pb = avio_alloc_context(io_buffer, io_buffer_size, 0, &opaque,
195                                   io_read, NULL, seekable ? io_seek : NULL);
196    if (!fuzzed_pb)
197        error("avio_alloc_context failed");
198
199    avfmt->pb = fuzzed_pb;
200
201    ret = avformat_open_input(&avfmt, filename, fmt, NULL);
202    if (ret < 0) {
203        goto fail;
204    }
205
206    ret = avformat_find_stream_info(avfmt, NULL);
207
208    //TODO, test seeking
209
210    for(it = 0; it < maxiteration; it++) {
211        ret = av_read_frame(avfmt, pkt);
212        if (ret < 0)
213            break;
214        av_packet_unref(pkt);
215    }
216
217fail:
218    av_packet_free(&pkt);
219    av_freep(&fuzzed_pb->buffer);
220    avio_context_free(&fuzzed_pb);
221    avformat_close_input(&avfmt);
222
223    return 0;
224
225}
226