• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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