1228753Smm/*-
2228753Smm * Copyright (c) 2003-2007 Tim Kientzle
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24228753Smm */
25228753Smm#include "test.h"
26228763Smm__FBSDID("$FreeBSD$");
27228753Smm
28228753Smm/*
29228753Smm * This was inspired by an ISO fuzz tester written by Michal Zalewski
30228753Smm * and posted to the "vulnwatch" mailing list on March 17, 2005:
31228753Smm *    http://seclists.org/vulnwatch/2005/q1/0088.html
32228753Smm *
33228753Smm * This test simply reads each archive image into memory, pokes
34228753Smm * random values into it and runs it through libarchive.  It tries
35228753Smm * to damage about 1% of each file and repeats the exercise 100 times
36228753Smm * with each file.
37228753Smm *
38228753Smm * Unlike most other tests, this test does not verify libarchive's
39228753Smm * responses other than to ensure that libarchive doesn't crash.
40228753Smm *
41228753Smm * Due to the deliberately random nature of this test, it may be hard
42228753Smm * to reproduce failures.  Because this test deliberately attempts to
43228753Smm * induce crashes, there's little that can be done in the way of
44228753Smm * post-failure diagnostics.
45228753Smm */
46228753Smm
47228753Smm/* Because this works for any archive, we can just re-use the archives
48228753Smm * developed for other tests. */
49248616Smmstruct files {
50228753Smm	int uncompress; /* If 1, decompress the file before fuzzing. */
51248616Smm	const char **names;
52228753Smm};
53228753Smm
54248616Smmstatic void
55248616Smmtest_fuzz(const struct files *filesets)
56228753Smm{
57228753Smm	const void *blk;
58228753Smm	size_t blk_size;
59232153Smm	int64_t blk_offset;
60228753Smm	int n;
61228753Smm
62248616Smm	for (n = 0; filesets[n].names != NULL; ++n) {
63228753Smm		const size_t buffsize = 30000000;
64228753Smm		struct archive_entry *ae;
65228753Smm		struct archive *a;
66248616Smm		char *rawimage = NULL, *image = NULL, *tmp = NULL;
67248616Smm		size_t size = 0, oldsize = 0;
68232153Smm		int i, q;
69228753Smm
70248616Smm		extract_reference_files(filesets[n].names);
71248616Smm		if (filesets[n].uncompress) {
72228753Smm			int r;
73228753Smm			/* Use format_raw to decompress the data. */
74228753Smm			assert((a = archive_read_new()) != NULL);
75228753Smm			assertEqualIntA(a, ARCHIVE_OK,
76232153Smm			    archive_read_support_filter_all(a));
77228753Smm			assertEqualIntA(a, ARCHIVE_OK,
78228753Smm			    archive_read_support_format_raw(a));
79248616Smm			r = archive_read_open_filenames(a, filesets[n].names, 16384);
80228753Smm			if (r != ARCHIVE_OK) {
81232153Smm				archive_read_free(a);
82248616Smm				if (filesets[n].names[0] == NULL || filesets[n].names[1] == NULL) {
83248616Smm					skipping("Cannot uncompress fileset");
84248616Smm				} else {
85248616Smm					skipping("Cannot uncompress %s", filesets[n].names[0]);
86248616Smm				}
87228753Smm				continue;
88228753Smm			}
89228753Smm			assertEqualIntA(a, ARCHIVE_OK,
90228753Smm			    archive_read_next_header(a, &ae));
91228753Smm			rawimage = malloc(buffsize);
92228753Smm			size = archive_read_data(a, rawimage, buffsize);
93228753Smm			assertEqualIntA(a, ARCHIVE_EOF,
94228753Smm			    archive_read_next_header(a, &ae));
95228753Smm			assertEqualInt(ARCHIVE_OK,
96232153Smm			    archive_read_free(a));
97228753Smm			assert(size > 0);
98248616Smm			if (filesets[n].names[0] == NULL || filesets[n].names[1] == NULL) {
99248616Smm				failure("Internal buffer is not big enough for "
100248616Smm					"uncompressed test files");
101248616Smm			} else {
102248616Smm				failure("Internal buffer is not big enough for "
103248616Smm					"uncompressed test file: %s", filesets[n].names[0]);
104248616Smm			}
105228753Smm			if (!assert(size < buffsize)) {
106228753Smm				free(rawimage);
107228753Smm				continue;
108228753Smm			}
109228753Smm		} else {
110248616Smm			for (i = 0; filesets[n].names[i] != NULL; ++i)
111248616Smm			{
112248616Smm				tmp = slurpfile(&size, filesets[n].names[i]);
113248616Smm				rawimage = (char *)realloc(rawimage, oldsize + size);
114248616Smm				memcpy(rawimage + oldsize, tmp, size);
115248616Smm				oldsize += size;
116248616Smm				size = oldsize;
117248616Smm				free(tmp);
118248616Smm				if (!assert(rawimage != NULL))
119248616Smm					continue;
120248616Smm			}
121228753Smm		}
122248616Smm		if (size == 0)
123248616Smm			continue;
124228753Smm		image = malloc(size);
125228753Smm		assert(image != NULL);
126248616Smm		if (image == NULL)
127248616Smm			return;
128228753Smm		srand((unsigned)time(NULL));
129228753Smm
130228753Smm		for (i = 0; i < 100; ++i) {
131228753Smm			FILE *f;
132232153Smm			int j, numbytes, trycnt;
133228753Smm
134228753Smm			/* Fuzz < 1% of the bytes in the archive. */
135228753Smm			memcpy(image, rawimage, size);
136248616Smm			q = (int)size / 100;
137232153Smm			if (!q) q = 1;
138232153Smm			numbytes = (int)(rand() % q);
139228753Smm			for (j = 0; j < numbytes; ++j)
140228753Smm				image[rand() % size] = (char)rand();
141228753Smm
142228753Smm			/* Save the messed-up image to a file.
143228753Smm			 * If we crash, that file will be useful. */
144232153Smm			for (trycnt = 0; trycnt < 3; trycnt++) {
145232153Smm				f = fopen("after.test.failure.send.this.file."
146232153Smm				    "to.libarchive.maintainers.with.system.details", "wb");
147232153Smm				if (f != NULL)
148232153Smm					break;
149232153Smm#if defined(_WIN32) && !defined(__CYGWIN__)
150232153Smm				/*
151232153Smm				 * Sometimes previous close operation does not completely
152232153Smm				 * end at this time. So we should take a wait while
153232153Smm				 * the operation running.
154232153Smm				 */
155232153Smm				Sleep(100);
156232153Smm#endif
157232153Smm			}
158232153Smm			assertEqualInt((size_t)size, fwrite(image, 1, (size_t)size, f));
159228753Smm			fclose(f);
160228753Smm
161228753Smm			assert((a = archive_read_new()) != NULL);
162228753Smm			assertEqualIntA(a, ARCHIVE_OK,
163232153Smm			    archive_read_support_filter_all(a));
164228753Smm			assertEqualIntA(a, ARCHIVE_OK,
165228753Smm			    archive_read_support_format_all(a));
166228753Smm
167228753Smm			if (0 == archive_read_open_memory(a, image, size)) {
168228753Smm				while(0 == archive_read_next_header(a, &ae)) {
169228753Smm					while (0 == archive_read_data_block(a,
170228753Smm						&blk, &blk_size, &blk_offset))
171228753Smm						continue;
172228753Smm				}
173228753Smm				archive_read_close(a);
174228753Smm			}
175232153Smm			archive_read_free(a);
176228753Smm		}
177228753Smm		free(image);
178228753Smm		free(rawimage);
179228753Smm	}
180228753Smm}
181228753Smm
182248616SmmDEFINE_TEST(test_fuzz_ar)
183248616Smm{
184248616Smm	static const char *fileset1[] = {
185248616Smm		"test_read_format_ar.ar",
186248616Smm		NULL
187248616Smm	};
188248616Smm	static const struct files filesets[] = {
189248616Smm		{0, fileset1},
190248616Smm		{1, NULL}
191248616Smm	};
192248616Smm	test_fuzz(filesets);
193248616Smm}
194228753Smm
195248616SmmDEFINE_TEST(test_fuzz_cab)
196248616Smm{
197248616Smm	static const char *fileset1[] = {
198248616Smm		"test_fuzz.cab",
199248616Smm		NULL
200248616Smm	};
201248616Smm	static const struct files filesets[] = {
202248616Smm		{0, fileset1},
203248616Smm		{1, NULL}
204248616Smm	};
205248616Smm	test_fuzz(filesets);
206248616Smm}
207248616Smm
208248616SmmDEFINE_TEST(test_fuzz_cpio)
209248616Smm{
210248616Smm	static const char *fileset1[] = {
211248616Smm		"test_read_format_cpio_bin_be.cpio",
212248616Smm		NULL
213248616Smm	};
214248616Smm	static const char *fileset2[] = {
215248616Smm		/* Test RPM unwrapper */
216248616Smm		"test_read_format_cpio_svr4_gzip_rpm.rpm",
217248616Smm		NULL
218248616Smm	};
219248616Smm	static const struct files filesets[] = {
220248616Smm		{0, fileset1},
221248616Smm		{0, fileset2},
222248616Smm		{1, NULL}
223248616Smm	};
224248616Smm	test_fuzz(filesets);
225248616Smm}
226248616Smm
227248616SmmDEFINE_TEST(test_fuzz_iso9660)
228248616Smm{
229248616Smm	static const char *fileset1[] = {
230248616Smm		"test_fuzz_1.iso.Z",
231248616Smm		NULL
232248616Smm	};
233248616Smm	static const struct files filesets[] = {
234248616Smm		{0, fileset1}, /* Exercise compress decompressor. */
235248616Smm		{1, fileset1},
236248616Smm		{1, NULL}
237248616Smm	};
238248616Smm	test_fuzz(filesets);
239248616Smm}
240248616Smm
241248616SmmDEFINE_TEST(test_fuzz_lzh)
242248616Smm{
243248616Smm	static const char *fileset1[] = {
244248616Smm		"test_fuzz.lzh",
245248616Smm		NULL
246248616Smm	};
247248616Smm	static const struct files filesets[] = {
248248616Smm		{0, fileset1},
249248616Smm		{1, NULL}
250248616Smm	};
251248616Smm	test_fuzz(filesets);
252248616Smm}
253248616Smm
254248616SmmDEFINE_TEST(test_fuzz_mtree)
255248616Smm{
256248616Smm	static const char *fileset1[] = {
257248616Smm		"test_read_format_mtree.mtree",
258248616Smm		NULL
259248616Smm	};
260248616Smm	static const struct files filesets[] = {
261248616Smm		{0, fileset1},
262248616Smm		{1, NULL}
263248616Smm	};
264248616Smm	test_fuzz(filesets);
265248616Smm}
266248616Smm
267248616SmmDEFINE_TEST(test_fuzz_rar)
268248616Smm{
269248616Smm	static const char *fileset1[] = {
270248616Smm		/* Uncompressed RAR test */
271248616Smm		"test_read_format_rar.rar",
272248616Smm		NULL
273248616Smm	};
274248616Smm	static const char *fileset2[] = {
275248616Smm		/* RAR file with binary data */
276248616Smm		"test_read_format_rar_binary_data.rar",
277248616Smm		NULL
278248616Smm	};
279248616Smm	static const char *fileset3[] = {
280248616Smm		/* Best Compressed RAR test */
281248616Smm		"test_read_format_rar_compress_best.rar",
282248616Smm		NULL
283248616Smm	};
284248616Smm	static const char *fileset4[] = {
285248616Smm		/* Normal Compressed RAR test */
286248616Smm		"test_read_format_rar_compress_normal.rar",
287248616Smm		NULL
288248616Smm	};
289248616Smm	static const char *fileset5[] = {
290248616Smm		/* Normal Compressed Multi LZSS blocks RAR test */
291248616Smm		"test_read_format_rar_multi_lzss_blocks.rar",
292248616Smm		NULL
293248616Smm	};
294248616Smm	static const char *fileset6[] = {
295248616Smm		/* RAR with no EOF header */
296248616Smm		"test_read_format_rar_noeof.rar",
297248616Smm		NULL
298248616Smm	};
299248616Smm	static const char *fileset7[] = {
300248616Smm		/* Best Compressed RAR file with both PPMd and LZSS blocks */
301248616Smm		"test_read_format_rar_ppmd_lzss_conversion.rar",
302248616Smm		NULL
303248616Smm	};
304248616Smm	static const char *fileset8[] = {
305248616Smm		/* RAR with subblocks */
306248616Smm		"test_read_format_rar_subblock.rar",
307248616Smm		NULL
308248616Smm	};
309248616Smm	static const char *fileset9[] = {
310248616Smm		/* RAR with Unicode filenames */
311248616Smm		"test_read_format_rar_unicode.rar",
312248616Smm		NULL
313248616Smm	};
314248616Smm	static const char *fileset10[] = {
315248616Smm		"test_read_format_rar_multivolume.part0001.rar",
316248616Smm		"test_read_format_rar_multivolume.part0002.rar",
317248616Smm		"test_read_format_rar_multivolume.part0003.rar",
318248616Smm		"test_read_format_rar_multivolume.part0004.rar",
319248616Smm		NULL
320248616Smm	};
321248616Smm	static const struct files filesets[] = {
322248616Smm		{0, fileset1},
323248616Smm		{0, fileset2},
324248616Smm		{0, fileset3},
325248616Smm		{0, fileset4},
326248616Smm		{0, fileset5},
327248616Smm		{0, fileset6},
328248616Smm		{0, fileset7},
329248616Smm		{0, fileset8},
330248616Smm		{0, fileset9},
331248616Smm		{0, fileset10},
332248616Smm		{1, NULL}
333248616Smm	};
334248616Smm	test_fuzz(filesets);
335248616Smm}
336248616Smm
337248616SmmDEFINE_TEST(test_fuzz_tar)
338248616Smm{
339248616Smm	static const char *fileset1[] = {
340248616Smm		"test_compat_bzip2_1.tbz",
341248616Smm		NULL
342248616Smm	};
343248616Smm	static const char *fileset2[] = {
344248616Smm		"test_compat_gtar_1.tar",
345248616Smm		NULL
346248616Smm	};
347248616Smm	static const char *fileset3[] = {
348248616Smm		"test_compat_gzip_1.tgz",
349248616Smm		NULL
350248616Smm	};
351248616Smm	static const char *fileset4[] = {
352248616Smm		"test_compat_gzip_2.tgz",
353248616Smm		NULL
354248616Smm	};
355248616Smm	static const char *fileset5[] = {
356248616Smm		"test_compat_tar_hardlink_1.tar",
357248616Smm		NULL
358248616Smm	};
359248616Smm	static const char *fileset6[] = {
360248616Smm		"test_compat_xz_1.txz",
361248616Smm		NULL
362248616Smm	};
363248616Smm	static const char *fileset7[] = {
364248616Smm		"test_read_format_gtar_sparse_1_17_posix10_modified.tar",
365248616Smm		NULL
366248616Smm	};
367248616Smm	static const char *fileset8[] = {
368248616Smm		"test_read_format_tar_empty_filename.tar",
369248616Smm		NULL
370248616Smm	};
371248616Smm	static const char *fileset9[] = {
372248616Smm		"test_compat_lzop_1.tar.lzo",
373248616Smm		NULL
374248616Smm	};
375248616Smm	static const struct files filesets[] = {
376248616Smm		{0, fileset1}, /* Exercise bzip2 decompressor. */
377248616Smm		{1, fileset1},
378248616Smm		{0, fileset2},
379248616Smm		{0, fileset3}, /* Exercise gzip decompressor. */
380248616Smm		{0, fileset4}, /* Exercise gzip decompressor. */
381248616Smm		{0, fileset5},
382248616Smm		{0, fileset6}, /* Exercise xz decompressor. */
383248616Smm		{0, fileset7},
384248616Smm		{0, fileset8},
385248616Smm		{0, fileset9}, /* Exercise lzo decompressor. */
386248616Smm		{1, NULL}
387248616Smm	};
388248616Smm	test_fuzz(filesets);
389248616Smm}
390248616Smm
391248616SmmDEFINE_TEST(test_fuzz_zip)
392248616Smm{
393248616Smm	static const char *fileset1[] = {
394248616Smm		"test_compat_zip_1.zip",
395248616Smm		NULL
396248616Smm	};
397248616Smm	static const char *fileset2[] = {
398248616Smm		"test_read_format_zip.zip",
399248616Smm		NULL
400248616Smm	};
401248616Smm	static const struct files filesets[] = {
402248616Smm		{0, fileset1},
403248616Smm		{0, fileset2},
404248616Smm		{1, NULL}
405248616Smm	};
406248616Smm	test_fuzz(filesets);
407248616Smm}
408248616Smm
409