Home | History | Annotate | Download | only in libhfcommon
      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