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 XAR writer, matching the protocol
32 * in fuzzers/custom/fuzz_writer_xar.cc.
33 */
34 static void
replay_xar_writer(const char * refname)35 replay_xar_writer(const char *refname)
36 {
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 char *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 >= 6))
55 return;
56
57 fuzz_consumer_init(&consumer, raw, rawsize);
58 opts = fuzz_consume_byte(&consumer);
59 num_entries = (fuzz_consume_byte(&consumer) % 5) + 1;
60
61 a = archive_write_new();
62 if (!assert(a != NULL))
63 return;
64
65 archive_write_set_format_xar(a);
66 archive_write_add_filter_none(a);
67
68 if (opts & 0x01)
69 archive_write_set_options(a, "xar:compression=gzip");
70 if (opts & 0x02)
71 archive_write_set_options(a, "xar:compression=bzip2");
72 if (opts & 0x04)
73 archive_write_set_options(a, "xar:checksum=sha1");
74 if (opts & 0x08)
75 archive_write_set_options(a, "xar:checksum=md5");
76
77 out_buf = (char *)malloc(512 * 1024);
78 if (!assert(out_buf != NULL)) {
79 archive_write_free(a);
80 return;
81 }
82 if (archive_write_open_memory(a, out_buf, 512 * 1024, &used)
83 != ARCHIVE_OK) {
84 archive_write_free(a);
85 free(out_buf);
86 return;
87 }
88
89 entry = archive_entry_new();
90 if (!assert(entry != NULL)) {
91 archive_write_free(a);
92 free(out_buf);
93 return;
94 }
95
96 for (i = 0; i < num_entries && fuzz_consumer_remaining(&consumer) > 4;
97 i++) {
98 const char *path;
99 uint8_t ftype;
100 uint16_t raw_mode;
101 int64_t file_size = 0;
102
103 archive_entry_clear(entry);
104
105 path = fuzz_consume_string(&consumer, 200);
106 if (path[0] == '\0')
107 path = "file.txt";
108 archive_entry_set_pathname(entry, path);
109
110 ftype = fuzz_consume_byte(&consumer) % 4;
111 raw_mode = fuzz_consume_u16(&consumer) & 07777;
112 switch (ftype) {
113 case 0: archive_entry_set_filetype(entry, AE_IFREG); break;
114 case 1: archive_entry_set_filetype(entry, AE_IFDIR); break;
115 case 2: archive_entry_set_filetype(entry, AE_IFLNK); break;
116 default: archive_entry_set_filetype(entry, AE_IFCHR); break;
117 }
118 archive_entry_set_perm(entry, raw_mode);
119
120 archive_entry_set_uname(entry,
121 fuzz_consume_string(&consumer, 32));
122 archive_entry_set_gname(entry,
123 fuzz_consume_string(&consumer, 32));
124 archive_entry_set_uid(entry, fuzz_consume_u32(&consumer));
125 archive_entry_set_gid(entry, fuzz_consume_u32(&consumer));
126 archive_entry_set_mtime(entry,
127 fuzz_consume_i64(&consumer), fuzz_consume_u32(&consumer));
128 archive_entry_set_atime(entry,
129 fuzz_consume_i64(&consumer), 0);
130 archive_entry_set_ctime(entry,
131 fuzz_consume_i64(&consumer), 0);
132
133 if (ftype == 0) {
134 file_size = fuzz_consume_byte(&consumer) % 200;
135 archive_entry_set_size(entry, file_size);
136 }
137
138 if (ftype == 2)
139 archive_entry_set_symlink(entry,
140 fuzz_consume_string(&consumer, 64));
141
142 if (archive_write_header(a, entry) == ARCHIVE_OK
143 && file_size > 0) {
144 size_t write_len = (size_t)file_size;
145 if (write_len > fuzz_consumer_remaining(&consumer))
146 write_len = fuzz_consumer_remaining(&consumer);
147 if (write_len > 0) {
148 uint8_t file_data[200];
149 fuzz_consume_bytes(&consumer, file_data,
150 write_len);
151 archive_write_data(a, file_data, write_len);
152 }
153 }
154 }
155
156 archive_entry_free(entry);
157 archive_write_close(a);
158 archive_write_free(a);
159 free(out_buf);
160 }
161
DEFINE_TEST(test_write_format_xar_strcpy_overlap)162 DEFINE_TEST(test_write_format_xar_strcpy_overlap)
163 {
164 replay_xar_writer("test_write_format_xar_strcpy_overlap.bin");
165 }
166
DEFINE_TEST(test_write_format_xar_underflow)167 DEFINE_TEST(test_write_format_xar_underflow)
168 {
169 replay_xar_writer("test_write_format_xar_underflow.bin");
170 }
171