blob: a2ba6071775557df5b15f70b7c908da7208b758c [file] [log] [blame]
robert.swiecki3bb518c2010-10-14 00:48:24 +00001/*
2
3 honggfuzz - fuzzing routines
4 -----------------------------------------
5
6 Author: Robert Swiecki <swiecki@google.com>
groebert@google.comb857deb2010-10-21 19:09:29 +00007 Felix Gröbert <groebert@google.com>
robert.swiecki3bb518c2010-10-14 00:48:24 +00008
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +00009 Copyright 2010-2015 by Google Inc. All Rights Reserved.
robert.swiecki3bb518c2010-10-14 00:48:24 +000010
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22
23*/
24
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000025#include "common.h"
26#include "fuzz.h"
27
28#include <errno.h>
29#include <fcntl.h>
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +000030#include <pthread.h>
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000031#include <signal.h>
32#include <stddef.h>
33#include <stdint.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
robert.swiecki3bb518c2010-10-14 00:48:24 +000037#include <sys/mman.h>
38#include <sys/param.h>
39#include <sys/stat.h>
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000040#include <sys/time.h>
41#include <sys/types.h>
42#include <sys/wait.h>
robert.swiecki3bb518c2010-10-14 00:48:24 +000043#include <time.h>
robert.swiecki@gmail.comba85c3e2015-02-02 14:55:16 +000044#include <unistd.h>
robert.swiecki3bb518c2010-10-14 00:48:24 +000045
robert.swiecki3bb518c2010-10-14 00:48:24 +000046#include "log.h"
47#include "arch.h"
48#include "util.h"
49#include "files.h"
50
51static void fuzz_mangleContent(honggfuzz_t * hfuzz, uint8_t * buf, off_t fileSz)
52{
robert.swieckicccbf0c2011-01-28 11:58:04 +000053 /*
54 * Just copy the file if "-r 0"
55 */
robert.swieckiba512632011-01-28 11:57:26 +000056 if (hfuzz->flipRate == 0.0) {
57 return;
58 }
59
robert.swiecki3bb518c2010-10-14 00:48:24 +000060 uint64_t changesCnt = fileSz * hfuzz->flipRate;
61
62 if (hfuzz->flipMode == 'b') {
63 changesCnt *= 8UL;
64 }
65
66 changesCnt = util_rndGet(1, changesCnt);
67
68 for (uint64_t x = 0; x < changesCnt; x++) {
69 off_t pos = util_rndGet(0, fileSz - 1);
70
71 if (hfuzz->flipMode == 'b') {
72 buf[pos] ^= (1 << util_rndGet(0, 7));
73 } else {
74 buf[pos] = (uint8_t) util_rndGet(0, 255);
75 }
76 }
77}
78
79static void fuzz_getFileName(honggfuzz_t * hfuzz, char *fileName)
80{
robert.swieckiba512632011-01-28 11:57:26 +000081 struct timeval tv;
82 gettimeofday(&tv, NULL);
83
84 snprintf(fileName, PATH_MAX, ".honggfuzz.%d.%lu.%lu.%lu.%s", (int)getpid(),
85 (unsigned long int)tv.tv_sec, (unsigned long int)tv.tv_usec,
robert.swiecki3bb518c2010-10-14 00:48:24 +000086 (unsigned long int)util_rndGet(0, 1 << 30), hfuzz->fileExtn);
87
88 return;
89}
90
robert.swiecki@gmail.comd7aed312015-02-03 21:26:37 +000091static void fuzz_appendOrTrunc(int fd, off_t fileSz)
92{
93 const int chance_one_in_x = 10;
94 if (util_rndGet(1, chance_one_in_x) != 1) {
95 return;
96 }
97
robert.swiecki@gmail.com37f194e2015-02-03 21:35:07 +000098 off_t maxSz = 2 * fileSz;
99 if (fileSz > (1024 * 1024 * 20)) {
100 maxSz = fileSz + (fileSz / 4);
101 }
102 if (fileSz > (1024 * 1024 * 100)) {
103 maxSz = fileSz;
104 }
105
106 off_t newSz = util_rndGet(1, maxSz);
robert.swiecki@gmail.comd7aed312015-02-03 21:26:37 +0000107 if (ftruncate(fd, newSz) == -1) {
108 LOGMSG_P(l_WARN, "Couldn't truncate file from '%ld' to '%ld' bytes", (long)fileSz,
109 (long)newSz);
110 return;
111 }
112}
113
groebert@google.com1c7e3b02013-06-19 09:27:38 +0000114static bool fuzz_prepareFile(honggfuzz_t * hfuzz, char *fileName, int rnd_index)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000115{
robert.swiecki3bb518c2010-10-14 00:48:24 +0000116 off_t fileSz;
117 int srcfd;
118
119 uint8_t *buf = files_mapFileToRead(hfuzz->files[rnd_index], &fileSz, &srcfd);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000120 if (buf == NULL) {
robert.swiecki56b88012010-12-31 14:04:13 +0000121 LOGMSG(l_ERROR, "Couldn't open and map '%s' in R/O mode", hfuzz->files[rnd_index]);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000122 return false;
123 }
124
125 LOGMSG(l_DEBUG, "Mmaped '%s' in R/O mode, size: %d", hfuzz->files[rnd_index], fileSz);
126
127 int dstfd = open(fileName, O_CREAT | O_EXCL | O_RDWR, 0644);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000128 if (dstfd == -1) {
robert.swiecki3d505e22010-10-14 01:17:17 +0000129 LOGMSG_P(l_ERROR, "Couldn't create a temporary file '%s' in the current directory",
130 fileName);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000131 munmap(buf, fileSz);
132 close(srcfd);
133 return false;
134 }
135
136 fuzz_mangleContent(hfuzz, buf, fileSz);
137
138 if (!files_writeToFd(dstfd, buf, fileSz)) {
139 munmap(buf, fileSz);
140 close(srcfd);
141 close(dstfd);
142 return false;
143 }
144
145 munmap(buf, fileSz);
robert.swiecki@gmail.comd7aed312015-02-03 21:26:37 +0000146
147 fuzz_appendOrTrunc(dstfd, fileSz);
148
robert.swiecki3bb518c2010-10-14 00:48:24 +0000149 close(srcfd);
150 close(dstfd);
151 return true;
152}
153
groebert@google.com1c7e3b02013-06-19 09:27:38 +0000154static bool fuzz_prepareFileExternally(honggfuzz_t * hfuzz, char *fileName, int rnd_index)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000155{
robert.swiecki3d505e22010-10-14 01:17:17 +0000156 off_t fileSz;
157 int srcfd;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000158
robert.swiecki3d505e22010-10-14 01:17:17 +0000159 int dstfd = open(fileName, O_CREAT | O_EXCL | O_RDWR, 0644);
160 if (dstfd == -1) {
161 LOGMSG_P(l_ERROR, "Couldn't create a temporary file '%s' in the current directory",
162 fileName);
robert.swiecki@gmail.comebc1cac2011-07-02 03:15:51 +0000163 return false;
164 }
165
166 LOGMSG(l_DEBUG, "Created '%f' as an input file", fileName);
167
168 if (hfuzz->inputFile) {
169 uint8_t *buf = files_mapFileToRead(hfuzz->files[rnd_index], &fileSz, &srcfd);
170 if (buf == NULL) {
171 LOGMSG(l_ERROR, "Couldn't open and map '%s' in R/O mode", hfuzz->files[rnd_index]);
172 close(dstfd);
173 return false;
174 }
175
176 LOGMSG(l_DEBUG, "Mmaped '%s' in R/O mode, size: %d", hfuzz->files[rnd_index], fileSz);
177
178 bool ret = files_writeToFd(dstfd, buf, fileSz);
robert.swiecki3d505e22010-10-14 01:17:17 +0000179 munmap(buf, fileSz);
180 close(srcfd);
robert.swiecki@gmail.comebc1cac2011-07-02 03:15:51 +0000181
182 if (!ret) {
183 close(dstfd);
184 return false;
185 }
robert.swiecki3d505e22010-10-14 01:17:17 +0000186 }
187
robert.swiecki3d505e22010-10-14 01:17:17 +0000188 close(dstfd);
189
robert.swiecki3bb518c2010-10-14 00:48:24 +0000190 pid_t pid = fork();
robert.swiecki3bb518c2010-10-14 00:48:24 +0000191 if (pid == -1) {
192 LOGMSG_P(l_ERROR, "Couldn't fork");
193 return false;
194 }
195
196 if (!pid) {
197 /*
198 * child does the external file modifications
199 */
robert.swiecki3d505e22010-10-14 01:17:17 +0000200 execl(hfuzz->externalCommand, hfuzz->externalCommand, fileName, NULL);
groebert@google.comb857deb2010-10-21 19:09:29 +0000201 LOGMSG_P(l_FATAL, "Couldn't execute '%s %s'", hfuzz->externalCommand, fileName);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000202 return false;
203 } else {
204 /*
205 * parent waits until child is done fuzzing the input file
206 */
207
208 int childStatus;
209 pid_t terminatedPid;
210 do {
211 terminatedPid = wait(&childStatus);
212 } while (terminatedPid != pid);
213
214 if (WIFEXITED(childStatus)) {
215 LOGMSG(l_DEBUG, "External command exited with status %d", WEXITSTATUS(childStatus));
216 return true;
217 } else if (WIFSIGNALED(childStatus)) {
robert.swiecki3d505e22010-10-14 01:17:17 +0000218 LOGMSG(l_ERROR, "External command terminated with signal %d", WTERMSIG(childStatus));
robert.swiecki3bb518c2010-10-14 00:48:24 +0000219 return false;
220 }
robert.swiecki3d505e22010-10-14 01:17:17 +0000221 LOGMSG(l_FATAL, "External command terminated abnormally, status: %d", childStatus);
222 return false;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000223 }
224
225 abort(); /* NOTREACHED */
226}
227
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000228static void *fuzz_threadCreate(void *arg)
robert.swiecki3bb518c2010-10-14 00:48:24 +0000229{
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000230 honggfuzz_t *hfuzz = (honggfuzz_t *) arg;
231 fuzzer_t fuzzer = {
robert.swiecki@gmail.com1f98a162015-02-11 15:09:22 +0000232 .pid = hfuzz->pid,
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000233 .timeStarted = time(NULL),
234 .pc = 0ULL,
235 .backtrace = 0ULL,
236 .access = 0ULL,
237 .exception = 0,
238 };
robert.swiecki3bb518c2010-10-14 00:48:24 +0000239
groebert@google.com1c7e3b02013-06-19 09:27:38 +0000240 int rnd_index = util_rndGet(0, hfuzz->fileCnt - 1);
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000241 strncpy(fuzzer.origFileName, files_basename(hfuzz->files[rnd_index]), PATH_MAX);
242 fuzz_getFileName(hfuzz, fuzzer.fileName);
groebert@google.com1c7e3b02013-06-19 09:27:38 +0000243
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000244 /* Maybe we're fuzzing an external process */
robert.swiecki@gmail.com1f98a162015-02-11 15:09:22 +0000245 if (fuzzer.pid == 0) {
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000246 fuzzer.pid = fork();
robert.swiecki@gmail.com1f98a162015-02-11 15:09:22 +0000247 }
robert.swiecki3bb518c2010-10-14 00:48:24 +0000248
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000249 if (fuzzer.pid == -1) {
robert.swiecki3bb518c2010-10-14 00:48:24 +0000250 LOGMSG_P(l_FATAL, "Couldn't fork");
251 exit(EXIT_FAILURE);
252 }
253
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000254 if (!fuzzer.pid) {
robert.swiecki3bb518c2010-10-14 00:48:24 +0000255 /*
256 * We've forked, other pid's might have the same rnd seeds now,
257 * reinitialize it
258 */
259 util_rndInit();
260
robert.swiecki3bb518c2010-10-14 00:48:24 +0000261 if (hfuzz->externalCommand != NULL) {
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000262 if (!fuzz_prepareFileExternally(hfuzz, fuzzer.fileName, rnd_index)) {
robert.swiecki3bb518c2010-10-14 00:48:24 +0000263 exit(EXIT_FAILURE);
264 }
265 } else {
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000266 if (!fuzz_prepareFile(hfuzz, fuzzer.fileName, rnd_index)) {
robert.swiecki3bb518c2010-10-14 00:48:24 +0000267 exit(EXIT_FAILURE);
268 }
269 }
270
271 /*
272 * Ok, kill the parent
273 */
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000274 if (!arch_launchChild(hfuzz, fuzzer.fileName)) {
groebert@google.com1bd4c212013-06-19 11:13:56 +0000275 LOGMSG(l_DEBUG, "Error launching child process, killing parent");
robert.swiecki3bb518c2010-10-14 00:48:24 +0000276 kill(getppid(), SIGTERM);
277 exit(EXIT_FAILURE);
278 }
279 }
280
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000281 LOGMSG(l_INFO, "Launched new process, pid: %d, (%d/%d)", fuzzer.pid,
robert.swiecki3bb518c2010-10-14 00:48:24 +0000282 hfuzz->threadsCnt, hfuzz->threadsMax);
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000283
284 arch_reapChild(hfuzz, &fuzzer);
285 unlink(fuzzer.fileName);
286
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000287 while (pthread_mutex_lock(&hfuzz->mutex)) ;
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000288 hfuzz->threadsCnt--;
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000289 while (pthread_mutex_unlock(&hfuzz->mutex)) ;
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000290
291 return NULL;
292}
293
294static void fuzz_runNext(honggfuzz_t * hfuzz)
295{
296 pthread_attr_t attr;
297 pthread_t t;
298
299 pthread_attr_init(&attr);
300 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
301
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000302 while (pthread_mutex_lock(&hfuzz->mutex)) ;
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000303 hfuzz->threadsCnt++;
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000304 while (pthread_mutex_unlock(&hfuzz->mutex)) ;
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000305
306 if (pthread_create(&t, &attr, fuzz_threadCreate, (void *)hfuzz) < 0) {
307 LOGMSG_P(l_FATAL, "Couldn't create a new thread");
308 exit(EXIT_FAILURE);
309 }
310
robert.swiecki3bb518c2010-10-14 00:48:24 +0000311 return;
312}
313
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000314static void fuzz_waitForAll(honggfuzz_t * hfuzz)
315{
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000316 for (;;) {
317 while (pthread_mutex_lock(&hfuzz->mutex)) ;
318 int num = hfuzz->threadsCnt;
319 while (pthread_mutex_unlock(&hfuzz->mutex)) ;
320
321 if (num == 0) {
322 return;
323 }
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000324 usleep(10000);
325 }
326}
327
robert.swiecki3bb518c2010-10-14 00:48:24 +0000328void fuzz_main(honggfuzz_t * hfuzz)
329{
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000330 if (pthread_mutex_init(&hfuzz->mutex, NULL) != 0) {
331 LOGMSG(l_FATAL, "Couldn't initialize mutex");
332 exit(EXIT_FAILURE);
333 }
334
robert.swiecki@gmail.comef829fa2011-06-22 13:51:57 +0000335 if (!arch_prepareParent(hfuzz)) {
336 LOGMSG(l_FATAL, "Couldn't prepare parent for fuzzing");
337 exit(EXIT_FAILURE);
338 }
339
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000340 if (hfuzz->pid) {
341 fuzz_runNext(hfuzz);
342 fuzz_waitForAll(hfuzz);
343 exit(EXIT_SUCCESS);
344 }
345
robert.swiecki3bb518c2010-10-14 00:48:24 +0000346 for (;;) {
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000347 while (pthread_mutex_lock(&hfuzz->mutex)) ;
348 int num = hfuzz->threadsCnt;
349 while (pthread_mutex_unlock(&hfuzz->mutex)) ;
350
351 while (num < hfuzz->threadsMax) {
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000352 /* We just want a limited number of mutations */
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000353 if (hfuzz->mutationsMax && (hfuzz->mutationsCnt >= hfuzz->mutationsMax)) {
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000354 fuzz_waitForAll(hfuzz);
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000355 LOGMSG(l_INFO, "Finished fuzzing %ld times.", hfuzz->mutationsMax);
356 exit(EXIT_SUCCESS);
groebert@google.com8e2f44a2013-03-15 13:54:18 +0000357 }
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000358
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000359 hfuzz->mutationsCnt++;
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000360 fuzz_runNext(hfuzz);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000361 }
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000362 usleep(10000);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000363 }
364}