xref: /freebsd/crypto/openssh/regress/unittests/test_helper/fuzz.c (revision ef1c128c05a64dc96083697fdbf6f045262f7844)
1 /*	$OpenBSD: fuzz.c,v 1.8 2015/03/03 20:42:49 djm Exp $	*/
2 /*
3  * Copyright (c) 2011 Damien Miller <djm@mindrot.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /* Utility functions/framework for fuzz tests */
19 
20 #include "includes.h"
21 
22 #include <sys/types.h>
23 #include <sys/uio.h>
24 
25 #include <assert.h>
26 #include <ctype.h>
27 #include <stdio.h>
28 #ifdef HAVE_STDINT_H
29 # include <stdint.h>
30 #endif
31 #include <stdlib.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <unistd.h>
35 
36 #include "test_helper.h"
37 #include "atomicio.h"
38 
39 /* #define FUZZ_DEBUG */
40 
41 #ifdef FUZZ_DEBUG
42 # define FUZZ_DBG(x) do { \
43 		printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \
44 		printf x; \
45 		printf("\n"); \
46 		fflush(stdout); \
47 	} while (0)
48 #else
49 # define FUZZ_DBG(x)
50 #endif
51 
52 /* For brevity later */
53 typedef unsigned long long fuzz_ullong;
54 
55 /* For base-64 fuzzing */
56 static const char fuzz_b64chars[] =
57     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
58 
59 struct fuzz {
60 	/* Fuzz method currently in use */
61 	int strategy;
62 
63 	/* Fuzz methods remaining */
64 	int strategies;
65 
66 	/* Original seed data blob */
67 	void *seed;
68 	size_t slen;
69 
70 	/* Current working copy of seed with fuzz mutations applied */
71 	u_char *fuzzed;
72 
73 	/* Used by fuzz methods */
74 	size_t o1, o2;
75 };
76 
77 static const char *
fuzz_ntop(u_int n)78 fuzz_ntop(u_int n)
79 {
80 	switch (n) {
81 	case 0:
82 		return "NONE";
83 	case FUZZ_1_BIT_FLIP:
84 		return "FUZZ_1_BIT_FLIP";
85 	case FUZZ_2_BIT_FLIP:
86 		return "FUZZ_2_BIT_FLIP";
87 	case FUZZ_1_BYTE_FLIP:
88 		return "FUZZ_1_BYTE_FLIP";
89 	case FUZZ_2_BYTE_FLIP:
90 		return "FUZZ_2_BYTE_FLIP";
91 	case FUZZ_TRUNCATE_START:
92 		return "FUZZ_TRUNCATE_START";
93 	case FUZZ_TRUNCATE_END:
94 		return "FUZZ_TRUNCATE_END";
95 	case FUZZ_BASE64:
96 		return "FUZZ_BASE64";
97 	default:
98 		abort();
99 	}
100 }
101 
102 static int
fuzz_fmt(struct fuzz * fuzz,char * s,size_t n)103 fuzz_fmt(struct fuzz *fuzz, char *s, size_t n)
104 {
105 	if (fuzz == NULL)
106 		return -1;
107 
108 	switch (fuzz->strategy) {
109 	case FUZZ_1_BIT_FLIP:
110 		snprintf(s, n, "%s case %zu of %zu (bit: %zu)\n",
111 		    fuzz_ntop(fuzz->strategy),
112 		    fuzz->o1, fuzz->slen * 8, fuzz->o1);
113 		return 0;
114 	case FUZZ_2_BIT_FLIP:
115 		snprintf(s, n, "%s case %llu of %llu (bits: %zu, %zu)\n",
116 		    fuzz_ntop(fuzz->strategy),
117 		    (((fuzz_ullong)fuzz->o2) * fuzz->slen * 8) + fuzz->o1,
118 		    ((fuzz_ullong)fuzz->slen * 8) * fuzz->slen * 8,
119 		    fuzz->o1, fuzz->o2);
120 		return 0;
121 	case FUZZ_1_BYTE_FLIP:
122 		snprintf(s, n, "%s case %zu of %zu (byte: %zu)\n",
123 		    fuzz_ntop(fuzz->strategy),
124 		    fuzz->o1, fuzz->slen, fuzz->o1);
125 		return 0;
126 	case FUZZ_2_BYTE_FLIP:
127 		snprintf(s, n, "%s case %llu of %llu (bytes: %zu, %zu)\n",
128 		    fuzz_ntop(fuzz->strategy),
129 		    (((fuzz_ullong)fuzz->o2) * fuzz->slen) + fuzz->o1,
130 		    ((fuzz_ullong)fuzz->slen) * fuzz->slen,
131 		    fuzz->o1, fuzz->o2);
132 		return 0;
133 	case FUZZ_TRUNCATE_START:
134 		snprintf(s, n, "%s case %zu of %zu (offset: %zu)\n",
135 		    fuzz_ntop(fuzz->strategy),
136 		    fuzz->o1, fuzz->slen, fuzz->o1);
137 		return 0;
138 	case FUZZ_TRUNCATE_END:
139 		snprintf(s, n, "%s case %zu of %zu (offset: %zu)\n",
140 		    fuzz_ntop(fuzz->strategy),
141 		    fuzz->o1, fuzz->slen, fuzz->o1);
142 		return 0;
143 	case FUZZ_BASE64:
144 		assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1);
145 		snprintf(s, n, "%s case %llu of %llu (offset: %zu char: %c)\n",
146 		    fuzz_ntop(fuzz->strategy),
147 		    (fuzz->o1 * (fuzz_ullong)64) + fuzz->o2,
148 		    fuzz->slen * (fuzz_ullong)64, fuzz->o1,
149 		    fuzz_b64chars[fuzz->o2]);
150 		return 0;
151 	default:
152 		return -1;
153 		abort();
154 	}
155 }
156 
157 static void
dump(u_char * p,size_t len)158 dump(u_char *p, size_t len)
159 {
160 	size_t i, j;
161 
162 	for (i = 0; i < len; i += 16) {
163 		fprintf(stderr, "%.4zd: ", i);
164 		for (j = i; j < i + 16; j++) {
165 			if (j < len)
166 				fprintf(stderr, "%02x ", p[j]);
167 			else
168 				fprintf(stderr, "   ");
169 		}
170 		fprintf(stderr, " ");
171 		for (j = i; j < i + 16; j++) {
172 			if (j < len) {
173 				if  (isascii(p[j]) && isprint(p[j]))
174 					fprintf(stderr, "%c", p[j]);
175 				else
176 					fprintf(stderr, ".");
177 			}
178 		}
179 		fprintf(stderr, "\n");
180 	}
181 }
182 
183 void
fuzz_dump(struct fuzz * fuzz)184 fuzz_dump(struct fuzz *fuzz)
185 {
186 	char buf[256];
187 
188 	if (fuzz_fmt(fuzz, buf, sizeof(buf)) != 0) {
189 		fprintf(stderr, "%s: fuzz invalid\n", __func__);
190 		abort();
191 	}
192 	fputs(buf, stderr);
193 	fprintf(stderr, "fuzz original %p len = %zu\n", fuzz->seed, fuzz->slen);
194 	dump(fuzz->seed, fuzz->slen);
195 	fprintf(stderr, "fuzz context %p len = %zu\n", fuzz, fuzz_len(fuzz));
196 	dump(fuzz_ptr(fuzz), fuzz_len(fuzz));
197 }
198 
199 static struct fuzz *last_fuzz;
200 
201 static void
siginfo(int unused)202 siginfo(int unused __attribute__((__unused__)))
203 {
204 	char buf[256];
205 
206 	test_info(buf, sizeof(buf));
207 	atomicio(vwrite, STDERR_FILENO, buf, strlen(buf));
208 	if (last_fuzz != NULL) {
209 		fuzz_fmt(last_fuzz, buf, sizeof(buf));
210 		atomicio(vwrite, STDERR_FILENO, buf, strlen(buf));
211 	}
212 }
213 
214 struct fuzz *
fuzz_begin(u_int strategies,const void * p,size_t l)215 fuzz_begin(u_int strategies, const void *p, size_t l)
216 {
217 	struct fuzz *ret = calloc(sizeof(*ret), 1);
218 
219 	assert(p != NULL);
220 	assert(ret != NULL);
221 	ret->seed = malloc(l);
222 	assert(ret->seed != NULL);
223 	memcpy(ret->seed, p, l);
224 	ret->slen = l;
225 	ret->strategies = strategies;
226 
227 	assert(ret->slen < SIZE_MAX / 8);
228 	assert(ret->strategies <= (FUZZ_MAX|(FUZZ_MAX-1)));
229 
230 	FUZZ_DBG(("begin, ret = %p", ret));
231 
232 	fuzz_next(ret);
233 
234 	last_fuzz = ret;
235 #ifdef SIGINFO
236 	signal(SIGINFO, siginfo);
237 #endif
238 	signal(SIGUSR1, siginfo);
239 
240 	return ret;
241 }
242 
243 void
fuzz_cleanup(struct fuzz * fuzz)244 fuzz_cleanup(struct fuzz *fuzz)
245 {
246 	FUZZ_DBG(("cleanup, fuzz = %p", fuzz));
247 	last_fuzz = NULL;
248 #ifdef SIGINFO
249 	signal(SIGINFO, SIG_DFL);
250 #endif
251 	signal(SIGUSR1, SIG_DFL);
252 	assert(fuzz != NULL);
253 	assert(fuzz->seed != NULL);
254 	assert(fuzz->fuzzed != NULL);
255 	free(fuzz->seed);
256 	free(fuzz->fuzzed);
257 	free(fuzz);
258 }
259 
260 static int
fuzz_strategy_done(struct fuzz * fuzz)261 fuzz_strategy_done(struct fuzz *fuzz)
262 {
263 	FUZZ_DBG(("fuzz = %p, strategy = %s, o1 = %zu, o2 = %zu, slen = %zu",
264 	    fuzz, fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->o2, fuzz->slen));
265 
266 	switch (fuzz->strategy) {
267 	case FUZZ_1_BIT_FLIP:
268 		return fuzz->o1 >= fuzz->slen * 8;
269 	case FUZZ_2_BIT_FLIP:
270 		return fuzz->o2 >= fuzz->slen * 8;
271 	case FUZZ_2_BYTE_FLIP:
272 		return fuzz->o2 >= fuzz->slen;
273 	case FUZZ_1_BYTE_FLIP:
274 	case FUZZ_TRUNCATE_START:
275 	case FUZZ_TRUNCATE_END:
276 	case FUZZ_BASE64:
277 		return fuzz->o1 >= fuzz->slen;
278 	default:
279 		abort();
280 	}
281 }
282 
283 void
fuzz_next(struct fuzz * fuzz)284 fuzz_next(struct fuzz *fuzz)
285 {
286 	u_int i;
287 
288 	FUZZ_DBG(("start, fuzz = %p, strategy = %s, strategies = 0x%lx, "
289 	    "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy),
290 	    (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen));
291 
292 	if (fuzz->strategy == 0 || fuzz_strategy_done(fuzz)) {
293 		/* If we are just starting out, we need to allocate too */
294 		if (fuzz->fuzzed == NULL) {
295 			FUZZ_DBG(("alloc"));
296 			fuzz->fuzzed = calloc(fuzz->slen, 1);
297 		}
298 		/* Pick next strategy */
299 		FUZZ_DBG(("advance"));
300 		for (i = 1; i <= FUZZ_MAX; i <<= 1) {
301 			if ((fuzz->strategies & i) != 0) {
302 				fuzz->strategy = i;
303 				break;
304 			}
305 		}
306 		FUZZ_DBG(("selected = %u", fuzz->strategy));
307 		if (fuzz->strategy == 0) {
308 			FUZZ_DBG(("done, no more strategies"));
309 			return;
310 		}
311 		fuzz->strategies &= ~(fuzz->strategy);
312 		fuzz->o1 = fuzz->o2 = 0;
313 	}
314 
315 	assert(fuzz->fuzzed != NULL);
316 
317 	switch (fuzz->strategy) {
318 	case FUZZ_1_BIT_FLIP:
319 		assert(fuzz->o1 / 8 < fuzz->slen);
320 		memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
321 		fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8);
322 		fuzz->o1++;
323 		break;
324 	case FUZZ_2_BIT_FLIP:
325 		assert(fuzz->o1 / 8 < fuzz->slen);
326 		assert(fuzz->o2 / 8 < fuzz->slen);
327 		memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
328 		fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8);
329 		fuzz->fuzzed[fuzz->o2 / 8] ^= 1 << (fuzz->o2 % 8);
330 		fuzz->o1++;
331 		if (fuzz->o1 >= fuzz->slen * 8) {
332 			fuzz->o1 = 0;
333 			fuzz->o2++;
334 		}
335 		break;
336 	case FUZZ_1_BYTE_FLIP:
337 		assert(fuzz->o1 < fuzz->slen);
338 		memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
339 		fuzz->fuzzed[fuzz->o1] ^= 0xff;
340 		fuzz->o1++;
341 		break;
342 	case FUZZ_2_BYTE_FLIP:
343 		assert(fuzz->o1 < fuzz->slen);
344 		assert(fuzz->o2 < fuzz->slen);
345 		memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
346 		fuzz->fuzzed[fuzz->o1] ^= 0xff;
347 		fuzz->fuzzed[fuzz->o2] ^= 0xff;
348 		fuzz->o1++;
349 		if (fuzz->o1 >= fuzz->slen) {
350 			fuzz->o1 = 0;
351 			fuzz->o2++;
352 		}
353 		break;
354 	case FUZZ_TRUNCATE_START:
355 	case FUZZ_TRUNCATE_END:
356 		assert(fuzz->o1 < fuzz->slen);
357 		memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
358 		fuzz->o1++;
359 		break;
360 	case FUZZ_BASE64:
361 		assert(fuzz->o1 < fuzz->slen);
362 		assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1);
363 		memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen);
364 		fuzz->fuzzed[fuzz->o1] = fuzz_b64chars[fuzz->o2];
365 		fuzz->o2++;
366 		if (fuzz->o2 >= sizeof(fuzz_b64chars) - 1) {
367 			fuzz->o2 = 0;
368 			fuzz->o1++;
369 		}
370 		break;
371 	default:
372 		abort();
373 	}
374 
375 	FUZZ_DBG(("done, fuzz = %p, strategy = %s, strategies = 0x%lx, "
376 	    "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy),
377 	    (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen));
378 }
379 
380 int
fuzz_matches_original(struct fuzz * fuzz)381 fuzz_matches_original(struct fuzz *fuzz)
382 {
383 	if (fuzz_len(fuzz) != fuzz->slen)
384 		return 0;
385 	return memcmp(fuzz_ptr(fuzz), fuzz->seed, fuzz->slen) == 0;
386 }
387 
388 int
fuzz_done(struct fuzz * fuzz)389 fuzz_done(struct fuzz *fuzz)
390 {
391 	FUZZ_DBG(("fuzz = %p, strategies = 0x%lx", fuzz,
392 	    (u_long)fuzz->strategies));
393 
394 	return fuzz_strategy_done(fuzz) && fuzz->strategies == 0;
395 }
396 
397 size_t
fuzz_len(struct fuzz * fuzz)398 fuzz_len(struct fuzz *fuzz)
399 {
400 	assert(fuzz->fuzzed != NULL);
401 	switch (fuzz->strategy) {
402 	case FUZZ_1_BIT_FLIP:
403 	case FUZZ_2_BIT_FLIP:
404 	case FUZZ_1_BYTE_FLIP:
405 	case FUZZ_2_BYTE_FLIP:
406 	case FUZZ_BASE64:
407 		return fuzz->slen;
408 	case FUZZ_TRUNCATE_START:
409 	case FUZZ_TRUNCATE_END:
410 		assert(fuzz->o1 <= fuzz->slen);
411 		return fuzz->slen - fuzz->o1;
412 	default:
413 		abort();
414 	}
415 }
416 
417 u_char *
fuzz_ptr(struct fuzz * fuzz)418 fuzz_ptr(struct fuzz *fuzz)
419 {
420 	assert(fuzz->fuzzed != NULL);
421 	switch (fuzz->strategy) {
422 	case FUZZ_1_BIT_FLIP:
423 	case FUZZ_2_BIT_FLIP:
424 	case FUZZ_1_BYTE_FLIP:
425 	case FUZZ_2_BYTE_FLIP:
426 	case FUZZ_BASE64:
427 		return fuzz->fuzzed;
428 	case FUZZ_TRUNCATE_START:
429 		assert(fuzz->o1 <= fuzz->slen);
430 		return fuzz->fuzzed + fuzz->o1;
431 	case FUZZ_TRUNCATE_END:
432 		assert(fuzz->o1 <= fuzz->slen);
433 		return fuzz->fuzzed;
434 	default:
435 		abort();
436 	}
437 }
438 
439