• 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-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 
files_readFileToBufMax(const char * fileName,uint8_t * buf,size_t fileMaxSz)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 
files_writeBufToFile(const char * fileName,const uint8_t * buf,size_t fileSz,int flags)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 
files_writeBufToTmpFile(const char * dir,const uint8_t * buf,size_t fileSz,int flags)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 
files_writeToFd(int fd,const uint8_t * buf,size_t fileSz)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 
files_writeStrToFd(int fd,const char * str)123 bool files_writeStrToFd(int fd, const char* str) {
124     return files_writeToFd(fd, (const uint8_t*)str, strlen(str));
125 }
126 
files_readFromFd(int fd,uint8_t * buf,size_t fileSz)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 
files_readFromFdSeek(int fd,uint8_t * buf,size_t fileSz,off_t off)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 
files_exists(const char * fileName)150 bool files_exists(const char* fileName) {
151     return (access(fileName, F_OK) != -1);
152 }
153 
files_writePatternToFd(int fd,off_t size,unsigned char p)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 
files_sendToSocketNB(int fd,const uint8_t * buf,size_t fileSz)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 
files_sendToSocket(int fd,const uint8_t * buf,size_t fileSz)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 
files_basename(const char * path)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  */
files_copyFile(const char * source,const char * destination,bool * dstExists,bool try_link)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  */
files_parseSymbolFilter(const char * srcFile,char *** filterList)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 
files_mapFile(const char * fileName,off_t * fileSz,int * fd,bool isWritable)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 
files_mapFileShared(const char * fileName,off_t * fileSz,int * fd)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 
files_mapSharedMem(size_t sz,int * fd,const char * name,const char * dir)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 
files_sockFamily(int sock)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 
files_sockAddrToStr(const struct sockaddr * sa)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