• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * honggfuzz - file operations
4  * -----------------------------------------
5  *
6  * Author: Robert Swiecki <swiecki@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 
files_readFileToBufMax(char * fileName,uint8_t * buf,size_t fileMaxSz)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 
files_writeBufToFile(const char * fileName,const uint8_t * buf,size_t fileSz,int flags)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 
files_writeToFd(int fd,const uint8_t * buf,size_t fileSz)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 
files_writeStrToFd(int fd,const char * str)96 bool files_writeStrToFd(int fd, const char* str) {
97     return files_writeToFd(fd, (const uint8_t*)str, strlen(str));
98 }
99 
files_readFromFd(int fd,uint8_t * buf,size_t fileSz)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 
files_exists(const char * fileName)115 bool files_exists(const char* fileName) { return (access(fileName, F_OK) != -1); }
116 
files_writePatternToFd(int fd,off_t size,unsigned char p)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 
files_sendToSocketNB(int fd,const uint8_t * buf,size_t fileSz)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 
files_sendToSocket(int fd,const uint8_t * buf,size_t fileSz)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 
files_basename(const char * path)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  */
files_copyFile(const char * source,const char * destination,bool * dstExists,bool try_link)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  */
files_parseSymbolFilter(const char * srcFile,char *** filterList)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 
files_mapFile(const char * fileName,off_t * fileSz,int * fd,bool isWritable)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 
files_mapFileShared(const char * fileName,off_t * fileSz,int * fd)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 
files_mapSharedMem(size_t sz,int * fd,const char * dir)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 
files_readPidFromFile(const char * fileName,pid_t * pidPtr)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