blob: 7d4545f867076751e2285eed5d2dd7c9c3281e74 [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
287 hfuzz->threadsCnt--;
robert.swiecki@gmail.come507cb62015-02-11 17:14:49 +0000288 sem_post(&hfuzz->sem);
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000289
290 return NULL;
291}
292
293static void fuzz_runNext(honggfuzz_t * hfuzz)
294{
295 pthread_attr_t attr;
296 pthread_t t;
297
298 pthread_attr_init(&attr);
299 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
300
robert.swiecki@gmail.com882900b2015-02-11 13:56:22 +0000301 if (pthread_create(&t, &attr, fuzz_threadCreate, (void *)hfuzz) < 0) {
302 LOGMSG_P(l_FATAL, "Couldn't create a new thread");
303 exit(EXIT_FAILURE);
304 }
305
robert.swiecki3bb518c2010-10-14 00:48:24 +0000306 return;
307}
308
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000309static void fuzz_waitForAll(honggfuzz_t * hfuzz)
310{
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000311 for (;;) {
robert.swiecki@gmail.come507cb62015-02-11 17:14:49 +0000312 if (hfuzz->threadsCnt == 0) {
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000313 return;
314 }
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000315 usleep(10000);
316 }
317}
318
robert.swiecki3bb518c2010-10-14 00:48:24 +0000319void fuzz_main(honggfuzz_t * hfuzz)
320{
robert.swiecki@gmail.come507cb62015-02-11 17:14:49 +0000321 if (sem_init(&hfuzz->sem, 1, hfuzz->threadsMax)) {
322 LOGMSG_P(l_FATAL, "sem_init() failed");
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000323 exit(EXIT_FAILURE);
robert.swiecki@gmail.come507cb62015-02-11 17:14:49 +0000324 exit(EXIT_SUCCESS);
robert.swiecki@gmail.come7635392015-02-11 16:17:49 +0000325 }
326
robert.swiecki@gmail.comef829fa2011-06-22 13:51:57 +0000327 if (!arch_prepareParent(hfuzz)) {
328 LOGMSG(l_FATAL, "Couldn't prepare parent for fuzzing");
329 exit(EXIT_FAILURE);
330 }
331
robert.swiecki@gmail.com65cfa1c2015-02-11 15:59:28 +0000332 if (hfuzz->pid) {
333 fuzz_runNext(hfuzz);
334 fuzz_waitForAll(hfuzz);
335 exit(EXIT_SUCCESS);
336 }
337
robert.swiecki3bb518c2010-10-14 00:48:24 +0000338 for (;;) {
robert.swiecki@gmail.come507cb62015-02-11 17:14:49 +0000339 if (sem_wait(&hfuzz->sem) == -1) {
340 LOGMSG_P(l_FATAL, "sem_wait() failed");
341 exit(EXIT_FAILURE);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000342 }
robert.swiecki@gmail.come507cb62015-02-11 17:14:49 +0000343
344 if (hfuzz->mutationsMax && (hfuzz->mutationsCnt >= hfuzz->mutationsMax)) {
345 fuzz_waitForAll(hfuzz);
346 LOGMSG(l_INFO, "Finished fuzzing %ld times.", hfuzz->mutationsMax);
347 exit(EXIT_SUCCESS);
348 }
349
350 hfuzz->mutationsCnt++;
351 hfuzz->threadsCnt++;
352 fuzz_runNext(hfuzz);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000353 }
354}