1bfd9d262SDmitri Tikhonov/*
2bfd9d262SDmitri Tikhonov * fuzz-decode: special program for fuzzing.  It still reads encoded
3bfd9d262SDmitri Tikhonov * files just like interop-decode, but tries to do it faster and
4bfd9d262SDmitri Tikhonov * forgoes several advanced options.
5bfd9d262SDmitri Tikhonov */
6bfd9d262SDmitri Tikhonov
7205a2804SDmitri Tikhonov#ifdef WIN32
8205a2804SDmitri Tikhonov
9205a2804SDmitri Tikhonov#include <stdio.h>
10205a2804SDmitri Tikhonov
11205a2804SDmitri Tikhonovint
12205a2804SDmitri Tikhonovmain (int argc, char **argv)
13205a2804SDmitri Tikhonov{
14205a2804SDmitri Tikhonov    fprintf(stderr, "%s is not supported on Windows: need mmap(2)\n", argv[0]);
15205a2804SDmitri Tikhonov    return 1;
16205a2804SDmitri Tikhonov}
17205a2804SDmitri Tikhonov
18205a2804SDmitri Tikhonov#else
19205a2804SDmitri Tikhonov
20bfd9d262SDmitri Tikhonov#include <assert.h>
21446dba69SDmitri Tikhonov
22446dba69SDmitri Tikhonov#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
23446dba69SDmitri Tikhonov#include <sys/endian.h>
24446dba69SDmitri Tikhonov#define bswap_16 bswap16
25446dba69SDmitri Tikhonov#define bswap_32 bswap32
26446dba69SDmitri Tikhonov#define bswap_64 bswap64
27446dba69SDmitri Tikhonov#elif defined(__APPLE__)
28446dba69SDmitri Tikhonov#include <libkern/OSByteOrder.h>
29446dba69SDmitri Tikhonov#define bswap_16 OSSwapInt16
30446dba69SDmitri Tikhonov#define bswap_32 OSSwapInt32
31446dba69SDmitri Tikhonov#define bswap_64 OSSwapInt64
32446dba69SDmitri Tikhonov#elif defined(WIN32)
33446dba69SDmitri Tikhonov#define bswap_16 _byteswap_ushort
34446dba69SDmitri Tikhonov#define bswap_32 _byteswap_ulong
35446dba69SDmitri Tikhonov#define bswap_64 _byteswap_uint64
36446dba69SDmitri Tikhonov#else
37bfd9d262SDmitri Tikhonov#include <byteswap.h>
38446dba69SDmitri Tikhonov#endif
39446dba69SDmitri Tikhonov
40bfd9d262SDmitri Tikhonov#include <errno.h>
41bfd9d262SDmitri Tikhonov#include <inttypes.h>
4260620859SDmitri Tikhonov#include <stddef.h>
43bfd9d262SDmitri Tikhonov#include <stdio.h>
44bfd9d262SDmitri Tikhonov#include <stdlib.h>
45bfd9d262SDmitri Tikhonov#include <string.h>
46bfd9d262SDmitri Tikhonov#include <unistd.h>
47bfd9d262SDmitri Tikhonov#include <sys/queue.h>
48bfd9d262SDmitri Tikhonov#include <sys/types.h>
49bfd9d262SDmitri Tikhonov#include <sys/stat.h>
50bfd9d262SDmitri Tikhonov#include <sys/mman.h>
51bfd9d262SDmitri Tikhonov#include <fcntl.h>
52bfd9d262SDmitri Tikhonov
53bfd9d262SDmitri Tikhonov#include "lsqpack.h"
5460620859SDmitri Tikhonov#include "lsxpack_header.h"
55bfd9d262SDmitri Tikhonov
56bfd9d262SDmitri Tikhonov#define MIN(a, b) ((a) < (b) ? (a) : (b))
57bfd9d262SDmitri Tikhonov
58bfd9d262SDmitri Tikhonovstatic void
59bfd9d262SDmitri Tikhonovusage (const char *name)
60bfd9d262SDmitri Tikhonov{
61bfd9d262SDmitri Tikhonov    fprintf(stderr,
62bfd9d262SDmitri Tikhonov"Usage: %s [options] [-i input] [-o output]\n"
63bfd9d262SDmitri Tikhonov"\n"
64bfd9d262SDmitri Tikhonov"Options:\n"
65bfd9d262SDmitri Tikhonov"   -i FILE     Input file.\n"
66bfd9d262SDmitri Tikhonov"   -f FILE     Fuzz file: this is the stuff the fuzzer will change.\n"
67bfd9d262SDmitri Tikhonov"   -s NUMBER   Maximum number of risked streams.  Defaults to %u.\n"
68bfd9d262SDmitri Tikhonov"   -t NUMBER   Dynamic table size.  Defaults to %u.\n"
69bfd9d262SDmitri Tikhonov"\n"
70bfd9d262SDmitri Tikhonov"   -h          Print this help screen and exit\n"
71bfd9d262SDmitri Tikhonov    , name, LSQPACK_DEF_MAX_RISKED_STREAMS, LSQPACK_DEF_DYN_TABLE_SIZE);
72bfd9d262SDmitri Tikhonov}
73bfd9d262SDmitri Tikhonov
74bfd9d262SDmitri Tikhonov
75bfd9d262SDmitri Tikhonovstatic void
76bfd9d262SDmitri Tikhonovhblock_unblocked (void *buf_p)
77bfd9d262SDmitri Tikhonov{
78bfd9d262SDmitri Tikhonov    exit(1);
79bfd9d262SDmitri Tikhonov}
80bfd9d262SDmitri Tikhonov
81bfd9d262SDmitri Tikhonov
8260620859SDmitri Tikhonovstruct header
8360620859SDmitri Tikhonov{
8460620859SDmitri Tikhonov    LIST_ENTRY(header)      next_header;
8560620859SDmitri Tikhonov    struct lsxpack_header   xhdr;
8660620859SDmitri Tikhonov    char                    buf[0x10000];
8760620859SDmitri Tikhonov};
8860620859SDmitri Tikhonov
8960620859SDmitri Tikhonov
9060620859SDmitri TikhonovLIST_HEAD(, header)     s_headers;
9160620859SDmitri Tikhonov
9260620859SDmitri Tikhonov
9360620859SDmitri Tikhonovstatic struct lsxpack_header *
9460620859SDmitri Tikhonovprepare_decode (void *hblock_ctx, struct lsxpack_header *xhdr, size_t space)
9560620859SDmitri Tikhonov{
9660620859SDmitri Tikhonov    struct header *header;
9760620859SDmitri Tikhonov
9860620859SDmitri Tikhonov    if (xhdr)
9960620859SDmitri Tikhonov    {
10060620859SDmitri Tikhonov        /* If the original buffer is not enough, we don't reallocate */
10160620859SDmitri Tikhonov        header = (struct header *) ((char *) xhdr
10260620859SDmitri Tikhonov                                        - offsetof(struct header, xhdr));
10360620859SDmitri Tikhonov        LIST_REMOVE(header, next_header);
10460620859SDmitri Tikhonov        free(header);
10560620859SDmitri Tikhonov        return NULL;
10660620859SDmitri Tikhonov    }
10760620859SDmitri Tikhonov    else if (space <= LSXPACK_MAX_STRLEN)
10860620859SDmitri Tikhonov    {
10960620859SDmitri Tikhonov        header = malloc(sizeof(*header));
11060620859SDmitri Tikhonov        if (!header)
11160620859SDmitri Tikhonov            return NULL;
11260620859SDmitri Tikhonov        LIST_INSERT_HEAD(&s_headers, header, next_header);
11360620859SDmitri Tikhonov        lsxpack_header_prepare_decode(&header->xhdr, header->buf,
11460620859SDmitri Tikhonov                                                0, sizeof(header->buf));
11560620859SDmitri Tikhonov        return &header->xhdr;
11660620859SDmitri Tikhonov    }
11760620859SDmitri Tikhonov    else
11860620859SDmitri Tikhonov        return NULL;
11960620859SDmitri Tikhonov}
12060620859SDmitri Tikhonov
12160620859SDmitri Tikhonov
12260620859SDmitri Tikhonovstatic int
12360620859SDmitri Tikhonovprocess_header (void *hblock_ctx, struct lsxpack_header *xhdr)
12460620859SDmitri Tikhonov{
12560620859SDmitri Tikhonov    struct header *header;
12660620859SDmitri Tikhonov
12760620859SDmitri Tikhonov    header = (struct header *) ((char *) xhdr - offsetof(struct header, xhdr));
12860620859SDmitri Tikhonov    LIST_REMOVE(header, next_header);
12960620859SDmitri Tikhonov    free(header);
13060620859SDmitri Tikhonov    return 0;
13160620859SDmitri Tikhonov}
13260620859SDmitri Tikhonov
13360620859SDmitri Tikhonov
13460620859SDmitri Tikhonovstatic const struct lsqpack_dec_hset_if hset_if = {
13560620859SDmitri Tikhonov    .dhi_unblocked      = hblock_unblocked,
13660620859SDmitri Tikhonov    .dhi_prepare_decode = prepare_decode,
13760620859SDmitri Tikhonov    .dhi_process_header = process_header,
13860620859SDmitri Tikhonov};
13960620859SDmitri Tikhonov
14060620859SDmitri Tikhonov
141bfd9d262SDmitri Tikhonovint
142bfd9d262SDmitri Tikhonovmain (int argc, char **argv)
143bfd9d262SDmitri Tikhonov{
144bfd9d262SDmitri Tikhonov    int in_fd = -1, fuzz_fd = STDIN_FILENO;
145bfd9d262SDmitri Tikhonov    int opt;
146bfd9d262SDmitri Tikhonov    unsigned dyn_table_size     = LSQPACK_DEF_DYN_TABLE_SIZE,
147bfd9d262SDmitri Tikhonov             max_risked_streams = LSQPACK_DEF_MAX_RISKED_STREAMS;
148bfd9d262SDmitri Tikhonov    struct lsqpack_dec decoder;
149bfd9d262SDmitri Tikhonov    const unsigned char *p, *end;
150bfd9d262SDmitri Tikhonov    unsigned char *begin;
151bfd9d262SDmitri Tikhonov    struct stat st;
152bfd9d262SDmitri Tikhonov    uint64_t stream_id;
153bfd9d262SDmitri Tikhonov    uint32_t size;
154bfd9d262SDmitri Tikhonov    int r;
15560620859SDmitri Tikhonov    struct header *header;
156bfd9d262SDmitri Tikhonov
157bfd9d262SDmitri Tikhonov    while (-1 != (opt = getopt(argc, argv, "i:f:s:t:h")))
158bfd9d262SDmitri Tikhonov    {
159bfd9d262SDmitri Tikhonov        switch (opt)
160bfd9d262SDmitri Tikhonov        {
161bfd9d262SDmitri Tikhonov        case 'i':
162bfd9d262SDmitri Tikhonov            in_fd = open(optarg, O_RDONLY);
163bfd9d262SDmitri Tikhonov            if (in_fd < 0)
164bfd9d262SDmitri Tikhonov            {
165bfd9d262SDmitri Tikhonov                fprintf(stderr, "cannot open `%s' for reading: %s\n",
166bfd9d262SDmitri Tikhonov                                            optarg, strerror(errno));
167bfd9d262SDmitri Tikhonov                exit(EXIT_FAILURE);
168bfd9d262SDmitri Tikhonov            }
169bfd9d262SDmitri Tikhonov            break;
170bfd9d262SDmitri Tikhonov        case 'f':
171bfd9d262SDmitri Tikhonov            fuzz_fd = open(optarg, O_RDONLY);
172bfd9d262SDmitri Tikhonov            if (fuzz_fd < 0)
173bfd9d262SDmitri Tikhonov            {
174bfd9d262SDmitri Tikhonov                fprintf(stderr, "cannot open `%s' for reading: %s\n",
175bfd9d262SDmitri Tikhonov                                            optarg, strerror(errno));
176bfd9d262SDmitri Tikhonov                exit(EXIT_FAILURE);
177bfd9d262SDmitri Tikhonov            }
178bfd9d262SDmitri Tikhonov            break;
179bfd9d262SDmitri Tikhonov        case 's':
180bfd9d262SDmitri Tikhonov            max_risked_streams = atoi(optarg);
181bfd9d262SDmitri Tikhonov            break;
182bfd9d262SDmitri Tikhonov        case 't':
183bfd9d262SDmitri Tikhonov            dyn_table_size = atoi(optarg);
184bfd9d262SDmitri Tikhonov            break;
185bfd9d262SDmitri Tikhonov        case 'h':
186bfd9d262SDmitri Tikhonov            usage(argv[0]);
187bfd9d262SDmitri Tikhonov            exit(EXIT_SUCCESS);
188bfd9d262SDmitri Tikhonov        default:
189bfd9d262SDmitri Tikhonov            exit(EXIT_FAILURE);
190bfd9d262SDmitri Tikhonov        }
191bfd9d262SDmitri Tikhonov    }
192bfd9d262SDmitri Tikhonov
19360620859SDmitri Tikhonov    LIST_INIT(&s_headers);
194bfd9d262SDmitri Tikhonov    lsqpack_dec_init(&decoder, NULL, dyn_table_size, max_risked_streams,
19560620859SDmitri Tikhonov                            &hset_if, 0 /* TODO: add command-line option */);
196bfd9d262SDmitri Tikhonov
197197b1c6fSDmitri Tikhonov    if (in_fd < 0)
198197b1c6fSDmitri Tikhonov    {
199197b1c6fSDmitri Tikhonov        fprintf(stderr, "Specify input using `-i' option\n");
200197b1c6fSDmitri Tikhonov        exit(1);
201197b1c6fSDmitri Tikhonov    }
202197b1c6fSDmitri Tikhonov
203bfd9d262SDmitri Tikhonov    if (0 != fstat(in_fd, &st))
204bfd9d262SDmitri Tikhonov    {
205bfd9d262SDmitri Tikhonov        perror("fstat");
206bfd9d262SDmitri Tikhonov        exit(1);
207bfd9d262SDmitri Tikhonov    }
208bfd9d262SDmitri Tikhonov
209bfd9d262SDmitri Tikhonov    begin = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in_fd, 0);
210bfd9d262SDmitri Tikhonov    if (!begin)
211bfd9d262SDmitri Tikhonov    {
212bfd9d262SDmitri Tikhonov        perror("mmap");
213bfd9d262SDmitri Tikhonov        exit(1);
214bfd9d262SDmitri Tikhonov    }
215bfd9d262SDmitri Tikhonov
216bfd9d262SDmitri Tikhonov    p = begin;
217bfd9d262SDmitri Tikhonov    end = begin + st.st_size;
218bfd9d262SDmitri Tikhonov    while (p + sizeof(stream_id) + sizeof(size) < end)
219bfd9d262SDmitri Tikhonov    {
220bfd9d262SDmitri Tikhonov        stream_id = * (uint64_t *) p;
221bfd9d262SDmitri Tikhonov        p += sizeof(uint64_t);
222bfd9d262SDmitri Tikhonov        size = * (uint32_t *) p;
223bfd9d262SDmitri Tikhonov        p += sizeof(uint32_t);
224bfd9d262SDmitri Tikhonov#if __BYTE_ORDER == __LITTLE_ENDIAN
225bfd9d262SDmitri Tikhonov        stream_id = bswap_64(stream_id);
226bfd9d262SDmitri Tikhonov        size = bswap_32(size);
227bfd9d262SDmitri Tikhonov#endif
228bfd9d262SDmitri Tikhonov        if (p + size > end)
229bfd9d262SDmitri Tikhonov        {
230bfd9d262SDmitri Tikhonov            fprintf(stderr, "truncated input at offset %u",
231bfd9d262SDmitri Tikhonov                                                (unsigned) (p - begin));
232bfd9d262SDmitri Tikhonov            abort();
233bfd9d262SDmitri Tikhonov        }
234bfd9d262SDmitri Tikhonov        if (stream_id == 0)
235bfd9d262SDmitri Tikhonov        {
236bfd9d262SDmitri Tikhonov            r = lsqpack_dec_enc_in(&decoder, p, size);
237bfd9d262SDmitri Tikhonov            if (r != 0)
238bfd9d262SDmitri Tikhonov                abort();
239bfd9d262SDmitri Tikhonov        }
240bfd9d262SDmitri Tikhonov        else
241bfd9d262SDmitri Tikhonov        {
242bfd9d262SDmitri Tikhonov            const unsigned char *cur = p;
243bfd9d262SDmitri Tikhonov            enum lsqpack_read_header_status rhs;
244bfd9d262SDmitri Tikhonov            rhs = lsqpack_dec_header_in(&decoder, NULL, stream_id,
24560620859SDmitri Tikhonov                        size, &cur, size, NULL, NULL);
246446dba69SDmitri Tikhonov            if (rhs != LQRHS_DONE || (uint32_t) (cur - p) != size)
247bfd9d262SDmitri Tikhonov                abort();
248bfd9d262SDmitri Tikhonov        }
249bfd9d262SDmitri Tikhonov        p += size;
250bfd9d262SDmitri Tikhonov    }
251bfd9d262SDmitri Tikhonov
252bfd9d262SDmitri Tikhonov    munmap(begin, st.st_size);
253bfd9d262SDmitri Tikhonov    (void) close(in_fd);
254bfd9d262SDmitri Tikhonov
255bfd9d262SDmitri Tikhonov    /* Now let's read the fuzzed part */
256bfd9d262SDmitri Tikhonov
257bfd9d262SDmitri Tikhonov    if (0 != fstat(fuzz_fd, &st))
258bfd9d262SDmitri Tikhonov    {
259bfd9d262SDmitri Tikhonov        perror("fstat");
260bfd9d262SDmitri Tikhonov        exit(1);
261bfd9d262SDmitri Tikhonov    }
262bfd9d262SDmitri Tikhonov
263bfd9d262SDmitri Tikhonov    begin = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fuzz_fd, 0);
264bfd9d262SDmitri Tikhonov    if (!begin)
265bfd9d262SDmitri Tikhonov    {
266bfd9d262SDmitri Tikhonov        perror("mmap");
267bfd9d262SDmitri Tikhonov        exit(1);
268bfd9d262SDmitri Tikhonov    }
269bfd9d262SDmitri Tikhonov
270bfd9d262SDmitri Tikhonov    p = begin;
271bfd9d262SDmitri Tikhonov    end = begin + st.st_size;
272bfd9d262SDmitri Tikhonov    while (p + sizeof(stream_id) + sizeof(size) < end)
273bfd9d262SDmitri Tikhonov    {
274bfd9d262SDmitri Tikhonov        stream_id = * (uint64_t *) p;
275bfd9d262SDmitri Tikhonov        p += sizeof(uint64_t);
276bfd9d262SDmitri Tikhonov        size = * (uint32_t *) p;
277bfd9d262SDmitri Tikhonov        p += sizeof(uint32_t);
278bfd9d262SDmitri Tikhonov#if __BYTE_ORDER == __LITTLE_ENDIAN
279bfd9d262SDmitri Tikhonov        stream_id = bswap_64(stream_id);
280bfd9d262SDmitri Tikhonov        size = bswap_32(size);
281bfd9d262SDmitri Tikhonov#endif
282bfd9d262SDmitri Tikhonov        if (stream_id == 0)
283bfd9d262SDmitri Tikhonov        {
284446dba69SDmitri Tikhonov            r = lsqpack_dec_enc_in(&decoder, p, MIN(size, (uint32_t) (end - p)));
285bfd9d262SDmitri Tikhonov            (void) r;
286bfd9d262SDmitri Tikhonov        }
287bfd9d262SDmitri Tikhonov        else
288bfd9d262SDmitri Tikhonov        {
289bfd9d262SDmitri Tikhonov            const unsigned char *cur = p;
290bfd9d262SDmitri Tikhonov            enum lsqpack_read_header_status rhs;
291446dba69SDmitri Tikhonov            size = MIN(size, (uint32_t) (end - p));
292bfd9d262SDmitri Tikhonov            rhs = lsqpack_dec_header_in(&decoder, NULL, stream_id,
29360620859SDmitri Tikhonov                        size, &cur, size, NULL, NULL);
294bfd9d262SDmitri Tikhonov            (void) rhs;
295bfd9d262SDmitri Tikhonov        }
296bfd9d262SDmitri Tikhonov        break;
297bfd9d262SDmitri Tikhonov    }
298bfd9d262SDmitri Tikhonov
299bfd9d262SDmitri Tikhonov    munmap(begin, st.st_size);
300bfd9d262SDmitri Tikhonov    (void) close(fuzz_fd);
301bfd9d262SDmitri Tikhonov
302bfd9d262SDmitri Tikhonov    lsqpack_dec_cleanup(&decoder);
30360620859SDmitri Tikhonov    while (!LIST_EMPTY(&s_headers))
30460620859SDmitri Tikhonov    {
30560620859SDmitri Tikhonov        header = LIST_FIRST(&s_headers);
30660620859SDmitri Tikhonov        LIST_REMOVE(header, next_header);
30760620859SDmitri Tikhonov        free(header);
30860620859SDmitri Tikhonov    }
309bfd9d262SDmitri Tikhonov
310bfd9d262SDmitri Tikhonov    exit(0);
311bfd9d262SDmitri Tikhonov}
312205a2804SDmitri Tikhonov
313205a2804SDmitri Tikhonov#endif
314