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