1 /* 2 * 3 * honggfuzz - file operations 4 * ----------------------------------------- 5 * 6 * Author: Robert Swiecki <swiecki (at) google.com> 7 * 8 * Copyright 2010-2015 by Google Inc. All Rights Reserved. 9 * 10 * Licensed under the Apache License, Version 2.0 (the "License"); you may 11 * not use this file except in compliance with the License. You may obtain 12 * a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, software 17 * distributed under the License is distributed on an "AS IS" BASIS, 18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 19 * implied. See the License for the specific language governing 20 * permissions and limitations under the License. 21 * 22 */ 23 24 #include "input.h" 25 26 #include <dirent.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <inttypes.h> 30 #include <stdint.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <sys/mman.h> 35 #include <sys/socket.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 #include <unistd.h> 39 40 #include "libcommon/common.h" 41 #include "libcommon/files.h" 42 43 #if defined(_HF_ARCH_LINUX) 44 #include <sys/syscall.h> 45 #if defined(__NR_memfd_create) 46 #include <linux/memfd.h> 47 #endif /* defined(__NR_memfd_create) */ 48 #endif /* defined(_HF_ARCH_LINUX) */ 49 50 #include "libcommon/log.h" 51 #include "libcommon/util.h" 52 53 static bool input_getDirStatsAndRewind(honggfuzz_t* hfuzz) { 54 rewinddir(hfuzz->io.inputDirPtr); 55 56 size_t maxSize = 0U; 57 size_t fileCnt = 0U; 58 for (;;) { 59 errno = 0; 60 struct dirent* entry = readdir(hfuzz->io.inputDirPtr); 61 if (entry == NULL && errno == EINTR) { 62 continue; 63 } 64 if (entry == NULL && errno != 0) { 65 PLOG_W("readdir('%s')", hfuzz->io.inputDir); 66 return false; 67 } 68 if (entry == NULL) { 69 break; 70 } 71 72 char fname[PATH_MAX]; 73 snprintf(fname, sizeof(fname), "%s/%s", hfuzz->io.inputDir, entry->d_name); 74 LOG_D("Analyzing file '%s'", fname); 75 76 struct stat st; 77 if (stat(fname, &st) == -1) { 78 LOG_W("Couldn't stat() the '%s' file", fname); 79 continue; 80 } 81 if (!S_ISREG(st.st_mode)) { 82 LOG_D("'%s' is not a regular file, skipping", fname); 83 continue; 84 } 85 if (hfuzz->maxFileSz != 0UL && st.st_size > (off_t)hfuzz->maxFileSz) { 86 LOG_W("File '%s' is bigger than maximal defined file size (-F): %" PRId64 " > %" PRId64, 87 fname, (int64_t)st.st_size, (int64_t)hfuzz->maxFileSz); 88 } 89 if ((size_t)st.st_size > maxSize) { 90 maxSize = st.st_size; 91 } 92 fileCnt++; 93 } 94 95 ATOMIC_SET(hfuzz->io.fileCnt, fileCnt); 96 if (hfuzz->maxFileSz == 0U) { 97 if (maxSize < 8192) { 98 hfuzz->maxFileSz = 8192; 99 } else { 100 hfuzz->maxFileSz = maxSize; 101 } 102 } 103 if (hfuzz->persistent && hfuzz->maxFileSz > (1024U * 128)) { 104 LOG_D("Persistent mode enabled, lowering maximum input size to 128KiB"); 105 hfuzz->maxFileSz = 1024U * 128; 106 } 107 108 if (hfuzz->io.fileCnt == 0U) { 109 LOG_W("No usable files in the input directory '%s'", hfuzz->io.inputDir); 110 return false; 111 } 112 113 LOG_D("Re-read the '%s', maxFileSz:%zu, number of usable files:%zu", hfuzz->io.inputDir, 114 hfuzz->maxFileSz, hfuzz->io.fileCnt); 115 116 rewinddir(hfuzz->io.inputDirPtr); 117 118 return true; 119 } 120 121 bool input_getNext(run_t* run, char* fname, bool rewind) { 122 static pthread_mutex_t input_mutex = PTHREAD_MUTEX_INITIALIZER; 123 MX_SCOPED_LOCK(&input_mutex); 124 125 if (run->global->io.fileCnt == 0U) { 126 return false; 127 } 128 129 for (;;) { 130 errno = 0; 131 struct dirent* entry = readdir(run->global->io.inputDirPtr); 132 if (entry == NULL && errno == EINTR) { 133 continue; 134 } 135 if (entry == NULL && errno != 0) { 136 PLOG_W("readdir_r('%s')", run->global->io.inputDir); 137 return false; 138 } 139 if (entry == NULL && rewind == false) { 140 return false; 141 } 142 if (entry == NULL && rewind == true) { 143 if (input_getDirStatsAndRewind(run->global) == false) { 144 LOG_E("input_getDirStatsAndRewind('%s')", run->global->io.inputDir); 145 return false; 146 } 147 continue; 148 } 149 150 snprintf(fname, PATH_MAX, "%s/%s", run->global->io.inputDir, entry->d_name); 151 152 struct stat st; 153 if (stat(fname, &st) == -1) { 154 LOG_W("Couldn't stat() the '%s' file", fname); 155 continue; 156 } 157 if (!S_ISREG(st.st_mode)) { 158 LOG_D("'%s' is not a regular file, skipping", fname); 159 continue; 160 } 161 return true; 162 } 163 } 164 165 bool input_init(honggfuzz_t* hfuzz) { 166 hfuzz->io.fileCnt = 0U; 167 168 if (!hfuzz->io.inputDir) { 169 LOG_W("No input file/dir specified"); 170 return false; 171 } 172 173 int dir_fd = open(hfuzz->io.inputDir, O_DIRECTORY | O_RDONLY | O_CLOEXEC); 174 if (dir_fd == -1) { 175 PLOG_W("open('%s', O_DIRECTORY|O_RDONLY|O_CLOEXEC)", hfuzz->io.inputDir); 176 return false; 177 } 178 if ((hfuzz->io.inputDirPtr = fdopendir(dir_fd)) == NULL) { 179 close(dir_fd); 180 PLOG_W("opendir('%s')", hfuzz->io.inputDir); 181 return false; 182 } 183 if (input_getDirStatsAndRewind(hfuzz) == false) { 184 hfuzz->io.fileCnt = 0U; 185 LOG_W("input_getDirStatsAndRewind('%s')", hfuzz->io.inputDir); 186 return false; 187 } 188 189 return true; 190 } 191 192 bool input_parseDictionary(honggfuzz_t* hfuzz) { 193 FILE* fDict = fopen(hfuzz->dictionaryFile, "rb"); 194 if (fDict == NULL) { 195 PLOG_W("Couldn't open '%s' - R/O mode", hfuzz->dictionaryFile); 196 return false; 197 } 198 defer { fclose(fDict); }; 199 200 char* lineptr = NULL; 201 size_t n = 0; 202 defer { free(lineptr); }; 203 for (;;) { 204 ssize_t len = getdelim(&lineptr, &n, '\n', fDict); 205 if (len == -1) { 206 break; 207 } 208 if (len > 1 && lineptr[len - 1] == '\n') { 209 lineptr[len - 1] = '\0'; 210 len--; 211 } 212 if (lineptr[0] == '#') { 213 continue; 214 } 215 if (lineptr[0] == '\n') { 216 continue; 217 } 218 if (lineptr[0] == '\0') { 219 continue; 220 } 221 char bufn[1025] = {0}; 222 char bufv[1025] = {0}; 223 if (sscanf(lineptr, "\"%1024s", bufv) != 1 && 224 sscanf(lineptr, "%1024[^=]=\"%1024s", bufn, bufv) != 2) { 225 LOG_W("Incorrect dictionary entry: '%s'. Skipping", lineptr); 226 continue; 227 } 228 229 char* s = util_StrDup(bufv); 230 struct strings_t* str = (struct strings_t*)util_Malloc(sizeof(struct strings_t)); 231 str->len = util_decodeCString(s); 232 str->s = s; 233 hfuzz->dictionaryCnt += 1; 234 TAILQ_INSERT_TAIL(&hfuzz->dictq, str, pointers); 235 236 LOG_D("Dictionary: loaded word: '%s' (len=%zu)", str->s, str->len); 237 } 238 LOG_I("Loaded %zu words from the dictionary", hfuzz->dictionaryCnt); 239 return true; 240 } 241 242 bool input_parseBlacklist(honggfuzz_t* hfuzz) { 243 FILE* fBl = fopen(hfuzz->blacklistFile, "rb"); 244 if (fBl == NULL) { 245 PLOG_W("Couldn't open '%s' - R/O mode", hfuzz->blacklistFile); 246 return false; 247 } 248 defer { fclose(fBl); }; 249 250 char* lineptr = NULL; 251 /* lineptr can be NULL, but it's fine for free() */ 252 defer { free(lineptr); }; 253 size_t n = 0; 254 for (;;) { 255 if (getline(&lineptr, &n, fBl) == -1) { 256 break; 257 } 258 259 if ((hfuzz->blacklist = util_Realloc(hfuzz->blacklist, 260 (hfuzz->blacklistCnt + 1) * sizeof(hfuzz->blacklist[0]))) == NULL) { 261 PLOG_W( 262 "realloc failed (sz=%zu)", (hfuzz->blacklistCnt + 1) * sizeof(hfuzz->blacklist[0])); 263 return false; 264 } 265 266 hfuzz->blacklist[hfuzz->blacklistCnt] = strtoull(lineptr, 0, 16); 267 LOG_D("Blacklist: loaded %'" PRIu64 "'", hfuzz->blacklist[hfuzz->blacklistCnt]); 268 269 // Verify entries are sorted so we can use interpolation search 270 if (hfuzz->blacklistCnt > 1) { 271 if (hfuzz->blacklist[hfuzz->blacklistCnt - 1] > hfuzz->blacklist[hfuzz->blacklistCnt]) { 272 LOG_F( 273 "Blacklist file not sorted. Use 'tools/createStackBlacklist.sh' to sort " 274 "records"); 275 return false; 276 } 277 } 278 hfuzz->blacklistCnt += 1; 279 } 280 281 if (hfuzz->blacklistCnt > 0) { 282 LOG_I("Loaded %zu stack hash(es) from the blacklist file", hfuzz->blacklistCnt); 283 } else { 284 LOG_F("Empty stack hashes blacklist file '%s'", hfuzz->blacklistFile); 285 } 286 return true; 287 } 288