xref: /freebsd/contrib/libarchive/libarchive/test/test_write_format_mtree_null_deref.c (revision 185becb1e1bd2657c156f78aeb52edac05ba5fb5)
1 /*-
2  * Copyright (c) 2025 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 #include "test_fuzz_consumer.h"
27 
28 #include <stdlib.h>
29 
30 /*
31  * Replay a fuzzer binary through the MTREE writer, matching the protocol
32  * in fuzzers/custom/fuzz_writer_mtree.cc.
33  */
DEFINE_TEST(test_write_format_mtree_null_deref)34 DEFINE_TEST(test_write_format_mtree_null_deref)
35 {
36 	const char *refname = "test_write_format_mtree_null_deref.bin";
37 	FILE *f;
38 	uint8_t raw[16384];
39 	size_t rawsize;
40 	struct fuzz_consumer consumer;
41 	uint8_t opts, num_entries;
42 	struct archive *a;
43 	struct archive_entry *entry;
44 	size_t used;
45 	void *out_buf;
46 	int i;
47 
48 	extract_reference_file(refname);
49 	f = fopen(refname, "rb");
50 	if (!assert(f != NULL))
51 		return;
52 	rawsize = fread(raw, 1, sizeof(raw), f);
53 	fclose(f);
54 	if (!assert(rawsize >= 4))
55 		return;
56 
57 	fuzz_consumer_init(&consumer, raw, rawsize);
58 	opts = fuzz_consume_byte(&consumer);
59 	num_entries = (fuzz_consume_byte(&consumer) % 8) + 1;
60 
61 	a = archive_write_new();
62 	if (!assert(a != NULL))
63 		return;
64 
65 	if (opts & 0x01)
66 		archive_write_set_format_mtree_classic(a);
67 	else
68 		archive_write_set_format_mtree(a);
69 
70 	if (opts & 0x02)
71 		archive_write_set_options(a, "mtree:all");
72 	if (opts & 0x04)
73 		archive_write_set_options(a, "mtree:use-set");
74 	if (opts & 0x08)
75 		archive_write_set_options(a, "mtree:indent");
76 	if (opts & 0x10)
77 		archive_write_set_options(a, "mtree:dironly");
78 
79 	out_buf = malloc(256 * 1024);
80 	if (!assert(out_buf != NULL)) {
81 		archive_write_free(a);
82 		return;
83 	}
84 	if (archive_write_open_memory(a, out_buf, 256 * 1024, &used)
85 	    != ARCHIVE_OK) {
86 		archive_write_free(a);
87 		free(out_buf);
88 		return;
89 	}
90 
91 	entry = archive_entry_new();
92 	if (!assert(entry != NULL)) {
93 		archive_write_free(a);
94 		free(out_buf);
95 		return;
96 	}
97 
98 	for (i = 0; i < num_entries && fuzz_consumer_remaining(&consumer) > 2;
99 	    i++) {
100 		const char *name;
101 		uint8_t ftype;
102 		uint32_t file_size = 0;
103 
104 		archive_entry_clear(entry);
105 
106 		name = fuzz_consume_string(&consumer, 128);
107 		if (name[0] == '\0')
108 			name = "file.txt";
109 		archive_entry_set_pathname(entry, name);
110 
111 		ftype = fuzz_consume_byte(&consumer) % 5;
112 		switch (ftype) {
113 		case 0:
114 			archive_entry_set_filetype(entry, AE_IFREG);
115 			archive_entry_set_perm(entry, 0644);
116 			break;
117 		case 1:
118 			archive_entry_set_filetype(entry, AE_IFDIR);
119 			archive_entry_set_perm(entry, 0755);
120 			break;
121 		case 2:
122 			archive_entry_set_filetype(entry, AE_IFLNK);
123 			archive_entry_set_perm(entry, 0777);
124 			archive_entry_set_symlink(entry,
125 			    fuzz_consume_string(&consumer, 64));
126 			break;
127 		case 3:
128 			archive_entry_set_filetype(entry, AE_IFBLK);
129 			archive_entry_set_perm(entry, 0600);
130 			archive_entry_set_rdev(entry,
131 			    fuzz_consume_u16(&consumer));
132 			break;
133 		case 4:
134 			archive_entry_set_filetype(entry, AE_IFIFO);
135 			archive_entry_set_perm(entry, 0644);
136 			break;
137 		}
138 
139 		archive_entry_set_uid(entry, fuzz_consume_byte(&consumer));
140 		archive_entry_set_gid(entry, fuzz_consume_byte(&consumer));
141 		archive_entry_set_mtime(entry,
142 		    1700000000 + fuzz_consume_u16(&consumer), 0);
143 		archive_entry_set_uname(entry, "user");
144 		archive_entry_set_gname(entry, "group");
145 
146 		if (fuzz_consumer_remaining(&consumer) > 1 &&
147 		    (fuzz_consume_byte(&consumer) & 0x01))
148 			archive_entry_copy_fflags_text(entry, "uappnd,uchg");
149 
150 		if (ftype == 0) {
151 			file_size = fuzz_consume_byte(&consumer) % 128;
152 			archive_entry_set_size(entry, file_size);
153 		}
154 
155 		if (archive_write_header(a, entry) != ARCHIVE_OK)
156 			continue;
157 
158 		if (file_size > 0 && fuzz_consumer_remaining(&consumer) > 0) {
159 			size_t to_write = file_size;
160 			uint8_t data[128];
161 			if (to_write > fuzz_consumer_remaining(&consumer))
162 				to_write = fuzz_consumer_remaining(&consumer);
163 			fuzz_consume_bytes(&consumer, data, to_write);
164 			archive_write_data(a, data, to_write);
165 		}
166 	}
167 
168 	archive_entry_free(entry);
169 	/* Close triggers tree traversal; must not crash. */
170 	archive_write_close(a);
171 	archive_write_free(a);
172 	free(out_buf);
173 }
174 
DEFINE_TEST(test_write_format_mtree_no_set_symlink)175 DEFINE_TEST(test_write_format_mtree_no_set_symlink)
176 {
177 	struct archive *a;
178 	size_t buffsize = 4096;
179 	char *buff;
180 	size_t used;
181 	assert((buff = malloc(buffsize)) != NULL);
182 	assert((a = archive_write_new()) != NULL);
183 	assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_mtree(a));
184 	assertEqualIntA(a, ARCHIVE_OK,
185 		archive_write_open_memory(a, buff, buffsize, &used));
186 
187 	struct archive_entry *ae;
188 	assert((ae = archive_entry_new()) != NULL);
189 	archive_entry_set_pathname(ae, "./badlink"),
190 	archive_entry_set_filetype(ae, AE_IFLNK);
191 	archive_entry_set_perm(ae, 0777);
192 	/* archive_entry_set_symlink(ae, "target"); (omitted) */
193 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
194 	archive_entry_free(ae);
195 
196 	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
197 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
198 	free(buff);
199 }
200 
DEFINE_TEST(test_write_format_mtree_reg_dot_root)201 DEFINE_TEST(test_write_format_mtree_reg_dot_root)
202 {
203 	struct archive *a;
204 	size_t buffsize = 4096;
205 	char *buff;
206 	size_t used;
207 	assert((buff = malloc(buffsize)) != NULL);
208 	assert((a = archive_write_new()) != NULL);
209 	assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_mtree(a));
210 	assertEqualIntA(a, ARCHIVE_OK,
211 		archive_write_open_memory(a, buff, buffsize, &used));
212 
213 	struct archive_entry *ae;
214 	/* Entry 1: "." with filetype AE_IFREG (not AE_IFDIR). */
215 	assert((ae = archive_entry_new()) != NULL);
216 	archive_entry_set_pathname(ae, "."),
217 	archive_entry_set_filetype(ae, AE_IFREG);
218 	archive_entry_set_perm(ae, 0644);
219 	assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae));
220 	archive_entry_free(ae);
221 
222 	/* Entry 2: any child path. */
223 	assert((ae = archive_entry_new()) != NULL);
224 	archive_entry_set_pathname(ae, "./foo"),
225 	archive_entry_set_filetype(ae, AE_IFREG);
226 	archive_entry_set_perm(ae, 0644);
227 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
228 	archive_entry_free(ae);
229 
230 	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
231 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
232 	free(buff);
233 }
234