blob: 1d288101c544a18e75b7184f6f6cba6ce9ec6f94 [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
9 Copyright 2010 by Google Inc. All Rights Reserved.
10
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
25#include <sys/time.h>
26#include <sys/types.h>
27#include <sys/wait.h>
28#include <sys/mman.h>
29#include <sys/param.h>
30#include <sys/stat.h>
31#include <stddef.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <stdint.h>
35#include <stdio.h>
36#include <string.h>
37#include <fcntl.h>
38#include <time.h>
39#include <signal.h>
40#include <errno.h>
41
42#include "common.h"
43#include "log.h"
44#include "arch.h"
45#include "util.h"
46#include "files.h"
47
48static void fuzz_mangleContent(honggfuzz_t * hfuzz, uint8_t * buf, off_t fileSz)
49{
robert.swieckiba512632011-01-28 11:57:26 +000050 // Just copy the file if "-r 0"
51 if (hfuzz->flipRate == 0.0) {
52 return;
53 }
54
robert.swiecki3bb518c2010-10-14 00:48:24 +000055 uint64_t changesCnt = fileSz * hfuzz->flipRate;
56
57 if (hfuzz->flipMode == 'b') {
58 changesCnt *= 8UL;
59 }
60
61 changesCnt = util_rndGet(1, changesCnt);
62
63 for (uint64_t x = 0; x < changesCnt; x++) {
64 off_t pos = util_rndGet(0, fileSz - 1);
65
66 if (hfuzz->flipMode == 'b') {
67 buf[pos] ^= (1 << util_rndGet(0, 7));
68 } else {
69 buf[pos] = (uint8_t) util_rndGet(0, 255);
70 }
71 }
72}
73
74static void fuzz_getFileName(honggfuzz_t * hfuzz, char *fileName)
75{
robert.swieckiba512632011-01-28 11:57:26 +000076 struct timeval tv;
77 gettimeofday(&tv, NULL);
78
79 snprintf(fileName, PATH_MAX, ".honggfuzz.%d.%lu.%lu.%lu.%s", (int)getpid(),
80 (unsigned long int)tv.tv_sec, (unsigned long int)tv.tv_usec,
robert.swiecki3bb518c2010-10-14 00:48:24 +000081 (unsigned long int)util_rndGet(0, 1 << 30), hfuzz->fileExtn);
82
83 return;
84}
85
86static bool fuzz_prepareFile(honggfuzz_t * hfuzz, char *fileName)
87{
88 int rnd_index = util_rndGet(0, hfuzz->fileCnt - 1);
89 off_t fileSz;
90 int srcfd;
91
92 uint8_t *buf = files_mapFileToRead(hfuzz->files[rnd_index], &fileSz, &srcfd);
robert.swiecki3bb518c2010-10-14 00:48:24 +000093 if (buf == NULL) {
robert.swiecki56b88012010-12-31 14:04:13 +000094 LOGMSG(l_ERROR, "Couldn't open and map '%s' in R/O mode", hfuzz->files[rnd_index]);
robert.swiecki3bb518c2010-10-14 00:48:24 +000095 return false;
96 }
97
98 LOGMSG(l_DEBUG, "Mmaped '%s' in R/O mode, size: %d", hfuzz->files[rnd_index], fileSz);
99
100 int dstfd = open(fileName, O_CREAT | O_EXCL | O_RDWR, 0644);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000101 if (dstfd == -1) {
robert.swiecki3d505e22010-10-14 01:17:17 +0000102 LOGMSG_P(l_ERROR, "Couldn't create a temporary file '%s' in the current directory",
103 fileName);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000104 munmap(buf, fileSz);
105 close(srcfd);
106 return false;
107 }
108
109 fuzz_mangleContent(hfuzz, buf, fileSz);
110
111 if (!files_writeToFd(dstfd, buf, fileSz)) {
112 munmap(buf, fileSz);
113 close(srcfd);
114 close(dstfd);
115 return false;
116 }
117
118 munmap(buf, fileSz);
119 close(srcfd);
120 close(dstfd);
121 return true;
122}
123
124static bool fuzz_prepareFileExternally(honggfuzz_t * hfuzz, char *fileName)
125{
126 int rnd_index = util_rndGet(0, hfuzz->fileCnt - 1);
robert.swiecki3d505e22010-10-14 01:17:17 +0000127 off_t fileSz;
128 int srcfd;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000129
robert.swiecki3d505e22010-10-14 01:17:17 +0000130 uint8_t *buf = files_mapFileToRead(hfuzz->files[rnd_index], &fileSz, &srcfd);
131 if (buf == NULL) {
robert.swiecki56b88012010-12-31 14:04:13 +0000132 LOGMSG(l_ERROR, "Couldn't open and map '%s' in R/O mode", hfuzz->files[rnd_index]);
robert.swiecki3d505e22010-10-14 01:17:17 +0000133 return false;
134 }
135
136 LOGMSG(l_DEBUG, "Mmaped '%s' in R/O mode, size: %d", hfuzz->files[rnd_index], fileSz);
137
138 int dstfd = open(fileName, O_CREAT | O_EXCL | O_RDWR, 0644);
139 if (dstfd == -1) {
140 LOGMSG_P(l_ERROR, "Couldn't create a temporary file '%s' in the current directory",
141 fileName);
142 munmap(buf, fileSz);
143 close(srcfd);
144 return false;
145 }
146
147 bool ret = files_writeToFd(dstfd, buf, fileSz);
148 munmap(buf, fileSz);
149 close(srcfd);
150 close(dstfd);
151
152 if (!ret) {
robert.swiecki3bb518c2010-10-14 00:48:24 +0000153 return false;
154 }
155
156 pid_t pid = fork();
robert.swiecki3bb518c2010-10-14 00:48:24 +0000157 if (pid == -1) {
158 LOGMSG_P(l_ERROR, "Couldn't fork");
159 return false;
160 }
161
162 if (!pid) {
163 /*
164 * child does the external file modifications
165 */
robert.swiecki3d505e22010-10-14 01:17:17 +0000166 execl(hfuzz->externalCommand, hfuzz->externalCommand, fileName, NULL);
groebert@google.comb857deb2010-10-21 19:09:29 +0000167 LOGMSG_P(l_FATAL, "Couldn't execute '%s %s'", hfuzz->externalCommand, fileName);
robert.swiecki3bb518c2010-10-14 00:48:24 +0000168 return false;
169 } else {
170 /*
171 * parent waits until child is done fuzzing the input file
172 */
173
174 int childStatus;
175 pid_t terminatedPid;
176 do {
177 terminatedPid = wait(&childStatus);
178 } while (terminatedPid != pid);
179
180 if (WIFEXITED(childStatus)) {
181 LOGMSG(l_DEBUG, "External command exited with status %d", WEXITSTATUS(childStatus));
182 return true;
183 } else if (WIFSIGNALED(childStatus)) {
robert.swiecki3d505e22010-10-14 01:17:17 +0000184 LOGMSG(l_ERROR, "External command terminated with signal %d", WTERMSIG(childStatus));
robert.swiecki3bb518c2010-10-14 00:48:24 +0000185 return false;
186 }
robert.swiecki3d505e22010-10-14 01:17:17 +0000187 LOGMSG(l_FATAL, "External command terminated abnormally, status: %d", childStatus);
188 return false;
robert.swiecki3bb518c2010-10-14 00:48:24 +0000189 }
190
191 abort(); /* NOTREACHED */
192}
193
194static void fuzz_reapChild(honggfuzz_t * hfuzz)
195{
196 pid_t pid = arch_reapChild(hfuzz);
197
198 if (pid <= 0) {
199 return;
200 }
201
202 int idx = HF_SLOT(hfuzz, pid);
203
204 if (idx == -1) {
205 LOGMSG(l_WARN, "A process of pid %d finished, but it's not our child. It's magic!", pid);
206 return;
207 }
208
209 unlink(hfuzz->fuzzers[idx].fileName);
210 hfuzz->fuzzers[idx].pid = 0;
211 hfuzz->threadsCnt--;
212}
213
214static void fuzz_runNext(honggfuzz_t * hfuzz)
215{
216 int i = HF_SLOT(hfuzz, 0);
217
218 fuzz_getFileName(hfuzz, hfuzz->fuzzers[i].fileName);
219
220 pid_t pid = fork();
221
222 if (pid == -1) {
223 LOGMSG_P(l_FATAL, "Couldn't fork");
224 exit(EXIT_FAILURE);
225 }
226
227 if (!pid) {
228 /*
229 * We've forked, other pid's might have the same rnd seeds now,
230 * reinitialize it
231 */
232 util_rndInit();
233
234 hfuzz->fuzzers[i].pid = getpid();
235
236 if (hfuzz->externalCommand != NULL) {
237 if (!fuzz_prepareFileExternally(hfuzz, hfuzz->fuzzers[i].fileName)) {
238 exit(EXIT_FAILURE);
239 }
240 } else {
241 if (!fuzz_prepareFile(hfuzz, hfuzz->fuzzers[i].fileName)) {
242 exit(EXIT_FAILURE);
243 }
244 }
245
246 /*
247 * Ok, kill the parent
248 */
249 if (!arch_launchChild(hfuzz, hfuzz->fuzzers[i].fileName)) {
250 kill(getppid(), SIGTERM);
251 exit(EXIT_FAILURE);
252 }
253 }
254
255 hfuzz->threadsCnt++;
256 hfuzz->fuzzers[i].pid = pid;
257
258 LOGMSG(l_INFO, "Launched new process, pid: %d, (%d/%d)", pid,
259 hfuzz->threadsCnt, hfuzz->threadsMax);
260 return;
261}
262
263void fuzz_main(honggfuzz_t * hfuzz)
264{
265 for (;;) {
266 while (hfuzz->threadsCnt < hfuzz->threadsMax) {
267 fuzz_runNext(hfuzz);
268 }
269
270 fuzz_reapChild(hfuzz);
271 }
272}