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