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 * 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 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 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 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 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 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