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 "common.h"
25 #include "files.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/stat.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39
40 #include "log.h"
41 #include "util.h"
42
files_readFileToBufMax(char * fileName,uint8_t * buf,size_t fileMaxSz)43 size_t files_readFileToBufMax(char *fileName, uint8_t * buf, size_t fileMaxSz)
44 {
45 int fd = open(fileName, O_RDONLY);
46 if (fd == -1) {
47 PLOG_E("Couldn't open '%s' for R/O", fileName);
48 return 0UL;
49 }
50 DEFER(close(fd));
51
52 size_t readSz = files_readFromFd(fd, buf, fileMaxSz);
53 if (readSz == 0) {
54 LOG_E("Couldn't read '%s' to a buf", fileName);
55 return 0UL;
56 }
57
58 LOG_D("Read '%zu' bytes from '%s'", readSz, fileName);
59 return readSz;
60 }
61
files_writeBufToFile(char * fileName,uint8_t * buf,size_t fileSz,int flags)62 bool files_writeBufToFile(char *fileName, uint8_t * buf, size_t fileSz, int flags)
63 {
64 int fd = open(fileName, flags, 0644);
65 if (fd == -1) {
66 PLOG_E("Couldn't open '%s' for R/O", fileName);
67 return false;
68 }
69 DEFER(close(fd));
70
71 if (files_writeToFd(fd, buf, fileSz) == false) {
72 PLOG_E("Couldn't write '%zu' bytes to file '%s' (fd='%d')", fileSz, fileName, fd);
73 unlink(fileName);
74 return false;
75 }
76
77 LOG_D("Written '%zu' bytes to '%s'", fileSz, fileName);
78 return true;
79 }
80
files_writeToFd(int fd,uint8_t * buf,size_t fileSz)81 bool files_writeToFd(int fd, uint8_t * buf, size_t fileSz)
82 {
83 size_t writtenSz = 0;
84 while (writtenSz < fileSz) {
85 ssize_t sz = write(fd, &buf[writtenSz], fileSz - writtenSz);
86 if (sz < 0 && errno == EINTR)
87 continue;
88
89 if (sz < 0)
90 return false;
91
92 writtenSz += sz;
93 }
94 return true;
95 }
96
files_writeStrToFd(int fd,char * str)97 bool files_writeStrToFd(int fd, char *str)
98 {
99 return files_writeToFd(fd, (uint8_t *) str, strlen(str));
100 }
101
files_readFromFd(int fd,uint8_t * buf,size_t fileSz)102 size_t files_readFromFd(int fd, uint8_t * buf, size_t fileSz)
103 {
104 size_t readSz = 0;
105 while (readSz < fileSz) {
106 ssize_t sz = read(fd, &buf[readSz], fileSz - readSz);
107 if (sz < 0 && errno == EINTR)
108 continue;
109
110 if (sz <= 0)
111 break;
112
113 readSz += sz;
114 }
115 return readSz;
116 }
117
files_exists(char * fileName)118 bool files_exists(char *fileName)
119 {
120 return (access(fileName, F_OK) != -1);
121 }
122
files_writePatternToFd(int fd,off_t size,unsigned char p)123 bool files_writePatternToFd(int fd, off_t size, unsigned char p)
124 {
125 void *buf = malloc(size);
126 if (!buf) {
127 PLOG_W("Couldn't allocate memory");
128 return false;
129 }
130 DEFER(free(buf));
131
132 memset(buf, p, (size_t) size);
133 int ret = files_writeToFd(fd, buf, size);
134
135 return ret;
136 }
137
files_readdir(honggfuzz_t * hfuzz)138 static bool files_readdir(honggfuzz_t * hfuzz)
139 {
140 DIR *dir = opendir(hfuzz->inputFile);
141 if (!dir) {
142 PLOG_E("Couldn't open dir '%s'", hfuzz->inputFile);
143 return false;
144 }
145 DEFER(closedir(dir));
146
147 int count = 0;
148 for (;;) {
149 struct dirent de, *res;
150 if (readdir_r(dir, &de, &res) > 0) {
151 PLOG_E("Couldn't read the '%s' dir", hfuzz->inputFile);
152 return false;
153 }
154
155 if (res == NULL && count > 0) {
156 LOG_I("%zu input files have been added to the list", hfuzz->fileCnt);
157 return true;
158 }
159
160 if (res == NULL && count == 0) {
161 LOG_E("Directory '%s' doesn't contain any regular files", hfuzz->inputFile);
162 return false;
163 }
164
165 char path[PATH_MAX];
166 snprintf(path, sizeof(path), "%s/%s", hfuzz->inputFile, res->d_name);
167 struct stat st;
168 if (stat(path, &st) == -1) {
169 LOG_W("Couldn't stat() the '%s' file", path);
170 continue;
171 }
172
173 if (!S_ISREG(st.st_mode)) {
174 LOG_D("'%s' is not a regular file, skipping", path);
175 continue;
176 }
177
178 if (st.st_size == 0ULL) {
179 LOG_D("'%s' is empty", path);
180 continue;
181 }
182
183 if (st.st_size > (off_t) hfuzz->maxFileSz) {
184 LOG_W("File '%s' is bigger than maximal defined file size (-F): %" PRId64 " > %"
185 PRId64, path, (int64_t) st.st_size, (int64_t) hfuzz->maxFileSz);
186 continue;
187 }
188
189 if (!(hfuzz->files = realloc(hfuzz->files, sizeof(char *) * (count + 1)))) {
190 PLOG_E("Couldn't allocate memory");
191 return false;
192 }
193
194 hfuzz->files[count] = strdup(path);
195 if (!hfuzz->files[count]) {
196 PLOG_E("Couldn't allocate memory");
197 return false;
198 }
199 hfuzz->fileCnt = ++count;
200 LOG_D("Added '%s' to the list of input files", path);
201 }
202
203 abort(); /* NOTREACHED */
204 return false;
205 }
206
files_init(honggfuzz_t * hfuzz)207 bool files_init(honggfuzz_t * hfuzz)
208 {
209 hfuzz->files = util_Malloc(sizeof(char *));
210 if (hfuzz->externalCommand && !hfuzz->inputFile) {
211 hfuzz->fileCnt = 1;
212 hfuzz->files[0] = "CREATED";
213 LOG_I
214 ("No input file corpus specified, the external command '%s' is responsible for creating the fuzz files",
215 hfuzz->externalCommand);
216 return true;
217 }
218
219 if (!hfuzz->inputFile) {
220 LOG_E("No input file/dir specified");
221 return false;
222 }
223
224 struct stat st;
225 if (stat(hfuzz->inputFile, &st) == -1) {
226 PLOG_E("Couldn't stat the input file/dir '%s'", hfuzz->inputFile);
227 return false;
228 }
229
230 if (S_ISDIR(st.st_mode)) {
231 return files_readdir(hfuzz);
232 }
233
234 if (!S_ISREG(st.st_mode)) {
235 LOG_E("'%s' is not a regular file, nor a directory", hfuzz->inputFile);
236 return false;
237 }
238
239 if (st.st_size > (off_t) hfuzz->maxFileSz) {
240 LOG_E("File '%s' is bigger than maximal defined file size (-F): %" PRId64 " > %" PRId64,
241 hfuzz->inputFile, (int64_t) st.st_size, (int64_t) hfuzz->maxFileSz);
242 return false;
243 }
244
245 hfuzz->files[0] = hfuzz->inputFile;
246 hfuzz->fileCnt = 1;
247 return true;
248 }
249
files_basename(char * path)250 char *files_basename(char *path)
251 {
252 char *base = strrchr(path, '/');
253 return base ? base + 1 : path;
254 }
255
files_parseDictionary(honggfuzz_t * hfuzz)256 bool files_parseDictionary(honggfuzz_t * hfuzz)
257 {
258 FILE *fDict = fopen(hfuzz->dictionaryFile, "rb");
259 if (fDict == NULL) {
260 PLOG_E("Couldn't open '%s' - R/O mode", hfuzz->dictionaryFile);
261 return false;
262 }
263 DEFER(fclose(fDict));
264
265 for (;;) {
266 char *lineptr = NULL;
267 size_t n = 0;
268 if (getdelim(&lineptr, &n, '\0', fDict) == -1) {
269 break;
270 }
271 if ((hfuzz->dictionary =
272 realloc(hfuzz->dictionary,
273 (hfuzz->dictionaryCnt + 1) * sizeof(hfuzz->dictionary[0]))) == NULL) {
274 PLOG_E("Realloc failed (sz=%zu)",
275 (hfuzz->dictionaryCnt + 1) * sizeof(hfuzz->dictionary[0]));
276 return false;
277 }
278 hfuzz->dictionary[hfuzz->dictionaryCnt] = lineptr;
279 LOG_D("Dictionary: loaded word: '%s' (len=%zu)",
280 hfuzz->dictionary[hfuzz->dictionaryCnt],
281 strlen(hfuzz->dictionary[hfuzz->dictionaryCnt]));
282 hfuzz->dictionaryCnt += 1;
283 }
284 LOG_I("Loaded %zu words from the dictionary", hfuzz->dictionaryCnt);
285 return true;
286 }
287
288 /*
289 * dstExists argument can be used by caller for cases where existing destination
290 * file requires special handling (e.g. save unique crashes)
291 */
files_copyFile(const char * source,const char * destination,bool * dstExists)292 bool files_copyFile(const char *source, const char *destination, bool * dstExists)
293 {
294 if (dstExists)
295 *dstExists = false;
296 if (link(source, destination) == 0) {
297 return true;
298 } else {
299 if (errno == EEXIST) {
300 // Should kick-in before MAC, so avoid the hassle
301 if (dstExists)
302 *dstExists = true;
303 return false;
304 } else {
305 PLOG_D("Couldn't link '%s' as '%s'", source, destination);
306 /*
307 * Don't fail yet as we might have a running env which doesn't allow
308 * hardlinks (e.g. SELinux)
309 */
310 }
311 }
312
313 // Now try with a verbose POSIX alternative
314 int inFD, outFD, dstOpenFlags;
315 mode_t dstFilePerms;
316
317 // O_EXCL is important for saving unique crashes
318 dstOpenFlags = O_CREAT | O_WRONLY | O_EXCL;
319 dstFilePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
320
321 inFD = open(source, O_RDONLY);
322 if (inFD == -1) {
323 PLOG_D("Couldn't open '%s' source", source);
324 return false;
325 }
326 DEFER(close(inFD));
327
328 struct stat inSt;
329 if (fstat(inFD, &inSt) == -1) {
330 PLOG_E("Couldn't fstat(fd='%d' fileName='%s')", inFD, source);
331 return false;
332 }
333
334 outFD = open(destination, dstOpenFlags, dstFilePerms);
335 if (outFD == -1) {
336 if (errno == EEXIST) {
337 if (dstExists)
338 *dstExists = true;
339 }
340 PLOG_D("Couldn't open '%s' destination", destination);
341 return false;
342 }
343 DEFER(close(outFD));
344
345 uint8_t *inFileBuf = malloc(inSt.st_size);
346 if (!inFileBuf) {
347 PLOG_E("malloc(%zu) failed", (size_t) inSt.st_size);
348 return false;
349 }
350 DEFER(free(inFileBuf));
351
352 size_t readSz = files_readFromFd(inFD, inFileBuf, (size_t) inSt.st_size);
353 if (readSz == 0) {
354 PLOG_E("Couldn't read '%s' to a buf", source);
355 return false;
356 }
357
358 if (files_writeToFd(outFD, inFileBuf, readSz) == false) {
359 PLOG_E("Couldn't write '%zu' bytes to file '%s' (fd='%d')", (size_t) readSz,
360 destination, outFD);
361 unlink(destination);
362 return false;
363 }
364
365 return true;
366 }
367
files_parseBlacklist(honggfuzz_t * hfuzz)368 bool files_parseBlacklist(honggfuzz_t * hfuzz)
369 {
370 FILE *fBl = fopen(hfuzz->blacklistFile, "rb");
371 if (fBl == NULL) {
372 PLOG_E("Couldn't open '%s' - R/O mode", hfuzz->blacklistFile);
373 return false;
374 }
375 DEFER(fclose(fBl));
376
377 char *lineptr = NULL;
378 size_t n = 0;
379 for (;;) {
380 if (getline(&lineptr, &n, fBl) == -1) {
381 break;
382 }
383 DEFER(free(lineptr));
384
385 if ((hfuzz->blacklist =
386 realloc(hfuzz->blacklist,
387 (hfuzz->blacklistCnt + 1) * sizeof(hfuzz->blacklist[0]))) == NULL) {
388 PLOG_E("realloc failed (sz=%zu)",
389 (hfuzz->blacklistCnt + 1) * sizeof(hfuzz->blacklist[0]));
390 return false;
391 }
392
393 hfuzz->blacklist[hfuzz->blacklistCnt] = strtoull(lineptr, 0, 16);
394 LOG_D("Blacklist: loaded %'" PRIu64 "'", hfuzz->blacklist[hfuzz->blacklistCnt]);
395
396 // Verify entries are sorted so we can use interpolation search
397 if (hfuzz->blacklistCnt > 1) {
398 if (hfuzz->blacklist[hfuzz->blacklistCnt - 1] > hfuzz->blacklist[hfuzz->blacklistCnt]) {
399 LOG_F
400 ("Blacklist file not sorted. Use 'tools/createStackBlacklist.sh' to sort records");
401 return false;
402 }
403 }
404 hfuzz->blacklistCnt += 1;
405 }
406
407 if (hfuzz->blacklistCnt > 0) {
408 LOG_I("Loaded %zu stack hash(es) from the blacklist file", hfuzz->blacklistCnt);
409 } else {
410 LOG_F("Empty stack hashes blacklist file '%s'", hfuzz->blacklistFile);
411 }
412 return true;
413 }
414
files_mapFile(char * fileName,off_t * fileSz,int * fd,bool isWritable)415 uint8_t *files_mapFile(char *fileName, off_t * fileSz, int *fd, bool isWritable)
416 {
417 int mmapProt = PROT_READ;
418 if (isWritable) {
419 mmapProt |= PROT_WRITE;
420 }
421
422 if ((*fd = open(fileName, O_RDONLY)) == -1) {
423 PLOG_E("Couldn't open() '%s' file in R/O mode", fileName);
424 return NULL;
425 }
426
427 struct stat st;
428 if (fstat(*fd, &st) == -1) {
429 PLOG_E("Couldn't stat() the '%s' file", fileName);
430 close(*fd);
431 return NULL;
432 }
433
434 uint8_t *buf;
435 if ((buf = mmap(NULL, st.st_size, mmapProt, MAP_PRIVATE, *fd, 0)) == MAP_FAILED) {
436 PLOG_E("Couldn't mmap() the '%s' file", fileName);
437 close(*fd);
438 return NULL;
439 }
440
441 *fileSz = st.st_size;
442 return buf;
443 }
444
files_readPidFromFile(const char * fileName,pid_t * pidPtr)445 bool files_readPidFromFile(const char *fileName, pid_t * pidPtr)
446 {
447 FILE *fPID = fopen(fileName, "rb");
448 if (fPID == NULL) {
449 PLOG_E("Couldn't open '%s' - R/O mode", fileName);
450 return false;
451 }
452 DEFER(fclose(fPID));
453
454 char *lineptr = NULL;
455 size_t lineSz = 0;
456 if (getline(&lineptr, &lineSz, fPID) == -1) {
457 if (lineSz == 0) {
458 LOG_E("Empty PID file (%s)", fileName);
459 return false;
460 }
461 }
462 DEFER(free(lineptr));
463
464 *pidPtr = atoi(lineptr);
465 if (*pidPtr < 1) {
466 LOG_E("Invalid PID read from '%s' file", fileName);
467 return false;
468 }
469
470 return true;
471 }
472