1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #include "test.h"
26 __FBSDID("$FreeBSD$");
27 
28 /*
29  * This was inspired by an ISO fuzz tester written by Michal Zalewski
30  * and posted to the "vulnwatch" mailing list on March 17, 2005:
31  *    http://seclists.org/vulnwatch/2005/q1/0088.html
32  *
33  * This test simply reads each archive image into memory, pokes
34  * random values into it and runs it through libarchive.  It tries
35  * to damage about 1% of each file and repeats the exercise 100 times
36  * with each file.
37  *
38  * Unlike most other tests, this test does not verify libarchive's
39  * responses other than to ensure that libarchive doesn't crash.
40  *
41  * Due to the deliberately random nature of this test, it may be hard
42  * to reproduce failures.  Because this test deliberately attempts to
43  * induce crashes, there's little that can be done in the way of
44  * post-failure diagnostics.
45  */
46 
47 /* Because this works for any archive, we can just re-use the archives
48  * developed for other tests. */
49 struct files {
50 	int uncompress; /* If 1, decompress the file before fuzzing. */
51 	const char **names;
52 };
53 
54 static void
55 test_fuzz(const struct files *filesets)
56 {
57 	const void *blk;
58 	size_t blk_size;
59 	int64_t blk_offset;
60 	int n;
61 	const char *skip_fuzz_tests;
62 
63 	skip_fuzz_tests = getenv("SKIP_TEST_FUZZ");
64 	if (skip_fuzz_tests != NULL) {
65 		skipping("Skipping fuzz tests due to SKIP_TEST_FUZZ "
66 		    "environment variable");
67 		return;
68 	}
69 
70 	for (n = 0; filesets[n].names != NULL; ++n) {
71 		const size_t buffsize = 30000000;
72 		struct archive_entry *ae;
73 		struct archive *a;
74 		char *rawimage = NULL, *image = NULL, *tmp = NULL;
75 		size_t size = 0, oldsize = 0;
76 		int i, q;
77 
78 		extract_reference_files(filesets[n].names);
79 		if (filesets[n].uncompress) {
80 			int r;
81 			/* Use format_raw to decompress the data. */
82 			assert((a = archive_read_new()) != NULL);
83 			assertEqualIntA(a, ARCHIVE_OK,
84 			    archive_read_support_filter_all(a));
85 			assertEqualIntA(a, ARCHIVE_OK,
86 			    archive_read_support_format_raw(a));
87 			r = archive_read_open_filenames(a, filesets[n].names, 16384);
88 			if (r != ARCHIVE_OK) {
89 				archive_read_free(a);
90 				if (filesets[n].names[0] == NULL || filesets[n].names[1] == NULL) {
91 					skipping("Cannot uncompress fileset");
92 				} else {
93 					skipping("Cannot uncompress %s", filesets[n].names[0]);
94 				}
95 				continue;
96 			}
97 			assertEqualIntA(a, ARCHIVE_OK,
98 			    archive_read_next_header(a, &ae));
99 			rawimage = malloc(buffsize);
100 			size = archive_read_data(a, rawimage, buffsize);
101 			assertEqualIntA(a, ARCHIVE_EOF,
102 			    archive_read_next_header(a, &ae));
103 			assertEqualInt(ARCHIVE_OK,
104 			    archive_read_free(a));
105 			assert(size > 0);
106 			if (filesets[n].names[0] == NULL || filesets[n].names[1] == NULL) {
107 				failure("Internal buffer is not big enough for "
108 					"uncompressed test files");
109 			} else {
110 				failure("Internal buffer is not big enough for "
111 					"uncompressed test file: %s", filesets[n].names[0]);
112 			}
113 			if (!assert(size < buffsize)) {
114 				free(rawimage);
115 				rawimage = NULL;
116 				continue;
117 			}
118 		} else {
119 			for (i = 0; filesets[n].names[i] != NULL; ++i)
120 			{
121 				char *newraw;
122 				tmp = slurpfile(&size, "%s",
123 						filesets[n].names[i]);
124 				newraw = realloc(rawimage, oldsize + size);
125 				if (!assert(newraw != NULL))
126 				{
127 					free(rawimage);
128 					rawimage = NULL;
129 					free(tmp);
130 					continue;
131 				}
132 				rawimage = newraw;
133 				memcpy(rawimage + oldsize, tmp, size);
134 				oldsize += size;
135 				size = oldsize;
136 				free(tmp);
137 			}
138 		}
139 		if (size == 0) {
140 			free(rawimage);
141 			rawimage = NULL;
142 			continue;
143 		}
144 		image = malloc(size);
145 		assert(image != NULL);
146 		if (image == NULL) {
147 			free(rawimage);
148 			rawimage = NULL;
149 			return;
150 		}
151 
152 		assert(rawimage != NULL);
153 
154 		srand((unsigned)time(NULL));
155 
156 		for (i = 0; i < 1000; ++i) {
157 			FILE *f;
158 			int j, numbytes, trycnt;
159 
160 			/* Fuzz < 1% of the bytes in the archive. */
161 			memcpy(image, rawimage, size);
162 			q = (int)size / 100;
163 			if (q < 4)
164 				q = 4;
165 			numbytes = (int)(rand() % q);
166 			for (j = 0; j < numbytes; ++j)
167 				image[rand() % size] = (char)rand();
168 
169 			/* Save the messed-up image to a file.
170 			 * If we crash, that file will be useful. */
171 			for (trycnt = 0; trycnt < 3; trycnt++) {
172 				f = fopen("after.test.failure.send.this.file."
173 				    "to.libarchive.maintainers.with.system.details", "wb");
174 				if (f != NULL)
175 					break;
176 #if defined(_WIN32) && !defined(__CYGWIN__)
177 				/*
178 				 * Sometimes previous close operation does not completely
179 				 * end at this time. So we should take a wait while
180 				 * the operation running.
181 				 */
182 				Sleep(100);
183 #endif
184 			}
185 			assert(f != NULL);
186 			assertEqualInt((size_t)size, fwrite(image, 1, (size_t)size, f));
187 			fclose(f);
188 
189 			// Try to read all headers and bodies.
190 			assert((a = archive_read_new()) != NULL);
191 			assertEqualIntA(a, ARCHIVE_OK,
192 			    archive_read_support_filter_all(a));
193 			assertEqualIntA(a, ARCHIVE_OK,
194 			    archive_read_support_format_all(a));
195 
196 			if (0 == archive_read_open_memory(a, image, size)) {
197 				while(0 == archive_read_next_header(a, &ae)) {
198 					while (0 == archive_read_data_block(a,
199 						&blk, &blk_size, &blk_offset))
200 						continue;
201 				}
202 				archive_read_close(a);
203 			}
204 			archive_read_free(a);
205 
206 			// Just list headers, skip bodies.
207 			assert((a = archive_read_new()) != NULL);
208 			assertEqualIntA(a, ARCHIVE_OK,
209 			    archive_read_support_filter_all(a));
210 			assertEqualIntA(a, ARCHIVE_OK,
211 			    archive_read_support_format_all(a));
212 
213 			if (0 == archive_read_open_memory(a, image, size)) {
214 				while(0 == archive_read_next_header(a, &ae)) {
215 				}
216 				archive_read_close(a);
217 			}
218 			archive_read_free(a);
219 		}
220 		free(image);
221 		free(rawimage);
222 	}
223 }
224 
225 DEFINE_TEST(test_fuzz_ar)
226 {
227 	static const char *fileset1[] = {
228 		"test_read_format_ar.ar",
229 		NULL
230 	};
231 	static const struct files filesets[] = {
232 		{0, fileset1},
233 		{1, NULL}
234 	};
235 	test_fuzz(filesets);
236 }
237 
238 DEFINE_TEST(test_fuzz_cab)
239 {
240 	static const char *fileset1[] = {
241 		"test_fuzz.cab",
242 		NULL
243 	};
244 	static const struct files filesets[] = {
245 		{0, fileset1},
246 		{1, NULL}
247 	};
248 	test_fuzz(filesets);
249 }
250 
251 DEFINE_TEST(test_fuzz_cpio)
252 {
253 	static const char *fileset1[] = {
254 		"test_read_format_cpio_bin_be.cpio",
255 		NULL
256 	};
257 	static const char *fileset2[] = {
258 		"test_read_format_cpio_bin_le.cpio",
259 		NULL
260 	};
261 	static const char *fileset3[] = {
262 		/* Test RPM unwrapper */
263 		"test_read_format_cpio_svr4_gzip_rpm.rpm",
264 		NULL
265 	};
266 	static const struct files filesets[] = {
267 		{0, fileset1},
268 		{0, fileset2},
269 		{0, fileset3},
270 		{1, NULL}
271 	};
272 	test_fuzz(filesets);
273 }
274 
275 DEFINE_TEST(test_fuzz_iso9660)
276 {
277 	static const char *fileset1[] = {
278 		"test_fuzz_1.iso.Z",
279 		NULL
280 	};
281 	static const struct files filesets[] = {
282 		{0, fileset1}, /* Exercise compress decompressor. */
283 		{1, fileset1},
284 		{1, NULL}
285 	};
286 	test_fuzz(filesets);
287 }
288 
289 DEFINE_TEST(test_fuzz_lzh)
290 {
291 	static const char *fileset1[] = {
292 		"test_fuzz.lzh",
293 		NULL
294 	};
295 	static const struct files filesets[] = {
296 		{0, fileset1},
297 		{1, NULL}
298 	};
299 	test_fuzz(filesets);
300 }
301 
302 DEFINE_TEST(test_fuzz_mtree)
303 {
304 	static const char *fileset1[] = {
305 		"test_read_format_mtree.mtree",
306 		NULL
307 	};
308 	static const struct files filesets[] = {
309 		{0, fileset1},
310 		{1, NULL}
311 	};
312 	test_fuzz(filesets);
313 }
314 
315 DEFINE_TEST(test_fuzz_rar)
316 {
317 	static const char *fileset1[] = {
318 		/* Uncompressed RAR test */
319 		"test_read_format_rar.rar",
320 		NULL
321 	};
322 	static const char *fileset2[] = {
323 		/* RAR file with binary data */
324 		"test_read_format_rar_binary_data.rar",
325 		NULL
326 	};
327 	static const char *fileset3[] = {
328 		/* Best Compressed RAR test */
329 		"test_read_format_rar_compress_best.rar",
330 		NULL
331 	};
332 	static const char *fileset4[] = {
333 		/* Normal Compressed RAR test */
334 		"test_read_format_rar_compress_normal.rar",
335 		NULL
336 	};
337 	static const char *fileset5[] = {
338 		/* Normal Compressed Multi LZSS blocks RAR test */
339 		"test_read_format_rar_multi_lzss_blocks.rar",
340 		NULL
341 	};
342 	static const char *fileset6[] = {
343 		/* RAR with no EOF header */
344 		"test_read_format_rar_noeof.rar",
345 		NULL
346 	};
347 	static const char *fileset7[] = {
348 		/* Best Compressed RAR file with both PPMd and LZSS blocks */
349 		"test_read_format_rar_ppmd_lzss_conversion.rar",
350 		NULL
351 	};
352 	static const char *fileset8[] = {
353 		/* RAR with subblocks */
354 		"test_read_format_rar_subblock.rar",
355 		NULL
356 	};
357 	static const char *fileset9[] = {
358 		/* RAR with Unicode filenames */
359 		"test_read_format_rar_unicode.rar",
360 		NULL
361 	};
362 	static const char *fileset10[] = {
363 		"test_read_format_rar_multivolume.part0001.rar",
364 		"test_read_format_rar_multivolume.part0002.rar",
365 		"test_read_format_rar_multivolume.part0003.rar",
366 		"test_read_format_rar_multivolume.part0004.rar",
367 		NULL
368 	};
369 	static const struct files filesets[] = {
370 		{0, fileset1},
371 		{0, fileset2},
372 		{0, fileset3},
373 		{0, fileset4},
374 		{0, fileset5},
375 		{0, fileset6},
376 		{0, fileset7},
377 		{0, fileset8},
378 		{0, fileset9},
379 		{0, fileset10},
380 		{1, NULL}
381 	};
382 	test_fuzz(filesets);
383 }
384 
385 DEFINE_TEST(test_fuzz_tar)
386 {
387 	static const char *fileset1[] = {
388 		"test_compat_bzip2_1.tbz",
389 		NULL
390 	};
391 	static const char *fileset2[] = {
392 		"test_compat_gtar_1.tar",
393 		NULL
394 	};
395 	static const char *fileset3[] = {
396 		"test_compat_gzip_1.tgz",
397 		NULL
398 	};
399 	static const char *fileset4[] = {
400 		"test_compat_gzip_2.tgz",
401 		NULL
402 	};
403 	static const char *fileset5[] = {
404 		"test_compat_tar_hardlink_1.tar",
405 		NULL
406 	};
407 	static const char *fileset6[] = {
408 		"test_compat_xz_1.txz",
409 		NULL
410 	};
411 	static const char *fileset7[] = {
412 		"test_read_format_gtar_sparse_1_17_posix10_modified.tar",
413 		NULL
414 	};
415 	static const char *fileset8[] = {
416 		"test_read_format_tar_empty_filename.tar",
417 		NULL
418 	};
419 #if HAVE_LIBLZO2 && HAVE_LZO_LZO1X_H && HAVE_LZO_LZOCONF_H
420 	static const char *fileset9[] = {
421 		"test_compat_lzop_1.tar.lzo",
422 		NULL
423 	};
424 #endif
425 #if HAVE_ZSTD_H && HAVE_LIBZSTD
426 	static const char *fileset10[] = {
427 		"test_compat_zstd_1.tar.zst",
428 		NULL
429 	};
430 #endif
431 	static const struct files filesets[] = {
432 		{0, fileset1}, /* Exercise bzip2 decompressor. */
433 		{1, fileset1},
434 		{0, fileset2},
435 		{0, fileset3}, /* Exercise gzip decompressor. */
436 		{0, fileset4}, /* Exercise gzip decompressor. */
437 		{0, fileset5},
438 		{0, fileset6}, /* Exercise xz decompressor. */
439 		{0, fileset7},
440 		{0, fileset8},
441 #if HAVE_LIBLZO2 && HAVE_LZO_LZO1X_H && HAVE_LZO_LZOCONF_H
442 		{0, fileset9}, /* Exercise lzo decompressor. */
443 #endif
444 #if HAVE_ZSTD_H && HAVE_LIBZSTD
445 		{0, fileset10}, /* Exercise zstd decompressor. */
446 #endif
447 		{1, NULL}
448 	};
449 	test_fuzz(filesets);
450 }
451 
452 DEFINE_TEST(test_fuzz_zip)
453 {
454 	static const char *fileset1[] = {
455 		"test_compat_zip_1.zip",
456 		NULL
457 	};
458 	static const char *fileset2[] = {
459 		"test_compat_zip_2.zip",
460 		NULL
461 	};
462 	static const char *fileset3[] = {
463 		"test_compat_zip_3.zip",
464 		NULL
465 	};
466 	static const char *fileset4[] = {
467 		"test_compat_zip_4.zip",
468 		NULL
469 	};
470 	static const char *fileset5[] = {
471 		"test_compat_zip_5.zip",
472 		NULL
473 	};
474 	static const char *fileset6[] = {
475 		"test_compat_zip_6.zip",
476 		NULL
477 	};
478 	static const char *fileset7[] = {
479 		"test_read_format_zip.zip",
480 		NULL
481 	};
482 	static const char *fileset8[] = {
483 		"test_read_format_zip_comment_stored_1.zip",
484 		NULL
485 	};
486 	static const char *fileset9[] = {
487 		"test_read_format_zip_comment_stored_2.zip",
488 		NULL
489 	};
490 	static const char *fileset10[] = {
491 		"test_read_format_zip_encryption_data.zip",
492 		NULL
493 	};
494 	static const char *fileset11[] = {
495 		"test_read_format_zip_encryption_header.zip",
496 		NULL
497 	};
498 	static const char *fileset12[] = {
499 		"test_read_format_zip_encryption_partially.zip",
500 		NULL
501 	};
502 	static const char *fileset13[] = {
503 		"test_read_format_zip_filename_cp866.zip",
504 		NULL
505 	};
506 	static const char *fileset14[] = {
507 		"test_read_format_zip_filename_cp932.zip",
508 		NULL
509 	};
510 	static const char *fileset15[] = {
511 		"test_read_format_zip_filename_koi8r.zip",
512 		NULL
513 	};
514 	static const char *fileset16[] = {
515 		"test_read_format_zip_filename_utf8_jp.zip",
516 		NULL
517 	};
518 	static const char *fileset17[] = {
519 		"test_read_format_zip_filename_utf8_ru.zip",
520 		NULL
521 	};
522 	static const char *fileset18[] = {
523 		"test_read_format_zip_filename_utf8_ru2.zip",
524 		NULL
525 	};
526 	static const char *fileset19[] = {
527 		"test_read_format_zip_length_at_end.zip",
528 		NULL
529 	};
530 	static const char *fileset20[] = {
531 		"test_read_format_zip_mac_metadata.zip",
532 		NULL
533 	};
534 	static const char *fileset21[] = {
535 		"test_read_format_zip_malformed1.zip",
536 		NULL
537 	};
538 	static const char *fileset22[] = {
539 		"test_read_format_zip_msdos.zip",
540 		NULL
541 	};
542 	static const char *fileset23[] = {
543 		"test_read_format_zip_nested.zip",
544 		NULL
545 	};
546 	static const char *fileset24[] = {
547 		"test_read_format_zip_nofiletype.zip",
548 		NULL
549 	};
550 	static const char *fileset25[] = {
551 		"test_read_format_zip_padded1.zip",
552 		NULL
553 	};
554 	static const char *fileset26[] = {
555 		"test_read_format_zip_padded2.zip",
556 		NULL
557 	};
558 	static const char *fileset27[] = {
559 		"test_read_format_zip_padded3.zip",
560 		NULL
561 	};
562 	static const char *fileset28[] = {
563 		"test_read_format_zip_symlink.zip",
564 		NULL
565 	};
566 	static const char *fileset29[] = {
567 		"test_read_format_zip_traditional_encryption_data.zip",
568 		NULL
569 	};
570 	static const char *fileset30[] = {
571 		"test_read_format_zip_ux.zip",
572 		NULL
573 	};
574 	static const char *fileset31[] = {
575 		"test_read_format_zip_winzip_aes128.zip",
576 		NULL
577 	};
578 	static const char *fileset32[] = {
579 		"test_read_format_zip_winzip_aes256.zip",
580 		NULL
581 	};
582 	static const char *fileset33[] = {
583 		"test_read_format_zip_winzip_aes256_large.zip",
584 		NULL
585 	};
586 	static const char *fileset34[] = {
587 		"test_read_format_zip_winzip_aes256_stored.zip",
588 		NULL
589 	};
590 	static const char *fileset35[] = {
591 		"test_read_format_zip_zip64a.zip",
592 		NULL
593 	};
594 	static const char *fileset36[] = {
595 		"test_read_format_zip_zip64b.zip",
596 		NULL
597 	};
598 
599 	static const struct files filesets[] = {
600 		{0, fileset1},
601 		{0, fileset2},
602 		{0, fileset3},
603 		{0, fileset4},
604 		{0, fileset5},
605 		{0, fileset6},
606 		{0, fileset7},
607 		{0, fileset8},
608 		{0, fileset9},
609 		{0, fileset10},
610 		{0, fileset11},
611 		{0, fileset12},
612 		{0, fileset13},
613 		{0, fileset14},
614 		{0, fileset15},
615 		{0, fileset16},
616 		{0, fileset17},
617 		{0, fileset18},
618 		{0, fileset19},
619 		{0, fileset20},
620 		{0, fileset21},
621 		{0, fileset22},
622 		{0, fileset23},
623 		{0, fileset24},
624 		{0, fileset25},
625 		{0, fileset26},
626 		{0, fileset27},
627 		{0, fileset28},
628 		{0, fileset29},
629 		{0, fileset30},
630 		{0, fileset31},
631 		{0, fileset32},
632 		{0, fileset33},
633 		{0, fileset34},
634 		{0, fileset35},
635 		{0, fileset36},
636 		{1, NULL}
637 	};
638 	test_fuzz(filesets);
639 }
640 
641