1/*
2 * Copyright (c) 2013 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2013 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/errno.h>
37#include <sys/stat.h>
38
39#include <ctype.h>
40#include <dirent.h>
41#include <dlfcn.h>
42#include <err.h>
43#include <fcntl.h>
44#include <limits.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49
50#include "check-common.h"
51#include "fuzzer.h"
52
53static char *leak_cmd = NULL;
54static void *dso;
55
56unsigned long count = 0;
57
58static void
59runTestcase(const char *name, const unsigned char *p, size_t length, heim_fuzz_type_t type)
60{
61    int (*decode_item)(const unsigned char *, size_t, void *, size_t *);
62    int (*free_item)(void *);
63    size_t (*size_item)(void);
64    char *decode_name, *free_name, *size_name;
65    size_t size;
66    void *data;
67    int ret;
68    unsigned char *copy;
69    unsigned long tcount = 1;
70    unsigned long lcount = 0;
71    size_t datasize = 10000;
72    struct map_page *data_map, *copy_map;
73    void *ctx = NULL;
74
75    if (type) {
76	printf("fuzzing using: %s\n", heim_fuzzer_name(type));
77	tcount = 10000000;
78    } else {
79	printf("non fuzzings\n");
80    }
81
82    asprintf(&decode_name, "decode_%s", name);
83    asprintf(&free_name, "free_%s", name);
84    asprintf(&size_name, "size_%s", name);
85
86    decode_item = dlsym(dso, decode_name);
87    free_item = dlsym(dso, free_name);
88    size_item = dlsym(dso, size_name);
89
90    free(decode_name);
91    free(free_name);
92
93    if (decode_item == NULL)
94	errx(1, "no decode_%s", name);
95    if (free_item == NULL)
96	errx(1, "no free_%s", name);
97
98    /* should export size_encoder */
99    if (size_item)
100	datasize = size_item();
101    else
102	datasize = 10000;
103
104
105    data = map_alloc(OVERRUN, NULL, datasize, &data_map);
106    memset(data, 0, datasize);
107
108    copy = map_alloc(OVERRUN, NULL, length, &copy_map);
109    memset(copy, 0, length);
110
111    /*
112     * Main fuzzer loop, keep modifying the input stream as long as it
113     * parses clearly.
114     */
115
116    memcpy(copy, p, length);
117    while (tcount > 0) {
118
119	if (type) {
120	    if (heim_fuzzer(type, &ctx, lcount, copy, length)) {
121		heim_fuzzer_free(type, ctx);
122		ctx = NULL;
123		break;
124	    }
125	}
126
127	ret = decode_item(copy, length, data, &size);
128	if (ret) {
129	    memcpy(copy, p, length);
130	} else {
131	    free_item(data);
132	}
133
134	tcount--;
135	count++;
136	lcount++;
137	if ((count & 0xffff) == 0) {
138	    printf("%lu...\n", (unsigned long)lcount);
139
140	    if (leak_cmd) {
141		memset(data, 0, datasize);
142		if (system(leak_cmd))
143		    abort();
144	    }
145	}
146    }
147
148    map_free(copy_map, "fuzzer", "copy");
149    map_free(data_map, "fuzzer", "data");
150}
151
152static void
153parseTestcase(const char *filename)
154{
155    struct stat sb;
156    char *p, *buf;
157    ssize_t sret;
158    size_t size;
159    int fd;
160
161    fd = open(filename, O_RDONLY, 0);
162    if (fd < 0) {
163	warn("failed to open: %s", filename);
164	return;
165    }
166    if (fstat(fd, &sb) != 0)
167	err(1, "failed to stat: %s", filename);
168    if (!S_ISREG(sb.st_mode)) {
169	close(fd);
170	return;
171    }
172
173    if (sb.st_size > (off_t)(SIZE_T_MAX >> 1))
174	errx(1, "%s to larger", filename);
175
176    buf = malloc((size_t)sb.st_size);
177    if (buf == NULL)
178	err(1, "malloc");
179    size = (size_t)sb.st_size;
180
181    sret = read(fd, buf, size);
182    if (sret < 0)
183	err(1, "read");
184    else if (sret != (ssize_t)size)
185	errx(1, "short read");
186
187    close(fd);
188
189    p = memchr(buf, '\0', size);
190    if (p && p != buf) {
191	p++;
192
193	runTestcase(buf, (const void *)p, size - (p - buf), NULL);
194	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_RANDOM);
195	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_BITFLIP);
196	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_BYTEFLIP);
197	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_SHORTFLIP);
198	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_WORDFLIP);
199	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_INTERESTING8);
200	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_INTERESTING16);
201	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_INTERESTING32);
202#if 0
203	runTestcase(buf, (const void *)p, size - (p - buf), HEIM_FUZZ_ASN1);
204#endif
205
206    } else {
207	warnx("file '%s' not a valid test case", filename);
208    }
209
210    free(buf);
211}
212
213int
214main(int argc, char **argv)
215{
216    const char *cmd;
217
218    if (getenv("MallocStackLogging") || getenv("MallocStackLoggingNoCompact"))
219	asprintf(&leak_cmd, "leaks %d > /tmp/leaks-log-pid-%d", (int)getpid(), (int)getpid());
220
221    dso = dlopen("/usr/local/lib/libheimdal-asn1-all-templates.dylib", RTLD_LAZY);
222    if (dso == NULL)
223	errx(1, "dlopen: %s", dlerror());
224
225    if (argc < 3)
226	errx(1, "missing command[fuzz-random-|][file|dir] and argument");
227
228
229    cmd = argv[1];
230
231    if (strcasecmp("dir", cmd) == 0) {
232	const char *dir = argv[2];
233	struct dirent *de;
234	DIR *d;
235
236	d = opendir(dir);
237	if (d == NULL)
238	    err(1, "opendir: %s", dir);
239
240	while ((de = readdir(d)) != NULL) {
241	    char *str;
242	    asprintf(&str, "%s/%.*s", dir, (int)de->d_namlen, de->d_name);
243
244	    parseTestcase(str);
245	    free(str);
246	}
247    } else if (strcasecmp("file", cmd) == 0) {
248	parseTestcase(argv[2]);
249    } else {
250	errx(1, "unknown command: %s", cmd);
251    }
252
253    printf("ran %lu test cases\n", count);
254
255    return 0;
256}
257