blob: 0a3ba5918cfc53fd4099587703504929a0e7491e [file] [log] [blame]
robert.swiecki3bb518c2010-10-14 00:48:24 +00001/*
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00002 *
robert.swiecki@gmail.com81189852015-02-14 23:44:55 +00003 * honggfuzz - file operations
4 * -----------------------------------------
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00005 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +00006 * Author: Robert Swiecki <swiecki@google.com>
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00007 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +00008 * Copyright 2010-2015 by Google Inc. All Rights Reserved.
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +00009 *
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
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000012 * a copy of the License at
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000013 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000014 * http://www.apache.org/licenses/LICENSE-2.0
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000015 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000016 * 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.
robert.swiecki@gmail.com3b630b42015-02-16 10:53:53 +000021 *
robert.swiecki@gmail.com772b33d2015-02-14 20:35:00 +000022 */
robert.swiecki3bb518c2010-10-14 00:48:24 +000023
robert.swiecki3bb518c2010-10-14 00:48:24 +000024#include "common.h"
25#include "files.h"
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000026
27#include <dirent.h>
28#include <errno.h>
29#include <fcntl.h>
robert.swiecki@gmail.com86941cb2015-02-22 15:15:08 +000030#include <inttypes.h>
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000031#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
robert.swiecki3bb518c2010-10-14 00:48:24 +000040#include "log.h"
41
robert.swiecki@gmail.com4a7a9d82015-03-01 01:25:16 +000042size_t files_readFileToBufMax(char *fileName, uint8_t * buf, size_t fileMaxSz)
43{
44 int fd = open(fileName, O_RDONLY);
45 if (fd == -1) {
46 LOGMSG_P(l_ERROR, "Couldn't open '%s' for R/O", fileName);
47 return 0UL;
48 }
49
50 struct stat st;
51 if (fstat(fd, &st) == -1) {
52 LOGMSG_P(l_ERROR, "Couldn't fstat(fd='%d' fileName='%s')", fd, fileName);
53 close(fd);
54 return 0UL;
55 }
56
robert.swiecki@gmail.comc1b759b2015-03-01 18:41:15 +000057 if (st.st_size > (off_t) fileMaxSz) {
58 LOGMSG(l_ERROR, "File '%s' size to big (%zu > %" PRId64 ")", fileName, (int64_t) st.st_size,
robert.swiecki@gmail.com4a7a9d82015-03-01 01:25:16 +000059 fileMaxSz);
60 close(fd);
61 return 0UL;
62 }
63
64 if (files_readFromFd(fd, buf, (size_t) st.st_size) == false) {
65 LOGMSG(l_ERROR, "Couldn't read '%s' to a buf", fileName);
66 close(fd);
67 return 0UL;
68 }
robert.swiecki@gmail.comdc8403e2015-03-01 01:33:00 +000069 close(fd);
robert.swiecki@gmail.com4a7a9d82015-03-01 01:25:16 +000070
robert.swiecki@gmail.com276f9f12015-03-01 01:39:47 +000071 LOGMSG(l_DEBUG, "Read '%zu' bytes (max: '%zu') from '%s'", (size_t) st.st_size, fileMaxSz,
72 fileName);
73
robert.swiecki@gmail.com4a7a9d82015-03-01 01:25:16 +000074 return (size_t) st.st_size;
75}
76
robert.swiecki@gmail.comdc8403e2015-03-01 01:33:00 +000077bool files_writeBufToFile(char *fileName, uint8_t * buf, size_t fileSz, int flags)
78{
79 int fd = open(fileName, flags, 0644);
80 if (fd == -1) {
81 LOGMSG_P(l_ERROR, "Couldn't open '%s' for R/O", fileName);
robert.swiecki@gmail.com276f9f12015-03-01 01:39:47 +000082 return false;
robert.swiecki@gmail.comdc8403e2015-03-01 01:33:00 +000083 }
84
85 if (files_writeToFd(fd, buf, fileSz) == false) {
robert.swiecki@gmail.com276f9f12015-03-01 01:39:47 +000086 LOGMSG(l_ERROR, "Couldn't write '%zu' bytes to file '%s' (fd='%d')", fileSz, fileName, fd);
robert.swiecki@gmail.comdc8403e2015-03-01 01:33:00 +000087 close(fd);
robert.swiecki@gmail.com9be63de2015-03-02 07:08:45 +000088 unlink(fileName);
robert.swiecki@gmail.comdc8403e2015-03-01 01:33:00 +000089 return false;
90 }
91 close(fd);
92
robert.swiecki@gmail.com276f9f12015-03-01 01:39:47 +000093 LOGMSG(l_DEBUG, "Written '%zu' bytes to '%s'", fileSz, fileName);
94
robert.swiecki@gmail.comdc8403e2015-03-01 01:33:00 +000095 return true;
96}
97
robert.swiecki@gmail.com26a9ab72015-02-22 13:41:18 +000098bool files_writeToFd(int fd, uint8_t * buf, size_t fileSz)
robert.swiecki3bb518c2010-10-14 00:48:24 +000099{
robert.swiecki@gmail.com26a9ab72015-02-22 13:41:18 +0000100 size_t writtenSz = 0;
101 while (writtenSz < fileSz) {
102 ssize_t sz = write(fd, &buf[writtenSz], fileSz - writtenSz);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000103 if (sz < 0 && errno == EINTR)
104 continue;
105
106 if (sz < 0)
107 return false;
108
robert.swiecki@gmail.com26a9ab72015-02-22 13:41:18 +0000109 writtenSz += sz;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000110 }
robert.swiecki3bb518c2010-10-14 00:48:24 +0000111 return true;
112}
113
robert.swiecki@gmail.comad6af222015-02-14 01:56:08 +0000114bool files_writeStrToFd(int fd, char *str)
115{
116 return files_writeToFd(fd, (uint8_t *) str, strlen(str));
117}
118
robert.swiecki@gmail.com26a9ab72015-02-22 13:41:18 +0000119bool files_readFromFd(int fd, uint8_t * buf, size_t fileSz)
120{
121 size_t readSz = 0;
122 while (readSz < fileSz) {
123 ssize_t sz = read(fd, &buf[readSz], fileSz - readSz);
124 if (sz < 0 && errno == EINTR)
125 continue;
126
127 if (sz < 0)
128 return false;
129
130 readSz += sz;
131 }
132 return true;
133}
134
groebert@google.com1bd4c212013-06-19 11:13:56 +0000135bool files_exists(char *fileName)
136{
137 return (access(fileName, F_OK) != -1);
138}
139
robert.swiecki3bb518c2010-10-14 00:48:24 +0000140bool files_writePatternToFd(int fd, off_t size, unsigned char p)
141{
142 void *buf = malloc(size);
143 if (!buf) {
144 LOGMSG_P(l_WARN, "Couldn't allocate memory");
145 return false;
146 }
147
148 memset(buf, p, (size_t) size);
149 int ret = files_writeToFd(fd, buf, size);
150 free(buf);
151
152 return ret;
153}
154
robert.swiecki3bb518c2010-10-14 00:48:24 +0000155static bool files_readdir(honggfuzz_t * hfuzz)
156{
157 DIR *dir = opendir(hfuzz->inputFile);
158 if (!dir) {
159 LOGMSG_P(l_ERROR, "Couldn't open dir '%s'", hfuzz->inputFile);
160 return false;
161 }
162
163 int count = 0;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000164 for (;;) {
165 struct dirent de, *res;
166 if (readdir_r(dir, &de, &res) > 0) {
167 LOGMSG_P(l_ERROR, "Couldn't read the '%s' dir", hfuzz->inputFile);
168 closedir(dir);
169 return false;
170 }
171
172 if (res == NULL && count > 0) {
173 LOGMSG(l_INFO, "%d input files have been added to the list", hfuzz->fileCnt);
174 closedir(dir);
175 return true;
176 }
177
178 if (res == NULL && count == 0) {
179 LOGMSG(l_ERROR, "Directory '%s' doesn't contain any regular files", hfuzz->inputFile);
180 closedir(dir);
181 return false;
182 }
183
184 char path[PATH_MAX];
185 snprintf(path, sizeof(path), "%s/%s", hfuzz->inputFile, res->d_name);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000186 struct stat st;
187 if (stat(path, &st) == -1) {
188 LOGMSG(l_WARN, "Couldn't stat() the '%s' file", path);
189 continue;
190 }
191
192 if (!S_ISREG(st.st_mode)) {
193 LOGMSG(l_DEBUG, "'%s' is not a regular file, skipping", path);
194 continue;
195 }
196
robert.swiecki@gmail.comc1b759b2015-03-01 18:41:15 +0000197 if (st.st_size == 0ULL) {
robert.swiecki3bb518c2010-10-14 00:48:24 +0000198 LOGMSG(l_DEBUG, "'%s' is empty", path);
199 continue;
200 }
201
robert.swiecki@gmail.com86941cb2015-02-22 15:15:08 +0000202 if (st.st_size > (off_t) hfuzz->maxFileSz) {
203 LOGMSG(l_WARN,
204 "File '%s' is bigger than maximal defined file size (-F): %" PRId64 " > %"
205 PRId64, path, (int64_t) st.st_size, (int64_t) hfuzz->maxFileSz);
206 continue;
207 }
208
robert.swiecki3bb518c2010-10-14 00:48:24 +0000209 if (!(hfuzz->files = realloc(hfuzz->files, sizeof(char *) * (count + 1)))) {
210 LOGMSG_P(l_ERROR, "Couldn't allocate memory");
211 closedir(dir);
212 return false;
213 }
214
215 hfuzz->files[count] = strdup(path);
216 if (!hfuzz->files[count]) {
217 LOGMSG_P(l_ERROR, "Couldn't allocate memory");
218 closedir(dir);
219 return false;
220 }
221 hfuzz->fileCnt = ++count;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000222 LOGMSG(l_DEBUG, "Added '%s' to the list of input files", path);
223 }
224
225 abort(); /* NOTREACHED */
226 return false;
227}
228
229bool files_init(honggfuzz_t * hfuzz)
230{
robert.swiecki@gmail.comea1be292014-07-21 17:36:50 +0000231 hfuzz->files = malloc(sizeof(char *));
robert.swiecki@gmail.comcac22fd2015-02-19 14:03:28 +0000232 if (hfuzz->dynFileMethod != _HF_DYNFILE_NONE && !hfuzz->inputFile) {
robert.swiecki@gmail.comcfff7592015-02-18 00:06:19 +0000233 hfuzz->fileCnt = 1;
robert.swiecki@gmail.comcac22fd2015-02-19 14:03:28 +0000234 hfuzz->files[0] = "DYNAMIC_FILE";
robert.swiecki@gmail.com6d6f7562015-02-17 22:18:51 +0000235 return true;
236 }
robert.swiecki@gmail.comebc1cac2011-07-02 03:15:51 +0000237 if (hfuzz->externalCommand && !hfuzz->inputFile) {
robert.swiecki@gmail.comc6deb6f2014-06-03 18:17:55 +0000238 hfuzz->fileCnt = 1;
robert.swiecki@gmail.com73df4252015-02-18 00:09:48 +0000239 hfuzz->files[0] = "CREATED";
robert.swiecki@gmail.comebc1cac2011-07-02 03:15:51 +0000240 LOGMSG(l_INFO,
241 "No input file corpus specified, the external command '%s' is responsible for creating the fuzz files",
242 hfuzz->externalCommand);
243 return true;
244 }
245
robert.swiecki3bb518c2010-10-14 00:48:24 +0000246 if (!hfuzz->files) {
247 LOGMSG_P(l_ERROR, "Couldn't allocate memory");
248 return false;
249 }
250
251 if (!hfuzz->inputFile) {
252 LOGMSG(l_ERROR, "No input file/dir specified");
253 return false;
254 }
255
256 struct stat st;
257 if (stat(hfuzz->inputFile, &st) == -1) {
258 LOGMSG_P(l_ERROR, "Couldn't stat the input file/dir '%s'", hfuzz->inputFile);
259 return false;
260 }
261
262 if (S_ISDIR(st.st_mode)) {
263 return files_readdir(hfuzz);
264 }
265
266 if (!S_ISREG(st.st_mode)) {
267 LOGMSG(l_ERROR, "'%s' is not a regular file, nor a directory", hfuzz->inputFile);
268 return false;
269 }
270
robert.swiecki@gmail.com2e0103f2015-03-30 01:38:12 +0000271 if (st.st_size > (off_t) hfuzz->maxFileSz) {
272 LOGMSG(l_ERROR,
273 "File '%s' is bigger than maximal defined file size (-F): %" PRId64 " > %" PRId64,
274 hfuzz->inputFile, (int64_t) st.st_size, (int64_t) hfuzz->maxFileSz);
275 return false;
276 }
277
robert.swiecki3bb518c2010-10-14 00:48:24 +0000278 hfuzz->files[0] = hfuzz->inputFile;
279 hfuzz->fileCnt = 1;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000280 return true;
281}
groebert@google.com1c7e3b02013-06-19 09:27:38 +0000282
groebert@google.com1bd4c212013-06-19 11:13:56 +0000283char *files_basename(char *path)
284{
groebert@google.com1c7e3b02013-06-19 09:27:38 +0000285 char *base = strrchr(path, '/');
286 return base ? base + 1 : path;
287}
robert.swiecki@gmail.com4f1124f2015-04-21 17:12:22 +0000288
289bool files_parseDictionary(honggfuzz_t * hfuzz)
290{
291 FILE *fDict = fopen(hfuzz->dictionaryFile, "rb");
292 if (fDict == NULL) {
293 LOGMSG_P(l_ERROR, "Couldn't open '%s' - R/O mode", hfuzz->dictionaryFile);
294 return false;
295 }
296
robert.swiecki@gmail.com0440c532015-04-21 17:27:08 +0000297 for (;;) {
298 char *lineptr = NULL;
299 size_t n = 0;
300 if (getdelim(&lineptr, &n, '\0', fDict) == -1) {
301 break;
302 }
robert.swiecki@gmail.com4f1124f2015-04-21 17:12:22 +0000303 if ((hfuzz->dictionary =
304 realloc(hfuzz->dictionary,
robert.swiecki@gmail.comd9363be2015-04-21 17:24:02 +0000305 (hfuzz->dictionaryCnt + 1) * sizeof(hfuzz->dictionary[0]))) == NULL) {
robert.swiecki@gmail.com4f1124f2015-04-21 17:12:22 +0000306 LOGMSG_P(l_ERROR, "Realloc failed (sz=%zu)",
robert.swiecki@gmail.comd9363be2015-04-21 17:24:02 +0000307 (hfuzz->dictionaryCnt + 1) * sizeof(hfuzz->dictionary[0]));
robert.swiecki@gmail.com4f1124f2015-04-21 17:12:22 +0000308 fclose(fDict);
309 return false;
310 }
311 hfuzz->dictionary[hfuzz->dictionaryCnt] = lineptr;
robert.swiecki@gmail.comd9363be2015-04-21 17:24:02 +0000312 LOGMSG(l_DEBUG, "Dictionary: loaded word: '%s' (len=%zu)",
313 hfuzz->dictionary[hfuzz->dictionaryCnt],
robert.swiecki@gmail.com4f1124f2015-04-21 17:12:22 +0000314 strlen(hfuzz->dictionary[hfuzz->dictionaryCnt]));
315 hfuzz->dictionaryCnt += 1;
316 }
317 LOGMSG(l_INFO, "Loaded %zu words from the dictionary", hfuzz->dictionaryCnt);
318 fclose(fDict);
319 return true;
320}
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300321
322/*
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300323 * dstExists argument can be used by caller for cases where existing destination
324 * file requires special handling (e.g. save unique crashes)
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300325 */
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300326bool files_copyFile(const char *source, const char *destination, bool * dstExists)
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300327{
Anestis Bechtsoudis38773212015-09-08 23:34:28 +0300328 if (dstExists)
329 *dstExists = false;
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300330 if (link(source, destination) == 0) {
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300331 return true;
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300332 } else {
333 if (errno == EEXIST) {
334 // Should kick-in before MAC, so avoid the hassle
Anestis Bechtsoudis38773212015-09-08 23:34:28 +0300335 if (dstExists)
336 *dstExists = true;
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300337 return false;
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300338 } else {
339 LOGMSG_P(l_DEBUG, "Couldn't link '%s' as '%s'", source, destination);
340 /*
341 * Don't fail yet as we might have a running env which doesn't allow
342 * hardlinks (e.g. SELinux)
343 */
344 }
345 }
346
347 // Now try with a verbose POSIX alternative
348 int inFD, outFD, dstOpenFlags;
349 mode_t dstFilePerms;
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300350
351 // O_EXCL is important for saving unique crashes
352 dstOpenFlags = O_CREAT | O_WRONLY | O_EXCL;
353 dstFilePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
354
355 inFD = open(source, O_RDONLY);
356 if (inFD == -1) {
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300357 LOGMSG_P(l_DEBUG, "Couldn't open '%s' source", source);
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300358 return false;
359 }
360
361 struct stat inSt;
362 if (fstat(inFD, &inSt) == -1) {
363 LOGMSG_P(l_ERROR, "Couldn't fstat(fd='%d' fileName='%s')", inFD, source);
364 close(inFD);
365 return false;
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300366 }
367
368 outFD = open(destination, dstOpenFlags, dstFilePerms);
369 if (outFD == -1) {
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300370 if (errno == EEXIST) {
Anestis Bechtsoudis38773212015-09-08 23:34:28 +0300371 if (dstExists)
372 *dstExists = true;
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300373 }
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300374 LOGMSG_P(l_DEBUG, "Couldn't open '%s' destination", destination);
375 close(inFD);
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300376 return false;
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300377 }
378
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300379 uint8_t *inFileBuf = malloc(inSt.st_size);
380 if (!inFileBuf) {
381 LOGMSG(l_ERROR, "malloc(%zu) failed", inSt.st_size);
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300382 close(inFD);
383 close(outFD);
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300384 return false;
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300385 }
386
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300387 if (files_readFromFd(inFD, inFileBuf, (size_t) inSt.st_size) == false) {
388 LOGMSG(l_ERROR, "Couldn't read '%s' to a buf", source);
389 free(inFileBuf);
390 close(inFD);
391 close(outFD);
392 return false;
393 }
394
395 if (files_writeToFd(outFD, inFileBuf, inSt.st_size) == false) {
396 LOGMSG(l_ERROR, "Couldn't write '%zu' bytes to file '%s' (fd='%d')", inSt.st_size,
397 destination, outFD);
398 free(inFileBuf);
399 close(inFD);
400 close(outFD);
401 unlink(destination);
402 return false;
403 }
404
405 free(inFileBuf);
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300406 close(inFD);
407 close(outFD);
Anestis Bechtsoudis1ae1ce12015-09-06 23:43:48 +0300408 return true;
Anestis Bechtsoudis7d42f9d2015-09-06 14:30:20 +0300409}