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