1 /* 2 * 3 * honggfuzz - the main file 4 * ----------------------------------------- 5 * 6 * Authors: Robert Swiecki <swiecki (at) google.com> 7 * Felix Grbert <groebert (at) 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 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; 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 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 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 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 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 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 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 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 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