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 "files.h" 25 #include "common.h" 26 27 #include <dirent.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <inttypes.h> 31 #include <stdint.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <sys/mman.h> 36 #include <sys/socket.h> 37 #include <sys/stat.h> 38 #if defined(_HF_ARCH_LINUX) 39 #include <sys/syscall.h> 40 #endif /* defined(_HF_ARCH_LINUX) */ 41 #include <sys/types.h> 42 #include <unistd.h> 43 44 #include "log.h" 45 #include "util.h" 46 47 ssize_t files_readFileToBufMax(char* fileName, uint8_t* buf, size_t fileMaxSz) { 48 int fd = open(fileName, O_RDONLY | O_CLOEXEC); 49 if (fd == -1) { 50 PLOG_W("Couldn't open '%s' for R/O", fileName); 51 return -1; 52 } 53 54 ssize_t readSz = files_readFromFd(fd, buf, fileMaxSz); 55 if (readSz < 0) { 56 LOG_W("Couldn't read '%s' to a buf", fileName); 57 } 58 close(fd); 59 60 LOG_D("Read '%zu' bytes from '%s'", readSz, fileName); 61 return readSz; 62 } 63 64 bool files_writeBufToFile(const char* fileName, const uint8_t* buf, size_t fileSz, int flags) { 65 int fd = open(fileName, flags, 0644); 66 if (fd == -1) { 67 PLOG_W("Couldn't open '%s' for R/W", fileName); 68 return false; 69 } 70 71 bool ret = files_writeToFd(fd, buf, fileSz); 72 if (ret == false) { 73 PLOG_W("Couldn't write '%zu' bytes to file '%s' (fd='%d')", fileSz, fileName, fd); 74 unlink(fileName); 75 } else { 76 LOG_D("Written '%zu' bytes to '%s'", fileSz, fileName); 77 } 78 79 close(fd); 80 return ret; 81 } 82 83 bool files_writeToFd(int fd, const uint8_t* buf, size_t fileSz) { 84 size_t writtenSz = 0; 85 while (writtenSz < fileSz) { 86 ssize_t sz = write(fd, &buf[writtenSz], fileSz - writtenSz); 87 if (sz < 0 && errno == EINTR) continue; 88 89 if (sz < 0) return false; 90 91 writtenSz += sz; 92 } 93 return true; 94 } 95 96 bool files_writeStrToFd(int fd, const char* str) { 97 return files_writeToFd(fd, (const uint8_t*)str, strlen(str)); 98 } 99 100 ssize_t files_readFromFd(int fd, uint8_t* buf, size_t fileSz) { 101 size_t readSz = 0; 102 while (readSz < fileSz) { 103 ssize_t sz = read(fd, &buf[readSz], fileSz - readSz); 104 if (sz < 0 && errno == EINTR) continue; 105 106 if (sz == 0) break; 107 108 if (sz < 0) return -1; 109 110 readSz += sz; 111 } 112 return (ssize_t)readSz; 113 } 114 115 bool files_exists(const char* fileName) { return (access(fileName, F_OK) != -1); } 116 117 bool files_writePatternToFd(int fd, off_t size, unsigned char p) { 118 void* buf = malloc(size); 119 if (!buf) { 120 PLOG_W("Couldn't allocate memory"); 121 return false; 122 } 123 124 memset(buf, p, (size_t)size); 125 int ret = files_writeToFd(fd, buf, size); 126 free(buf); 127 128 return ret; 129 } 130 131 bool files_sendToSocketNB(int fd, const uint8_t* buf, size_t fileSz) { 132 size_t writtenSz = 0; 133 while (writtenSz < fileSz) { 134 ssize_t sz = send(fd, &buf[writtenSz], fileSz - writtenSz, MSG_DONTWAIT); 135 if (sz < 0 && errno == EINTR) continue; 136 137 if (sz < 0) return false; 138 139 writtenSz += sz; 140 } 141 return true; 142 } 143 144 bool files_sendToSocket(int fd, const uint8_t* buf, size_t fileSz) { 145 int sendFlags = 0; 146 #ifdef _HF_ARCH_DARWIN 147 sendFlags |= SO_NOSIGPIPE; 148 #else 149 sendFlags |= MSG_NOSIGNAL; 150 #endif 151 152 size_t writtenSz = 0; 153 while (writtenSz < fileSz) { 154 ssize_t sz = send(fd, &buf[writtenSz], fileSz - writtenSz, sendFlags); 155 if (sz < 0 && errno == EINTR) continue; 156 157 if (sz < 0) return false; 158 159 writtenSz += sz; 160 } 161 return true; 162 } 163 164 const char* files_basename(const char* path) { 165 const char* base = strrchr(path, '/'); 166 return base ? base + 1 : path; 167 } 168 169 /* 170 * dstExists argument can be used by caller for cases where existing destination 171 * file requires special handling (e.g. save unique crashes) 172 */ 173 bool files_copyFile(const char* source, const char* destination, bool* dstExists, bool try_link) { 174 if (dstExists) { 175 *dstExists = false; 176 } 177 178 if (try_link) { 179 if (link(source, destination) == 0) { 180 return true; 181 } else { 182 if (errno == EEXIST) { 183 // Should kick-in before MAC, so avoid the hassle 184 if (dstExists) *dstExists = true; 185 return false; 186 } else { 187 PLOG_D("Couldn't link '%s' as '%s'", source, destination); 188 /* 189 * Don't fail yet as we might have a running env which doesn't allow 190 * hardlinks (e.g. SELinux) 191 */ 192 } 193 } 194 } 195 // Now try with a verbose POSIX alternative 196 int inFD, outFD, dstOpenFlags; 197 mode_t dstFilePerms; 198 199 // O_EXCL is important for saving unique crashes 200 dstOpenFlags = O_CREAT | O_WRONLY | O_CLOEXEC | O_EXCL; 201 dstFilePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; 202 203 inFD = open(source, O_RDONLY | O_CLOEXEC); 204 if (inFD == -1) { 205 PLOG_D("Couldn't open '%s' source", source); 206 return false; 207 } 208 209 struct stat inSt; 210 if (fstat(inFD, &inSt) == -1) { 211 PLOG_W("Couldn't fstat(fd='%d' fileName='%s')", inFD, source); 212 close(inFD); 213 return false; 214 } 215 216 outFD = open(destination, dstOpenFlags, dstFilePerms); 217 if (outFD == -1) { 218 if (errno == EEXIST) { 219 if (dstExists) *dstExists = true; 220 } 221 PLOG_D("Couldn't open '%s' destination", destination); 222 close(inFD); 223 return false; 224 } 225 close(outFD); 226 227 uint8_t* inFileBuf = malloc(inSt.st_size); 228 if (!inFileBuf) { 229 PLOG_W("malloc(%zu) failed", (size_t)inSt.st_size); 230 close(inFD); 231 close(outFD); 232 return false; 233 } 234 235 ssize_t readSz = files_readFromFd(inFD, inFileBuf, (size_t)inSt.st_size); 236 if (readSz < 0) { 237 PLOG_W("Couldn't read '%s' to a buf", source); 238 free(inFileBuf); 239 close(inFD); 240 close(outFD); 241 return false; 242 } 243 244 if (files_writeToFd(outFD, inFileBuf, readSz) == false) { 245 PLOG_W("Couldn't write '%zu' bytes to file '%s' (fd='%d')", (size_t)readSz, destination, 246 outFD); 247 unlink(destination); 248 free(inFileBuf); 249 close(inFD); 250 close(outFD); 251 return false; 252 } 253 254 free(inFileBuf); 255 close(inFD); 256 close(outFD); 257 return true; 258 } 259 260 /* 261 * Reads symbols from src file (one per line) and append them to filterList. The 262 * total number of added symbols is returned. 263 * 264 * Simple wildcard strings are also supported (e.g. mem*) 265 */ 266 size_t files_parseSymbolFilter(const char* srcFile, char*** filterList) { 267 FILE* f = fopen(srcFile, "rb"); 268 if (f == NULL) { 269 PLOG_W("Couldn't open '%s' - R/O mode", srcFile); 270 return 0; 271 } 272 273 char* lineptr = NULL; 274 size_t symbolsRead = 0, n = 0; 275 for (;;) { 276 if (getline(&lineptr, &n, f) == -1) { 277 break; 278 } 279 280 if (strlen(lineptr) < 3) { 281 LOG_F("Input symbol '%s' too short (strlen < 3)", lineptr); 282 symbolsRead = 0; 283 break; 284 } 285 if ((*filterList = (char**)util_Realloc( 286 *filterList, (symbolsRead + 1) * sizeof((*filterList)[0]))) == NULL) { 287 PLOG_W("realloc failed (sz=%zu)", (symbolsRead + 1) * sizeof((*filterList)[0])); 288 symbolsRead = 0; 289 break; 290 } 291 (*filterList)[symbolsRead] = malloc(strlen(lineptr)); 292 if (!(*filterList)[symbolsRead]) { 293 PLOG_E("malloc(%zu) failed", strlen(lineptr)); 294 symbolsRead = 0; 295 break; 296 } 297 strncpy((*filterList)[symbolsRead], lineptr, strlen(lineptr)); 298 symbolsRead++; 299 } 300 301 LOG_I("%zu filter symbols added to list", symbolsRead); 302 fclose(f); 303 free(lineptr); 304 return symbolsRead; 305 } 306 307 uint8_t* files_mapFile(const char* fileName, off_t* fileSz, int* fd, bool isWritable) { 308 int mmapProt = PROT_READ; 309 if (isWritable) { 310 mmapProt |= PROT_WRITE; 311 } 312 313 if ((*fd = open(fileName, O_RDONLY)) == -1) { 314 PLOG_W("Couldn't open() '%s' file in R/O mode", fileName); 315 return NULL; 316 } 317 318 struct stat st; 319 if (fstat(*fd, &st) == -1) { 320 PLOG_W("Couldn't stat() the '%s' file", fileName); 321 close(*fd); 322 return NULL; 323 } 324 325 uint8_t* buf; 326 if ((buf = mmap(NULL, st.st_size, mmapProt, MAP_PRIVATE, *fd, 0)) == MAP_FAILED) { 327 PLOG_W("Couldn't mmap() the '%s' file", fileName); 328 close(*fd); 329 return NULL; 330 } 331 332 *fileSz = st.st_size; 333 return buf; 334 } 335 336 uint8_t* files_mapFileShared(const char* fileName, off_t* fileSz, int* fd) { 337 if ((*fd = open(fileName, O_RDONLY)) == -1) { 338 PLOG_W("Couldn't open() '%s' file in R/O mode", fileName); 339 return NULL; 340 } 341 342 struct stat st; 343 if (fstat(*fd, &st) == -1) { 344 PLOG_W("Couldn't stat() the '%s' file", fileName); 345 close(*fd); 346 return NULL; 347 } 348 349 uint8_t* buf; 350 if ((buf = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, *fd, 0)) == MAP_FAILED) { 351 PLOG_W("Couldn't mmap() the '%s' file", fileName); 352 close(*fd); 353 return NULL; 354 } 355 356 *fileSz = st.st_size; 357 return buf; 358 } 359 360 void* files_mapSharedMem(size_t sz, int* fd, const char* dir) { 361 *fd = -1; 362 #if defined(_HF_ARCH_LINUX) && defined(__NR_memfd_create) 363 #if !defined(MFD_CLOEXEC) /* It's not defined as we didn't include sys/memfd.h, but it's \ 364 present with some Linux distros only */ 365 #define MFD_CLOEXEC 0x0001U 366 #endif /* !defined(MFD_CLOEXEC) */ 367 *fd = syscall(__NR_memfd_create, "honggfuzz", (uintptr_t)MFD_CLOEXEC); 368 #endif /* defined(_HF_ARCH_LINUX) && defined(__NR_memfd_create) */ 369 if (*fd == -1) { 370 char template[PATH_MAX]; 371 snprintf(template, sizeof(template), "%s/hfuzz.XXXXXX", dir); 372 if ((*fd = mkstemp(template)) == -1) { 373 PLOG_W("mkstemp('%s')", template); 374 return MAP_FAILED; 375 } 376 unlink(template); 377 } 378 if (ftruncate(*fd, sz) == -1) { 379 PLOG_W("ftruncate(%d, %zu)", *fd, sz); 380 close(*fd); 381 *fd = -1; 382 return MAP_FAILED; 383 } 384 void* ret = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0); 385 if (ret == MAP_FAILED) { 386 PLOG_W("mmap(sz=%zu, fd=%d)", sz, *fd); 387 *fd = -1; 388 close(*fd); 389 return MAP_FAILED; 390 } 391 return ret; 392 } 393 394 bool files_readPidFromFile(const char* fileName, pid_t* pidPtr) { 395 FILE* fPID = fopen(fileName, "rbe"); 396 if (fPID == NULL) { 397 PLOG_W("Couldn't open '%s' - R/O mode", fileName); 398 return false; 399 } 400 401 char* lineptr = NULL; 402 size_t lineSz = 0; 403 ssize_t ret = getline(&lineptr, &lineSz, fPID); 404 fclose(fPID); 405 if (ret == -1) { 406 if (lineSz == 0) { 407 LOG_W("Empty PID file (%s)", fileName); 408 fclose(fPID); 409 free(lineptr); 410 return false; 411 } 412 } 413 414 *pidPtr = atoi(lineptr); 415 free(lineptr); 416 if (*pidPtr < 1) { 417 LOG_W("Invalid PID read from '%s' file", fileName); 418 return false; 419 } 420 421 return true; 422 } 423