1/* 2 * Copyright (c) 2013 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2013 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include <sys/errno.h> 37#include <sys/stat.h> 38 39#include <ctype.h> 40#include <dirent.h> 41#include <dlfcn.h> 42#include <err.h> 43#include <fcntl.h> 44#include <limits.h> 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <unistd.h> 49 50static void *dso; 51 52unsigned long count = 0; 53 54typedef void (*permutate)(unsigned char *p, size_t length); 55 56static void 57permutate_random(unsigned char *p, size_t length) 58{ 59 if (length == 0) 60 return; 61 p[arc4random() % length] = arc4random(); 62} 63 64static void 65permutate_asn1(unsigned char *p, size_t length) 66{ 67 68} 69 70static void 71permutate_none(unsigned char *p, size_t length) 72{ 73} 74 75 76 77static void 78runTestcase(const char *name, const unsigned char *p, size_t length, permutate permutate_func) 79{ 80 int (*decode_item)(const unsigned char *, size_t, void *, size_t *); 81 int (*free_item)(void *); 82 char *decode_name, *free_name; 83 size_t size; 84 void *data; 85 int ret; 86 unsigned char *copy; 87 unsigned long tcount = 1; 88 char *leak_cmd = NULL; 89 90 const size_t datasize = 10000; 91 92 if (permutate_func) 93 tcount = 10000000; 94 95 if (getenv("MallocStackLogging") || getenv("MallocStackLoggingNoCompact")) 96 asprintf(&leak_cmd, "leaks %d > /tmp/leaks-log-pid-%d", (int)getpid(), (int)getpid()); 97 98 data = calloc(1, datasize); 99 if (data == NULL) 100 err(1, "malloc"); 101 102 asprintf(&decode_name, "decode_%s", name); 103 asprintf(&free_name, "free_%s", name); 104 105 decode_item = dlsym(dso, decode_name); 106 free_item = dlsym(dso, free_name); 107 108 free(decode_name); 109 free(free_name); 110 111 if (decode_item == NULL) 112 errx(1, "no decode_%s", name); 113 if (free_item == NULL) 114 errx(1, "no free_%s", name); 115 116 memset(data, 0, datasize); 117 118 copy = malloc(length); 119 if (copy == NULL) 120 err(1, "malloc"); 121 122 /* 123 * Main fuzzer loop, keep modifying the input stream as long as it 124 * parses clearly. 125 */ 126 127 memcpy(copy, p, length); 128 while (tcount > 0) { 129 130 if (permutate_func) 131 permutate_func(copy, length); 132 133 ret = decode_item(copy, length, data, &size); 134 if (ret) { 135 memcpy(copy, p, length); 136 } else { 137 free_item(data); 138 } 139 140 tcount--; 141 count++; 142 if ((count & 0xffff) == 0) { 143 printf("%lu...\n", (unsigned long)count); 144 145 if (leak_cmd) { 146 memset(data, 0, datasize); 147 if (system(leak_cmd)) 148 abort(); 149 } 150 } 151 } 152 153 free(copy); 154 free(data); 155} 156 157static void 158parseTestcase(const char *filename, permutate func) 159{ 160 struct stat sb; 161 char *p, *buf; 162 ssize_t sret; 163 size_t size; 164 int fd; 165 166 fd = open(filename, O_RDONLY, 0); 167 if (fd < 0) { 168 warn("failed to open: %s", filename); 169 return; 170 } 171 if (fstat(fd, &sb) != 0) 172 err(1, "failed to stat: %s", filename); 173 if (!S_ISREG(sb.st_mode)) { 174 close(fd); 175 return; 176 } 177 178 if (sb.st_size > SIZE_T_MAX) 179 errx(1, "%s to larger", filename); 180 181 buf = malloc((size_t)sb.st_size); 182 if (buf == NULL) 183 err(1, "malloc"); 184 size = (size_t)sb.st_size; 185 186 sret = read(fd, buf, size); 187 if (sret < 0) 188 err(1, "read"); 189 else if (sret != (ssize_t)size) 190 errx(1, "short read"); 191 192 close(fd); 193 194 p = memchr(buf, '\0', size); 195 if (p && p != buf) { 196 p++; 197 runTestcase(buf, (const void *)p, size - (p - buf), func); 198 } else { 199 warnx("file '%s' not a valid test case", filename); 200 } 201 202 free(buf); 203} 204 205int 206main(int argc, char **argv) 207{ 208 permutate func; 209 const char *cmd; 210 211 dso = dlopen("/usr/local/lib/libheimdal-asn1-all-templates.dylib", RTLD_LAZY); 212 if (dso == NULL) 213 errx(1, "dlopen: %s", dlerror()); 214 215 if (argc < 3) 216 errx(1, "missing command[fuzz-random-|][file|dir] and argument"); 217 218 219 cmd = argv[1]; 220 221 if (strncasecmp("fuzz-random-", cmd, 12) == 0) { 222 func = permutate_random; 223 cmd += 12; 224 } else if (strncasecmp("fuzz-asn1-", cmd, 9) == 0) { 225 func = permutate_asn1; 226 cmd += 9; 227 } else if (strncasecmp("none-", cmd, 5) == 0) { 228 func = permutate_none; 229 cmd += 5; 230 } else { 231 func = NULL; 232 } 233 234 if (strcasecmp("dir", cmd) == 0) { 235 const char *dir = argv[2]; 236 struct dirent *de; 237 DIR *d; 238 239 d = opendir(dir); 240 if (d == NULL) 241 err(1, "opendir: %s", dir); 242 243 while ((de = readdir(d)) != NULL) { 244 char *str; 245 asprintf(&str, "%s/%.*s", dir, (int)de->d_namlen, de->d_name); 246 247 parseTestcase(str, func); 248 free(str); 249 } 250 } else if (strcasecmp("file", cmd) == 0) { 251 parseTestcase(argv[2], func); 252 } else { 253 errx(1, "unknown command: %s", cmd); 254 } 255 256 printf("ran %lu test cases\n", count); 257 258 return 0; 259} 260