1 /*-
2 * Copyright (c) 2024 Kyle Evans <kevans@FreeBSD.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7 #include <sys/param.h>
8 #include <sys/socket.h>
9
10 #include <assert.h>
11 #include <pthread.h>
12 #include <signal.h>
13 #include <stdbool.h>
14 #include <stdio.h>
15 #include <unistd.h>
16
17 #include <libder.h>
18
19 #include "fuzzers.h"
20
21 struct supply_data {
22 const uint8_t *data;
23 volatile size_t datasz;
24 int socket;
25 };
26
27 static void *
supply_thread(void * data)28 supply_thread(void *data)
29 {
30 struct supply_data *sdata = data;
31 size_t sz = sdata->datasz;
32 ssize_t writesz;
33
34 do {
35 writesz = write(sdata->socket, sdata->data, sz);
36
37 data += writesz;
38 sz -= writesz;
39 } while (sz != 0 && writesz > 0);
40
41 sdata->datasz = sz;
42 shutdown(sdata->socket, SHUT_RDWR);
43 close(sdata->socket);
44
45 return (NULL);
46 }
47
48 static int
fuzz_fd(const struct fuzz_params * fparams,const uint8_t * data,size_t sz)49 fuzz_fd(const struct fuzz_params *fparams, const uint8_t *data, size_t sz)
50 {
51 struct supply_data sdata;
52 struct libder_ctx *ctx;
53 struct libder_object *obj;
54 size_t totalsz;
55 int sockets[2];
56 pid_t pid;
57 int ret;
58
59 ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,
60 &sockets[0]);
61 if (ret == -1)
62 return (-1);
63
64 sdata.data = data;
65 sdata.datasz = sz;
66 sdata.socket = sockets[1];
67 signal(SIGCHLD, SIG_IGN);
68 pid = fork();
69 if (pid == -1) {
70 close(sockets[0]);
71 close(sockets[1]);
72 return (-1);
73 }
74
75 if (pid == 0) {
76 close(sockets[0]);
77 supply_thread(&sdata);
78 _exit(0);
79 } else {
80 close(sockets[1]);
81 }
82
83 totalsz = 0;
84 ret = 0;
85 ctx = libder_open();
86 libder_set_strict(ctx, !!fparams->strict);
87 while (totalsz < sz) {
88 size_t readsz = 0;
89
90 obj = libder_read_fd(ctx, sockets[0], &readsz);
91 libder_obj_free(obj);
92
93 /*
94 * Even invalid reads should consume at least one byte.
95 */
96 assert(readsz != 0);
97
98 totalsz += readsz;
99 if (readsz == 0)
100 break;
101 }
102
103 assert(totalsz == sz);
104 libder_close(ctx);
105 close(sockets[0]);
106
107 return (ret);
108 }
109
110 static int
fuzz_file(const struct fuzz_params * fparams,const uint8_t * data,size_t sz)111 fuzz_file(const struct fuzz_params *fparams, const uint8_t *data, size_t sz)
112 {
113 FILE *fp;
114 struct libder_ctx *ctx;
115 struct libder_object *obj;
116 size_t totalsz;
117 int ret;
118
119 if (fparams->buftype >= BUFFER_END)
120 return (-1);
121
122 fp = fmemopen(__DECONST(void *, data), sz, "rb");
123 assert(fp != NULL);
124
125 switch (fparams->buftype) {
126 case BUFFER_NONE:
127 setvbuf(fp, NULL, 0, _IONBF);
128 break;
129 case BUFFER_FULL:
130 setvbuf(fp, NULL, 0, _IOFBF);
131 break;
132 case BUFFER_END:
133 assert(0);
134 }
135
136 totalsz = 0;
137 ret = 0;
138 ctx = libder_open();
139 libder_set_strict(ctx, !!fparams->strict);
140 while (!feof(fp)) {
141 size_t readsz = 0;
142
143 obj = libder_read_file(ctx, fp, &readsz);
144 libder_obj_free(obj);
145
146 if (obj == NULL)
147 assert(readsz != 0 || feof(fp));
148 else
149 assert(readsz != 0);
150
151 totalsz += readsz;
152 }
153
154 assert(totalsz == sz);
155 libder_close(ctx);
156 fclose(fp);
157
158 return (ret);
159 }
160
161 static int
fuzz_plain(const struct fuzz_params * fparams,const uint8_t * data,size_t sz)162 fuzz_plain(const struct fuzz_params *fparams, const uint8_t *data, size_t sz)
163 {
164 struct libder_ctx *ctx;
165 struct libder_object *obj;
166 int ret;
167
168 if (sz == 0)
169 return (-1);
170
171 ret = 0;
172 ctx = libder_open();
173 libder_set_strict(ctx, !!fparams->strict);
174 do {
175 size_t readsz;
176
177 readsz = sz;
178 obj = libder_read(ctx, data, &readsz);
179 libder_obj_free(obj);
180
181 if (obj == NULL)
182 assert(readsz != 0 || readsz == sz);
183 else
184 assert(readsz != 0);
185
186 /*
187 * If we hit an entirely invalid segment of the buffer, we'll
188 * just skip a byte and try again.
189 */
190 data += MAX(1, readsz);
191 sz -= MAX(1, readsz);
192 } while (sz != 0);
193
194 libder_close(ctx);
195
196 return (ret);
197 };
198
199 static bool
validate_padding(const struct fuzz_params * fparams)200 validate_padding(const struct fuzz_params *fparams)
201 {
202 const uint8_t *end = (const void *)(fparams + 1);
203 const uint8_t *pad = (const uint8_t *)&fparams->PARAM_PAD_START;
204
205 while (pad < end) {
206 if (*pad++ != 0)
207 return (false);
208 }
209
210 return (true);
211 }
212
213 int
LLVMFuzzerTestOneInput(const uint8_t * data,size_t sz)214 LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz)
215 {
216 const struct fuzz_params *fparams;
217
218 if (sz <= sizeof(*fparams))
219 return (-1);
220
221 fparams = (const void *)data;
222 if (fparams->type >= STREAM_END)
223 return (-1);
224
225 if (!validate_padding(fparams))
226 return (-1);
227
228 data += sizeof(*fparams);
229 sz -= sizeof(*fparams);
230
231 if (fparams->type != STREAM_FILE && fparams->buftype != BUFFER_NONE)
232 return (-1);
233
234 switch (fparams->type) {
235 case STREAM_FD:
236 return (fuzz_fd(fparams, data, sz));
237 case STREAM_FILE:
238 return (fuzz_file(fparams, data, sz));
239 case STREAM_PLAIN:
240 return (fuzz_plain(fparams, data, sz));
241 case STREAM_END:
242 assert(0);
243 }
244
245 __builtin_trap();
246 }
247