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