1 /*
2 *
3 * honggfuzz - the main file
4 * -----------------------------------------
5 *
6 * Authors: Robert Swiecki <swiecki@google.com>
7 * Felix Gröbert <groebert@google.com>
8 *
9 * Copyright 2010-2019 by Google Inc. All Rights Reserved.
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may
12 * not use this file except in compliance with the License. You may obtain
13 * 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
20 * implied. See the License for the specific language governing
21 * permissions and limitations under the License.
22 *
23 */
24
25 #include <errno.h>
26 #include <getopt.h>
27 #include <inttypes.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/mman.h>
33 #include <sys/resource.h>
34 #include <sys/time.h>
35 #include <time.h>
36 #include <unistd.h>
37
38 #include "cmdline.h"
39 #include "display.h"
40 #include "fuzz.h"
41 #include "input.h"
42 #include "libhfcommon/common.h"
43 #include "libhfcommon/files.h"
44 #include "libhfcommon/log.h"
45 #include "libhfcommon/util.h"
46 #include "socketfuzzer.h"
47 #include "subproc.h"
48
49 static int sigReceived = 0;
50
51 /*
52 * CygWin/MinGW incorrectly copies stack during fork(), so we need to keep some
53 * structures in the data section
54 */
55 honggfuzz_t hfuzz;
56
exitWithMsg(const char * msg,int exit_code)57 static void exitWithMsg(const char* msg, int exit_code) {
58 HF_ATTR_UNUSED ssize_t sz = write(STDERR_FILENO, msg, strlen(msg));
59 for (;;) {
60 exit(exit_code);
61 _exit(exit_code);
62 abort();
63 __builtin_trap();
64 }
65 }
66
67 static bool showDisplay = true;
sigHandler(int sig)68 static void sigHandler(int sig) {
69 /* We should not terminate upon SIGALRM delivery */
70 if (sig == SIGALRM) {
71 if (fuzz_shouldTerminate()) {
72 exitWithMsg("Terminating forcefully\n", EXIT_FAILURE);
73 }
74 showDisplay = true;
75 return;
76 }
77 /* Do nothing with pings from the main thread */
78 if (sig == SIGUSR1) {
79 return;
80 }
81 /* It's handled in the signal thread */
82 if (sig == SIGCHLD) {
83 return;
84 }
85
86 if (ATOMIC_GET(sigReceived) != 0) {
87 exitWithMsg("Repeated termination signal caugth\n", EXIT_FAILURE);
88 }
89
90 ATOMIC_SET(sigReceived, sig);
91 }
92
setupRLimits(void)93 static void setupRLimits(void) {
94 struct rlimit rlim;
95 if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) {
96 PLOG_W("getrlimit(RLIMIT_NOFILE)");
97 return;
98 }
99 if (rlim.rlim_cur >= 1024) {
100 return;
101 }
102 if (rlim.rlim_max < 1024) {
103 LOG_E("RLIMIT_NOFILE max limit < 1024 (%zu). Expect troubles!", (size_t)rlim.rlim_max);
104 return;
105 }
106 rlim.rlim_cur = MIN(1024, rlim.rlim_max); // we don't need more
107 if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
108 PLOG_E("Couldn't setrlimit(RLIMIT_NOFILE, cur=%zu/max=%zu)", (size_t)rlim.rlim_cur,
109 (size_t)rlim.rlim_max);
110 }
111 }
112
setupMainThreadTimer(void)113 static void setupMainThreadTimer(void) {
114 const struct itimerval it = {
115 .it_value =
116 {
117 .tv_sec = 1,
118 .tv_usec = 0,
119 },
120 .it_interval =
121 {
122 .tv_sec = 0,
123 .tv_usec = 1000ULL * 200ULL,
124 },
125 };
126 if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
127 PLOG_F("setitimer(ITIMER_REAL)");
128 }
129 }
130
setupSignalsPreThreads(void)131 static void setupSignalsPreThreads(void) {
132 /* Block signals which should be handled or blocked in the main thread */
133 sigset_t ss;
134 sigemptyset(&ss);
135 sigaddset(&ss, SIGTERM);
136 sigaddset(&ss, SIGINT);
137 sigaddset(&ss, SIGQUIT);
138 sigaddset(&ss, SIGALRM);
139 sigaddset(&ss, SIGPIPE);
140 /* Linux/arch uses it to discover events from persistent fuzzing processes */
141 sigaddset(&ss, SIGIO);
142 /* Let the signal thread catch SIGCHLD */
143 sigaddset(&ss, SIGCHLD);
144 /* This is checked for via sigwaitinfo/sigtimedwait */
145 sigaddset(&ss, SIGUSR1);
146 if (sigprocmask(SIG_SETMASK, &ss, NULL) != 0) {
147 PLOG_F("pthread_sigmask(SIG_SETMASK)");
148 }
149
150 struct sigaction sa = {
151 .sa_handler = sigHandler,
152 .sa_flags = 0,
153 };
154 sigemptyset(&sa.sa_mask);
155 if (sigaction(SIGTERM, &sa, NULL) == -1) {
156 PLOG_F("sigaction(SIGTERM) failed");
157 }
158 if (sigaction(SIGINT, &sa, NULL) == -1) {
159 PLOG_F("sigaction(SIGINT) failed");
160 }
161 if (sigaction(SIGQUIT, &sa, NULL) == -1) {
162 PLOG_F("sigaction(SIGQUIT) failed");
163 }
164 if (sigaction(SIGALRM, &sa, NULL) == -1) {
165 PLOG_F("sigaction(SIGQUIT) failed");
166 }
167 if (sigaction(SIGUSR1, &sa, NULL) == -1) {
168 PLOG_F("sigaction(SIGUSR1) failed");
169 }
170 if (sigaction(SIGCHLD, &sa, NULL) == -1) {
171 PLOG_F("sigaction(SIGCHLD) failed");
172 }
173 }
174
setupSignalsMainThread(void)175 static void setupSignalsMainThread(void) {
176 /* Unblock signals which should be handled by the main thread */
177 sigset_t ss;
178 sigemptyset(&ss);
179 sigaddset(&ss, SIGTERM);
180 sigaddset(&ss, SIGINT);
181 sigaddset(&ss, SIGQUIT);
182 sigaddset(&ss, SIGALRM);
183 if (sigprocmask(SIG_UNBLOCK, &ss, NULL) != 0) {
184 PLOG_F("pthread_sigmask(SIG_UNBLOCK)");
185 }
186 }
187
printSummary(honggfuzz_t * hfuzz)188 static void printSummary(honggfuzz_t* hfuzz) {
189 uint64_t exec_per_sec = 0;
190 uint64_t elapsed_sec = time(NULL) - hfuzz->timing.timeStart;
191 if (elapsed_sec) {
192 exec_per_sec = hfuzz->cnts.mutationsCnt / elapsed_sec;
193 }
194 LOG_I("Summary iterations:%zu time:%" PRIu64 " speed:%" PRIu64, hfuzz->cnts.mutationsCnt,
195 elapsed_sec, exec_per_sec);
196 }
197
pingThreads(honggfuzz_t * hfuzz)198 static void pingThreads(honggfuzz_t* hfuzz) {
199 for (size_t i = 0; i < hfuzz->threads.threadsMax; i++) {
200 if (pthread_kill(hfuzz->threads.threads[i], SIGUSR1) != 0 && errno != EINTR) {
201 PLOG_W("pthread_kill(thread=%zu, SIGUSR1)", i);
202 }
203 }
204 }
205
signalThread(void * arg)206 static void* signalThread(void* arg) {
207 honggfuzz_t* hfuzz = (honggfuzz_t*)arg;
208
209 sigset_t ss;
210 sigemptyset(&ss);
211 sigaddset(&ss, SIGCHLD);
212 if (pthread_sigmask(SIG_UNBLOCK, &ss, NULL) != 0) {
213 PLOG_F("Couldn't unblock SIGCHLD in the signal thread");
214 }
215
216 for (;;) {
217 int sig;
218 if (sigwait(&ss, &sig) != 0 && errno != EINTR) {
219 PLOG_F("sigwait(SIGCHLD)");
220 }
221 if (fuzz_isTerminating()) {
222 break;
223 }
224 if (sig == SIGCHLD) {
225 pingThreads(hfuzz);
226 }
227 }
228
229 return NULL;
230 }
231
main(int argc,char ** argv)232 int main(int argc, char** argv) {
233 /*
234 * Work around CygWin/MinGW
235 */
236 char** myargs = (char**)util_Malloc(sizeof(char*) * (argc + 1));
237 defer {
238 free(myargs);
239 };
240
241 int i;
242 for (i = 0U; i < argc; i++) {
243 myargs[i] = argv[i];
244 }
245 myargs[i] = NULL;
246
247 if (cmdlineParse(argc, myargs, &hfuzz) == false) {
248 LOG_F("Parsing of the cmd-line arguments failed");
249 }
250
251 if (hfuzz.display.useScreen) {
252 display_init();
253 }
254
255 if (hfuzz.socketFuzzer.enabled) {
256 LOG_I("No input file corpus loaded, the external socket_fuzzer is responsible for "
257 "creating the fuzz data");
258 setupSocketFuzzer(&hfuzz);
259 } else if (!input_init(&hfuzz)) {
260 LOG_F("Couldn't load input corpus");
261 exit(EXIT_FAILURE);
262 }
263
264 if (hfuzz.mutate.dictionaryFile && (input_parseDictionary(&hfuzz) == false)) {
265 LOG_F("Couldn't parse dictionary file ('%s')", hfuzz.mutate.dictionaryFile);
266 }
267
268 if (hfuzz.feedback.blacklistFile && (input_parseBlacklist(&hfuzz) == false)) {
269 LOG_F("Couldn't parse stackhash blacklist file ('%s')", hfuzz.feedback.blacklistFile);
270 }
271 #define hfuzzl hfuzz.linux
272 if (hfuzzl.symsBlFile &&
273 ((hfuzzl.symsBlCnt = files_parseSymbolFilter(hfuzzl.symsBlFile, &hfuzzl.symsBl)) == 0)) {
274 LOG_F("Couldn't parse symbols blacklist file ('%s')", hfuzzl.symsBlFile);
275 }
276
277 if (hfuzzl.symsWlFile &&
278 ((hfuzzl.symsWlCnt = files_parseSymbolFilter(hfuzzl.symsWlFile, &hfuzzl.symsWl)) == 0)) {
279 LOG_F("Couldn't parse symbols whitelist file ('%s')", hfuzzl.symsWlFile);
280 }
281
282 if (hfuzz.feedback.dynFileMethod != _HF_DYNFILE_NONE) {
283 if (!(hfuzz.feedback.feedbackMap = files_mapSharedMem(
284 sizeof(feedback_t), &hfuzz.feedback.bbFd, "hfuzz-feedback", hfuzz.io.workDir))) {
285 LOG_F("files_mapSharedMem(sz=%zu, dir='%s') failed", sizeof(feedback_t),
286 hfuzz.io.workDir);
287 }
288 }
289
290 setupRLimits();
291 setupSignalsPreThreads();
292 fuzz_threadsStart(&hfuzz);
293
294 pthread_t sigthread;
295 if (!subproc_runThread(&hfuzz, &sigthread, signalThread)) {
296 LOG_F("Couldn't start the signal thread");
297 }
298
299 setupSignalsMainThread();
300 setupMainThreadTimer();
301
302 for (;;) {
303 if (hfuzz.display.useScreen && showDisplay) {
304 display_display(&hfuzz);
305 showDisplay = false;
306 }
307 if (ATOMIC_GET(sigReceived) > 0) {
308 LOG_I("Signal %d (%s) received, terminating", ATOMIC_GET(sigReceived),
309 strsignal(ATOMIC_GET(sigReceived)));
310 break;
311 }
312 if (ATOMIC_GET(hfuzz.threads.threadsFinished) >= hfuzz.threads.threadsMax) {
313 break;
314 }
315 if (hfuzz.timing.runEndTime > 0 && (time(NULL) > hfuzz.timing.runEndTime)) {
316 LOG_I("Maximum run time reached, terminating");
317 break;
318 }
319 pingThreads(&hfuzz);
320 pause();
321 }
322
323 fuzz_setTerminating();
324
325 for (;;) {
326 if (ATOMIC_GET(hfuzz.threads.threadsFinished) >= hfuzz.threads.threadsMax) {
327 break;
328 }
329 pingThreads(&hfuzz);
330 usleep(50000); /* 50ms */
331 }
332
333 /* Clean-up global buffers */
334 if (hfuzz.feedback.blacklist) {
335 free(hfuzz.feedback.blacklist);
336 }
337 #if defined(_HF_ARCH_LINUX)
338 if (hfuzz.linux.symsBl) {
339 free(hfuzz.linux.symsBl);
340 }
341 if (hfuzz.linux.symsWl) {
342 free(hfuzz.linux.symsWl);
343 }
344 #elif defined(_HF_ARCH_NETBSD)
345 if (hfuzz.netbsd.symsBl) {
346 free(hfuzz.netbsd.symsBl);
347 }
348 if (hfuzz.netbsd.symsWl) {
349 free(hfuzz.netbsd.symsWl);
350 }
351 #endif
352 if (hfuzz.socketFuzzer.enabled) {
353 cleanupSocketFuzzer();
354 }
355
356 printSummary(&hfuzz);
357
358 return EXIT_SUCCESS;
359 }
360