1/*
2 * fuzz-decode: special program for fuzzing.  It still reads encoded
3 * files just like interop-decode, but tries to do it faster and
4 * forgoes several advanced options.
5 */
6
7#ifdef WIN32
8
9#include <stdio.h>
10
11int
12main (int argc, char **argv)
13{
14    fprintf(stderr, "%s is not supported on Windows: need mmap(2)\n", argv[0]);
15    return 1;
16}
17
18#else
19
20#include <assert.h>
21
22#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
23#include <sys/endian.h>
24#define bswap_16 bswap16
25#define bswap_32 bswap32
26#define bswap_64 bswap64
27#elif defined(__APPLE__)
28#include <libkern/OSByteOrder.h>
29#define bswap_16 OSSwapInt16
30#define bswap_32 OSSwapInt32
31#define bswap_64 OSSwapInt64
32#elif defined(WIN32)
33#define bswap_16 _byteswap_ushort
34#define bswap_32 _byteswap_ulong
35#define bswap_64 _byteswap_uint64
36#else
37#include <byteswap.h>
38#endif
39
40#include <errno.h>
41#include <inttypes.h>
42#include <stddef.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47#include <sys/queue.h>
48#include <sys/types.h>
49#include <sys/stat.h>
50#include <sys/mman.h>
51#include <fcntl.h>
52
53#include "lsqpack.h"
54#include "lsxpack_header.h"
55
56#define MIN(a, b) ((a) < (b) ? (a) : (b))
57
58static void
59usage (const char *name)
60{
61    fprintf(stderr,
62"Usage: %s [options] [-i input] [-o output]\n"
63"\n"
64"Options:\n"
65"   -i FILE     Input file.\n"
66"   -f FILE     Fuzz file: this is the stuff the fuzzer will change.\n"
67"   -s NUMBER   Maximum number of risked streams.  Defaults to %u.\n"
68"   -t NUMBER   Dynamic table size.  Defaults to %u.\n"
69"\n"
70"   -h          Print this help screen and exit\n"
71    , name, LSQPACK_DEF_MAX_RISKED_STREAMS, LSQPACK_DEF_DYN_TABLE_SIZE);
72}
73
74
75static void
76hblock_unblocked (void *buf_p)
77{
78    exit(1);
79}
80
81
82struct header
83{
84    LIST_ENTRY(header)      next_header;
85    struct lsxpack_header   xhdr;
86    char                    buf[0x10000];
87};
88
89
90LIST_HEAD(, header)     s_headers;
91
92
93static struct lsxpack_header *
94prepare_decode (void *hblock_ctx, struct lsxpack_header *xhdr, size_t space)
95{
96    struct header *header;
97
98    if (xhdr)
99    {
100        /* If the original buffer is not enough, we don't reallocate */
101        header = (struct header *) ((char *) xhdr
102                                        - offsetof(struct header, xhdr));
103        LIST_REMOVE(header, next_header);
104        free(header);
105        return NULL;
106    }
107    else if (space <= LSXPACK_MAX_STRLEN)
108    {
109        header = malloc(sizeof(*header));
110        if (!header)
111            return NULL;
112        LIST_INSERT_HEAD(&s_headers, header, next_header);
113        lsxpack_header_prepare_decode(&header->xhdr, header->buf,
114                                                0, sizeof(header->buf));
115        return &header->xhdr;
116    }
117    else
118        return NULL;
119}
120
121
122static int
123process_header (void *hblock_ctx, struct lsxpack_header *xhdr)
124{
125    struct header *header;
126
127    header = (struct header *) ((char *) xhdr - offsetof(struct header, xhdr));
128    LIST_REMOVE(header, next_header);
129    free(header);
130    return 0;
131}
132
133
134static const struct lsqpack_dec_hset_if hset_if = {
135    .dhi_unblocked      = hblock_unblocked,
136    .dhi_prepare_decode = prepare_decode,
137    .dhi_process_header = process_header,
138};
139
140
141int
142main (int argc, char **argv)
143{
144    int in_fd = -1, fuzz_fd = STDIN_FILENO;
145    int opt;
146    unsigned dyn_table_size     = LSQPACK_DEF_DYN_TABLE_SIZE,
147             max_risked_streams = LSQPACK_DEF_MAX_RISKED_STREAMS;
148    struct lsqpack_dec decoder;
149    const unsigned char *p, *end;
150    unsigned char *begin;
151    struct stat st;
152    uint64_t stream_id;
153    uint32_t size;
154    int r;
155    struct header *header;
156
157    while (-1 != (opt = getopt(argc, argv, "i:f:s:t:h")))
158    {
159        switch (opt)
160        {
161        case 'i':
162            in_fd = open(optarg, O_RDONLY);
163            if (in_fd < 0)
164            {
165                fprintf(stderr, "cannot open `%s' for reading: %s\n",
166                                            optarg, strerror(errno));
167                exit(EXIT_FAILURE);
168            }
169            break;
170        case 'f':
171            fuzz_fd = open(optarg, O_RDONLY);
172            if (fuzz_fd < 0)
173            {
174                fprintf(stderr, "cannot open `%s' for reading: %s\n",
175                                            optarg, strerror(errno));
176                exit(EXIT_FAILURE);
177            }
178            break;
179        case 's':
180            max_risked_streams = atoi(optarg);
181            break;
182        case 't':
183            dyn_table_size = atoi(optarg);
184            break;
185        case 'h':
186            usage(argv[0]);
187            exit(EXIT_SUCCESS);
188        default:
189            exit(EXIT_FAILURE);
190        }
191    }
192
193    LIST_INIT(&s_headers);
194    lsqpack_dec_init(&decoder, NULL, dyn_table_size, max_risked_streams,
195                            &hset_if, 0 /* TODO: add command-line option */);
196
197    if (in_fd < 0)
198    {
199        fprintf(stderr, "Specify input using `-i' option\n");
200        exit(1);
201    }
202
203    if (0 != fstat(in_fd, &st))
204    {
205        perror("fstat");
206        exit(1);
207    }
208
209    begin = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in_fd, 0);
210    if (!begin)
211    {
212        perror("mmap");
213        exit(1);
214    }
215
216    p = begin;
217    end = begin + st.st_size;
218    while (p + sizeof(stream_id) + sizeof(size) < end)
219    {
220        stream_id = * (uint64_t *) p;
221        p += sizeof(uint64_t);
222        size = * (uint32_t *) p;
223        p += sizeof(uint32_t);
224#if __BYTE_ORDER == __LITTLE_ENDIAN
225        stream_id = bswap_64(stream_id);
226        size = bswap_32(size);
227#endif
228        if (p + size > end)
229        {
230            fprintf(stderr, "truncated input at offset %u",
231                                                (unsigned) (p - begin));
232            abort();
233        }
234        if (stream_id == 0)
235        {
236            r = lsqpack_dec_enc_in(&decoder, p, size);
237            if (r != 0)
238                abort();
239        }
240        else
241        {
242            const unsigned char *cur = p;
243            enum lsqpack_read_header_status rhs;
244            rhs = lsqpack_dec_header_in(&decoder, NULL, stream_id,
245                        size, &cur, size, NULL, NULL);
246            if (rhs != LQRHS_DONE || (uint32_t) (cur - p) != size)
247                abort();
248        }
249        p += size;
250    }
251
252    munmap(begin, st.st_size);
253    (void) close(in_fd);
254
255    /* Now let's read the fuzzed part */
256
257    if (0 != fstat(fuzz_fd, &st))
258    {
259        perror("fstat");
260        exit(1);
261    }
262
263    begin = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fuzz_fd, 0);
264    if (!begin)
265    {
266        perror("mmap");
267        exit(1);
268    }
269
270    p = begin;
271    end = begin + st.st_size;
272    while (p + sizeof(stream_id) + sizeof(size) < end)
273    {
274        stream_id = * (uint64_t *) p;
275        p += sizeof(uint64_t);
276        size = * (uint32_t *) p;
277        p += sizeof(uint32_t);
278#if __BYTE_ORDER == __LITTLE_ENDIAN
279        stream_id = bswap_64(stream_id);
280        size = bswap_32(size);
281#endif
282        if (stream_id == 0)
283        {
284            r = lsqpack_dec_enc_in(&decoder, p, MIN(size, (uint32_t) (end - p)));
285            (void) r;
286        }
287        else
288        {
289            const unsigned char *cur = p;
290            enum lsqpack_read_header_status rhs;
291            size = MIN(size, (uint32_t) (end - p));
292            rhs = lsqpack_dec_header_in(&decoder, NULL, stream_id,
293                        size, &cur, size, NULL, NULL);
294            (void) rhs;
295        }
296        break;
297    }
298
299    munmap(begin, st.st_size);
300    (void) close(fuzz_fd);
301
302    lsqpack_dec_cleanup(&decoder);
303    while (!LIST_EMPTY(&s_headers))
304    {
305        header = LIST_FIRST(&s_headers);
306        LIST_REMOVE(header, next_header);
307        free(header);
308    }
309
310    exit(0);
311}
312
313#endif
314