Home | History | Annotate | Download | only in honggfuzz
      1 /*
      2 
      3    honggfuzz - cmdline parsing
      4 
      5    -----------------------------------------
      6 
      7    Copyright 2014 Google Inc. All Rights Reserved.
      8 
      9    Licensed under the Apache License, Version 2.0 (the "License");
     10    you may not use this file except in compliance with the License.
     11    You may obtain a copy of the License at
     12 
     13      http://www.apache.org/licenses/LICENSE-2.0
     14 
     15    Unless required by applicable law or agreed to in writing, software
     16    distributed under the License is distributed on an "AS IS" BASIS,
     17    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     18    See the License for the specific language governing permissions and
     19    limitations under the License.
     20 
     21 */
     22 
     23 #include "cmdline.h"
     24 
     25 #include <ctype.h>
     26 #include <errno.h>
     27 #include <getopt.h>
     28 #include <inttypes.h>
     29 #include <limits.h>
     30 #if defined(_HF_ARCH_LINUX)
     31 #include <sched.h>
     32 #endif /* defined(_HF_ARCH_LINUX) */
     33 #include <signal.h>
     34 #include <stdint.h>
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 #include <sys/mman.h>
     39 #include <sys/queue.h>
     40 #include <sys/stat.h>
     41 #include <sys/types.h>
     42 #include <unistd.h>
     43 
     44 #include "display.h"
     45 #include "libhfcommon/common.h"
     46 #include "libhfcommon/files.h"
     47 #include "libhfcommon/log.h"
     48 #include "libhfcommon/util.h"
     49 
     50 struct custom_option {
     51     struct option opt;
     52     const char* descr;
     53 };
     54 
     55 static bool checkFor_FILE_PLACEHOLDER(const char* const* args) {
     56     for (int x = 0; args[x]; x++) {
     57         if (strstr(args[x], _HF_FILE_PLACEHOLDER)) return true;
     58     }
     59     return false;
     60 }
     61 
     62 static bool cmdlineCheckBinaryType(honggfuzz_t* hfuzz) {
     63     int fd;
     64     off_t fileSz;
     65     uint8_t* map = files_mapFile(hfuzz->exe.cmdline[0], &fileSz, &fd, /* isWriteable= */ false);
     66     if (!map) {
     67         /* It's not a critical error */
     68         return true;
     69     }
     70     defer {
     71         if (munmap(map, fileSz) == -1) {
     72             PLOG_W("munmap(%p, %zu)", map, (size_t)fileSz);
     73         }
     74         close(fd);
     75     };
     76 
     77     if (memmem(map, fileSz, _HF_PERSISTENT_SIG, strlen(_HF_PERSISTENT_SIG))) {
     78         LOG_I("Persistent signature found in '%s'. Enabling persistent fuzzing mode",
     79             hfuzz->exe.cmdline[0]);
     80         hfuzz->exe.persistent = true;
     81     }
     82     if (memmem(map, fileSz, _HF_NETDRIVER_SIG, strlen(_HF_NETDRIVER_SIG))) {
     83         LOG_I("NetDriver signature found '%s'", hfuzz->exe.cmdline[0]);
     84         hfuzz->exe.netDriver = true;
     85     }
     86     return true;
     87 }
     88 
     89 static const char* cmdlineYesNo(bool yes) {
     90     return (yes ? "true" : "false");
     91 }
     92 
     93 static void cmdlineHelp(const char* pname, struct custom_option* opts) {
     94     LOG_HELP_BOLD("Usage: %s [options] -- path_to_command [args]", pname);
     95     LOG_HELP_BOLD("Options:");
     96     for (int i = 0; opts[i].opt.name; i++) {
     97         if (isprint(opts[i].opt.val) && opts[i].opt.val < 0x80) {
     98             LOG_HELP_BOLD(" --%s%s%c %s", opts[i].opt.name, "|-", opts[i].opt.val,
     99                 opts[i].opt.has_arg == required_argument ? "VALUE" : "");
    100         } else {
    101             LOG_HELP_BOLD(" --%s %s", opts[i].opt.name,
    102                 opts[i].opt.has_arg == required_argument ? "VALUE" : "");
    103         }
    104         LOG_HELP("\t%s", opts[i].descr);
    105     }
    106     LOG_HELP_BOLD("\nExamples:");
    107     LOG_HELP(
    108         " Run the binary over a mutated file chosen from the directory. Disable fuzzing feedback "
    109         "(static mode):");
    110     LOG_HELP_BOLD("  " PROG_NAME " -f input_dir -x -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
    111     LOG_HELP(" As above, provide input over STDIN:");
    112     LOG_HELP_BOLD("  " PROG_NAME " -f input_dir -x -s -- /usr/bin/djpeg");
    113     LOG_HELP(" Use compile-time instrumentation (-fsanitize-coverage=trace-pc-guard,...):");
    114     LOG_HELP_BOLD("  " PROG_NAME " -f input_dir -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
    115     LOG_HELP(" Use persistent mode w/o instrumentation:");
    116     LOG_HELP_BOLD("  " PROG_NAME " -f input_dir -P -x -- /usr/bin/djpeg_persistent_mode");
    117     LOG_HELP(" Use persistent mode and compile-time (-fsanitize-coverage=trace-pc-guard,...) "
    118              "instrumentation:");
    119     LOG_HELP_BOLD("  " PROG_NAME " -f input_dir -P -- /usr/bin/djpeg_persistent_mode");
    120 #if defined(_HF_ARCH_LINUX)
    121     LOG_HELP(
    122         " Run the binary with dynamically generate inputs, maximize total no. of instructions:");
    123     LOG_HELP_BOLD("  " PROG_NAME " --linux_perf_instr -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
    124     LOG_HELP(" As above, maximize total no. of branches:");
    125     LOG_HELP_BOLD("  " PROG_NAME " --linux_perf_branch -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
    126     LOG_HELP(" As above, maximize unique branches (edges) via Intel BTS:");
    127     LOG_HELP_BOLD("  " PROG_NAME " --linux_perf_bts_edge -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
    128     LOG_HELP(
    129         " As above, maximize unique code blocks via Intel Processor Trace (requires libipt.so):");
    130     LOG_HELP_BOLD("  " PROG_NAME " --linux_perf_ipt_block -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
    131 #endif /* defined(_HF_ARCH_LINUX) */
    132 }
    133 
    134 static void cmdlineUsage(const char* pname, struct custom_option* opts) {
    135     cmdlineHelp(pname, opts);
    136     exit(0);
    137 }
    138 
    139 bool cmdlineAddEnv(honggfuzz_t* hfuzz, char* env) {
    140     size_t enveqlen = strlen(env);
    141     const char* eqpos = strchr(env, '=');
    142     if (eqpos) {
    143         enveqlen = (uintptr_t)eqpos - (uintptr_t)env + 1;
    144     }
    145 
    146     for (size_t i = 0; i < ARRAYSIZE(hfuzz->exe.envs); i++) {
    147         if (hfuzz->exe.envs[i] == NULL) {
    148             LOG_D("Adding envar '%s'", env);
    149             hfuzz->exe.envs[i] = env;
    150             return true;
    151         }
    152         if (strncmp(hfuzz->exe.envs[i], env, enveqlen) == 0) {
    153             LOG_W("Replacing envar '%s' with '%s'", hfuzz->exe.envs[i], env);
    154             hfuzz->exe.envs[i] = env;
    155             return true;
    156         }
    157     }
    158     LOG_E("No more space for new envars (max.%zu)", ARRAYSIZE(hfuzz->exe.envs));
    159     return false;
    160 }
    161 
    162 rlim_t cmdlineParseRLimit(int res, const char* optarg, unsigned long mul) {
    163     struct rlimit cur;
    164     if (getrlimit(res, &cur) == -1) {
    165         PLOG_F("getrlimit(%d)", res);
    166     }
    167     if (strcasecmp(optarg, "max") == 0) {
    168         return cur.rlim_max;
    169     }
    170     if (strcasecmp(optarg, "def") == 0) {
    171         return cur.rlim_cur;
    172     }
    173     if (util_isANumber(optarg) == false) {
    174         LOG_F("RLIMIT %d needs a numeric or 'max'/'def' value ('%s' provided)", res, optarg);
    175     }
    176     rlim_t val = strtoul(optarg, NULL, 0) * mul;
    177     if ((unsigned long)val == ULONG_MAX && errno != 0) {
    178         PLOG_F("strtoul('%s', 0)", optarg);
    179     }
    180     return val;
    181 }
    182 
    183 static bool cmdlineVerify(honggfuzz_t* hfuzz) {
    184     if (!cmdlineCheckBinaryType(hfuzz)) {
    185         LOG_E("Couldn't test binary for signatures");
    186         return false;
    187     }
    188 
    189     if (!hfuzz->exe.fuzzStdin && !hfuzz->exe.persistent &&
    190         !checkFor_FILE_PLACEHOLDER(hfuzz->exe.cmdline)) {
    191         LOG_E("You must specify '" _HF_FILE_PLACEHOLDER
    192               "' if the -s (stdin fuzzing) or --persistent options are not set");
    193         return false;
    194     }
    195 
    196     if (hfuzz->exe.fuzzStdin && hfuzz->exe.persistent) {
    197         LOG_E(
    198             "Stdin fuzzing (-s) and persistent fuzzing (-P) cannot be specified at the same time");
    199         return false;
    200     }
    201 
    202     if (hfuzz->threads.threadsMax >= _HF_THREAD_MAX) {
    203         LOG_E("Too many fuzzing threads specified %zu (>= _HF_THREAD_MAX (%u))",
    204             hfuzz->threads.threadsMax, _HF_THREAD_MAX);
    205         return false;
    206     }
    207 
    208     if (strchr(hfuzz->io.fileExtn, '/')) {
    209         LOG_E("The file extension contains the '/' character: '%s'", hfuzz->io.fileExtn);
    210         return false;
    211     }
    212 
    213     if (hfuzz->io.workDir == NULL) {
    214         hfuzz->io.workDir = ".";
    215     }
    216     if (mkdir(hfuzz->io.workDir, 0700) == -1 && errno != EEXIST) {
    217         PLOG_E("Couldn't create the workspace directory '%s'", hfuzz->io.workDir);
    218         return false;
    219     }
    220     if (hfuzz->io.crashDir == NULL) {
    221         hfuzz->io.crashDir = hfuzz->io.workDir;
    222     }
    223     if (mkdir(hfuzz->io.crashDir, 0700) && errno != EEXIST) {
    224         PLOG_E("Couldn't create the crash directory '%s'", hfuzz->io.crashDir);
    225         return false;
    226     }
    227 
    228     if (hfuzz->mutate.mutationsPerRun == 0U && hfuzz->cfg.useVerifier) {
    229         LOG_I("Verifier enabled with mutationsPerRun == 0, activating the dry run mode");
    230     }
    231 
    232     if (hfuzz->mutate.maxFileSz > _HF_INPUT_MAX_SIZE) {
    233         LOG_E("Maximum file size '%zu' bigger than the maximum size '%zu'", hfuzz->mutate.maxFileSz,
    234             (size_t)_HF_INPUT_MAX_SIZE);
    235         return false;
    236     }
    237 
    238     return true;
    239 }
    240 
    241 bool cmdlineParse(int argc, char* argv[], honggfuzz_t* hfuzz) {
    242     *hfuzz = (honggfuzz_t){
    243         .threads =
    244             {
    245                 .threadsFinished = 0,
    246                 .threadsMax =
    247                     (sysconf(_SC_NPROCESSORS_ONLN) <= 1) ? 1 : sysconf(_SC_NPROCESSORS_ONLN) / 2,
    248                 .threadsActiveCnt = 0,
    249                 .mainThread = pthread_self(),
    250                 .mainPid = getpid(),
    251             },
    252         .io =
    253             {
    254                 .inputDir = NULL,
    255                 .inputDirPtr = NULL,
    256                 .fileCnt = 0,
    257                 .fileCntDone = false,
    258                 .fileExtn = "fuzz",
    259                 .workDir = NULL,
    260                 .crashDir = NULL,
    261                 .covDirAll = NULL,
    262                 .covDirNew = NULL,
    263                 .saveUnique = true,
    264                 .dynfileqCnt = 0U,
    265                 .dynfileq_mutex = PTHREAD_RWLOCK_INITIALIZER,
    266             },
    267         .exe =
    268             {
    269                 .argc = 0,
    270                 .cmdline = NULL,
    271                 .nullifyStdio = true,
    272                 .fuzzStdin = false,
    273                 .externalCommand = NULL,
    274                 .postExternalCommand = NULL,
    275                 .persistent = false,
    276                 .netDriver = false,
    277                 .asLimit = 0U,
    278                 .rssLimit = 0U,
    279                 .dataLimit = 0U,
    280                 .clearEnv = false,
    281                 .envs = {},
    282             },
    283         .timing =
    284             {
    285                 .timeStart = time(NULL),
    286                 .runEndTime = 0,
    287                 .tmOut = 10,
    288                 .tmoutVTALRM = false,
    289                 .lastCovUpdate = time(NULL),
    290             },
    291         .mutate =
    292             {
    293                 .mutationsMax = 0,
    294                 .dictionaryFile = NULL,
    295                 .dictionaryCnt = 0,
    296                 .mutationsPerRun = 6U,
    297                 .maxFileSz = 0UL,
    298             },
    299         .display =
    300             {
    301                 .useScreen = true,
    302                 .lastDisplayMillis = util_timeNowMillis(),
    303                 .cmdline_txt[0] = '\0',
    304             },
    305         .cfg =
    306             {
    307                 .useVerifier = false,
    308                 .exitUponCrash = false,
    309                 .report_mutex = PTHREAD_MUTEX_INITIALIZER,
    310                 .reportFile = NULL,
    311                 .dynFileIterExpire = 0,
    312 #if defined(__ANDROID__)
    313                 .monitorSIGABRT = false,
    314 #else
    315                 .monitorSIGABRT = true,
    316 #endif
    317                 .only_printable = false,
    318             },
    319         .sanitizer =
    320             {
    321                 .enable = false,
    322             },
    323         .feedback =
    324             {
    325                 .feedbackMap = NULL,
    326                 .feedback_mutex = PTHREAD_MUTEX_INITIALIZER,
    327                 .bbFd = -1,
    328                 .blacklistFile = NULL,
    329                 .blacklist = NULL,
    330                 .blacklistCnt = 0,
    331                 .skipFeedbackOnTimeout = false,
    332                 .dynFileMethod = _HF_DYNFILE_SOFT,
    333                 .state = _HF_STATE_UNSET,
    334             },
    335         .cnts =
    336             {
    337                 .mutationsCnt = 0,
    338                 .crashesCnt = 0,
    339                 .uniqueCrashesCnt = 0,
    340                 .verifiedCrashesCnt = 0,
    341                 .blCrashesCnt = 0,
    342                 .timeoutedCnt = 0,
    343             },
    344         .socketFuzzer =
    345             {
    346                 .enabled = false,
    347                 .serverSocket = -1,
    348                 .clientSocket = -1,
    349             },
    350 
    351         /* Linux code */
    352         .linux =
    353             {
    354                 .exeFd = -1,
    355                 .hwCnts =
    356                     {
    357                         .cpuInstrCnt = 0ULL,
    358                         .cpuBranchCnt = 0ULL,
    359                         .bbCnt = 0ULL,
    360                         .newBBCnt = 0ULL,
    361                         .softCntPc = 0ULL,
    362                         .softCntCmp = 0ULL,
    363                     },
    364                 .dynamicCutOffAddr = ~(0ULL),
    365                 .disableRandomization = true,
    366                 .ignoreAddr = NULL,
    367                 .numMajorFrames = 7,
    368                 .symsBlFile = NULL,
    369                 .symsBlCnt = 0,
    370                 .symsBl = NULL,
    371                 .symsWlFile = NULL,
    372                 .symsWlCnt = 0,
    373                 .symsWl = NULL,
    374                 .cloneFlags = 0,
    375                 .kernelOnly = false,
    376                 .useClone = true,
    377             },
    378         /* NetBSD code */
    379         .netbsd =
    380             {
    381                 .ignoreAddr = NULL,
    382                 .numMajorFrames = 7,
    383                 .symsBlFile = NULL,
    384                 .symsBlCnt = 0,
    385                 .symsBl = NULL,
    386                 .symsWlFile = NULL,
    387                 .symsWlCnt = 0,
    388                 .symsWl = NULL,
    389             },
    390     };
    391 
    392     TAILQ_INIT(&hfuzz->io.dynfileq);
    393     TAILQ_INIT(&hfuzz->mutate.dictq);
    394 
    395     // clang-format off
    396     struct custom_option custom_opts[] = {
    397         { { "help", no_argument, NULL, 'h' }, "Help plz.." },
    398         { { "input", required_argument, NULL, 'f' }, "Path to a directory containing initial file corpus" },
    399         { { "persistent", no_argument, NULL, 'P' }, "Enable persistent fuzzing (use hfuzz_cc/hfuzz-clang to compile code). This will be auto-detected!!!" },
    400         { { "instrument", no_argument, NULL, 'z' }, "*DEFAULT-MODE-BY-DEFAULT* Enable compile-time instrumentation (use hfuzz_cc/hfuzz-clang to compile code)" },
    401         { { "noinst", no_argument, NULL, 'x' }, "Static mode only, disable any instrumentation (hw/sw) feedback" },
    402         { { "keep_output", no_argument, NULL, 'Q' }, "Don't close children's stdin, stdout, stderr; can be noisy" },
    403         { { "timeout", required_argument, NULL, 't' }, "Timeout in seconds (default: 10)" },
    404         { { "threads", required_argument, NULL, 'n' }, "Number of concurrent fuzzing threads (default: number of CPUs / 2)" },
    405         { { "stdin_input", no_argument, NULL, 's' }, "Provide fuzzing input on STDIN, instead of ___FILE___" },
    406         { { "mutations_per_run", required_argument, NULL, 'r' }, "Maximal number of mutations per one run (default: 6)" },
    407         { { "logfile", required_argument, NULL, 'l' }, "Log file" },
    408         { { "verbose", no_argument, NULL, 'v' }, "Disable ANSI console; use simple log output" },
    409         { { "verifier", no_argument, NULL, 'V' }, "Enable crashes verifier" },
    410         { { "debug", no_argument, NULL, 'd' }, "Show debug messages (level >= 4)" },
    411         { { "quiet", no_argument, NULL, 'q' }, "Show only warnings and more serious messages (level <= 1)" },
    412         { { "extension", required_argument, NULL, 'e' }, "Input file extension (e.g. 'swf'), (default: 'fuzz')" },
    413         { { "workspace", required_argument, NULL, 'W' }, "Workspace directory to save crashes & runtime files (default: '.')" },
    414         { { "crashdir", required_argument, NULL, 0x600 }, "Directory where crashes are saved to (default: workspace directory)" },
    415         { { "covdir_all", required_argument, NULL, 0x601 }, "Coverage is written to a separate directory (default: input directory)" },
    416         { { "covdir_new", required_argument, NULL, 0x602 }, "New coverage (beyond the dry-run fuzzing phase) is written to this separate directory" },
    417         { { "dict", required_argument, NULL, 'w' }, "Dictionary file. Format:http://llvm.org/docs/LibFuzzer.html#dictionaries" },
    418         { { "stackhash_bl", required_argument, NULL, 'B' }, "Stackhashes blacklist file (one entry per line)" },
    419         { { "mutate_cmd", required_argument, NULL, 'c' }, "External command producing fuzz files (instead of internal mutators)" },
    420         { { "pprocess_cmd", required_argument, NULL, 0x104 }, "External command postprocessing files produced by internal mutators" },
    421         { { "run_time", required_argument, NULL, 0x109 }, "Number of seconds this fuzzing session will last (default: 0 [no limit])" },
    422         { { "iterations", required_argument, NULL, 'N' }, "Number of fuzzing iterations (default: 0 [no limit])" },
    423         { { "rlimit_as", required_argument, NULL, 0x100 }, "Per process RLIMIT_AS in MiB (default: 0 [no limit])" },
    424         { { "rlimit_rss", required_argument, NULL, 0x101 }, "Per process RLIMIT_RSS in MiB (default: 0 [no limit]). It will also set *SAN's soft_rss_limit_mb if used" },
    425         { { "rlimit_data", required_argument, NULL, 0x102 }, "Per process RLIMIT_DATA in MiB (default: 0 [no limit])" },
    426         { { "rlimit_core", required_argument, NULL, 0x103 }, "Per process RLIMIT_CORE in MiB (default: 0 [no cores are produced])" },
    427         { { "report", required_argument, NULL, 'R' }, "Write report to this file (default: '<workdir>/" _HF_REPORT_FILE "')" },
    428         { { "max_file_size", required_argument, NULL, 'F' }, "Maximal size of files processed by the fuzzer in bytes (default: 1048576)" },
    429         { { "clear_env", no_argument, NULL, 0x108 }, "Clear all environment variables before executing the binary" },
    430         { { "env", required_argument, NULL, 'E' }, "Pass this environment variable, can be used multiple times" },
    431         { { "save_all", no_argument, NULL, 'u' }, "Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames" },
    432         { { "tmout_sigvtalrm", no_argument, NULL, 'T' }, "Use SIGVTALRM to kill timeouting processes (default: use SIGKILL)" },
    433         { { "sanitizers", no_argument, NULL, 'S' }, "Enable sanitizers settings (default: false)" },
    434         { { "monitor_sigabrt", required_argument, NULL, 0x105 }, "Monitor SIGABRT (default: false for Android, true for other platforms)" },
    435         { { "no_fb_timeout", required_argument, NULL, 0x106 }, "Skip feedback if the process has timeouted (default: false)" },
    436         { { "exit_upon_crash", no_argument, NULL, 0x107 }, "Exit upon seeing the first crash (default: false)" },
    437         { { "socket_fuzzer", no_argument, NULL, 0x10B }, "Instrument external fuzzer via socket" },
    438         { { "netdriver", no_argument, NULL, 0x10C }, "Use netdriver (libhfnetdriver/). In most cases it will be autodetected through a binary signature" },
    439         { { "only_printable", no_argument, NULL, 'o' }, "Only generate printable inputs" },
    440 
    441 #if defined(_HF_ARCH_LINUX)
    442         { { "linux_symbols_bl", required_argument, NULL, 0x504 }, "Symbols blacklist filter file (one entry per line)" },
    443         { { "linux_symbols_wl", required_argument, NULL, 0x505 }, "Symbols whitelist filter file (one entry per line)" },
    444         { { "linux_addr_low_limit", required_argument, NULL, 0x500 }, "Address limit (from si.si_addr) below which crashes are not reported, (default: 0)" },
    445         { { "linux_keep_aslr", no_argument, NULL, 0x501 }, "Don't disable ASLR randomization, might be useful with MSAN" },
    446         { { "linux_perf_ignore_above", required_argument, NULL, 0x503 }, "Ignore perf events which report IPs above this address" },
    447         { { "linux_perf_instr", no_argument, NULL, 0x510 }, "Use PERF_COUNT_HW_INSTRUCTIONS perf" },
    448         { { "linux_perf_branch", no_argument, NULL, 0x511 }, "Use PERF_COUNT_HW_BRANCH_INSTRUCTIONS perf" },
    449         { { "linux_perf_bts_edge", no_argument, NULL, 0x513 }, "Use Intel BTS to count unique edges" },
    450         { { "linux_perf_ipt_block", no_argument, NULL, 0x514 }, "Use Intel Processor Trace to count unique blocks (requires libipt.so)" },
    451         { { "linux_perf_kernel_only", no_argument, NULL, 0x515 }, "Gather kernel-only coverage with Intel PT and with Intel BTS" },
    452         { { "linux_ns_net", no_argument, NULL, 0x0530 }, "Use Linux NET namespace isolation" },
    453         { { "linux_ns_pid", no_argument, NULL, 0x0531 }, "Use Linux PID namespace isolation" },
    454         { { "linux_ns_ipc", no_argument, NULL, 0x0532 }, "Use Linux IPC namespace isolation" },
    455 #endif // defined(_HF_ARCH_LINUX)
    456 
    457 #if defined(_HF_ARCH_NETBSD)
    458         { { "netbsd_symbols_bl", required_argument, NULL, 0x504 }, "Symbols blacklist filter file (one entry per line)" },
    459         { { "netbsd_symbols_wl", required_argument, NULL, 0x505 }, "Symbols whitelist filter file (one entry per line)" },
    460         { { "netbsd_addr_low_limit", required_argument, NULL, 0x500 }, "Address limit (from si.si_addr) below which crashes are not reported, (default: 0)" },
    461 #endif // defined(_HF_ARCH_NETBSD)
    462         { { 0, 0, 0, 0 }, NULL },
    463     };
    464     // clang-format on
    465 
    466     struct option opts[ARRAYSIZE(custom_opts)];
    467     for (unsigned i = 0; i < ARRAYSIZE(custom_opts); i++) {
    468         opts[i] = custom_opts[i].opt;
    469     }
    470 
    471     enum llevel_t ll = INFO;
    472     const char* logfile = NULL;
    473     int opt_index = 0;
    474     for (;;) {
    475         int c = getopt_long(
    476             argc, argv, "-?hQvVsuPxf:dqe:W:r:c:F:t:R:n:N:l:p:g:E:w:B:zTSo", opts, &opt_index);
    477         if (c < 0) break;
    478 
    479         switch (c) {
    480             case 'h':
    481             case '?':
    482                 cmdlineUsage(argv[0], custom_opts);
    483                 break;
    484             case 'f':
    485                 hfuzz->io.inputDir = optarg;
    486                 if (hfuzz->io.covDirAll == NULL) {
    487                     hfuzz->io.covDirAll = optarg;
    488                 }
    489                 break;
    490             case 'x':
    491                 hfuzz->feedback.dynFileMethod = _HF_DYNFILE_NONE;
    492                 break;
    493             case 'Q':
    494                 hfuzz->exe.nullifyStdio = false;
    495                 break;
    496             case 'v':
    497                 hfuzz->display.useScreen = false;
    498                 break;
    499             case 'V':
    500                 hfuzz->cfg.useVerifier = true;
    501                 break;
    502             case 's':
    503                 hfuzz->exe.fuzzStdin = true;
    504                 break;
    505             case 'u':
    506                 hfuzz->io.saveUnique = false;
    507                 break;
    508             case 'l':
    509                 logfile = optarg;
    510                 break;
    511             case 'd':
    512                 ll = DEBUG;
    513                 break;
    514             case 'q':
    515                 ll = WARNING;
    516                 break;
    517             case 'e':
    518                 hfuzz->io.fileExtn = optarg;
    519                 break;
    520             case 'W':
    521                 hfuzz->io.workDir = optarg;
    522                 break;
    523             case 0x600:
    524                 hfuzz->io.crashDir = optarg;
    525                 break;
    526             case 0x601:
    527                 hfuzz->io.covDirAll = optarg;
    528                 break;
    529             case 0x602:
    530                 hfuzz->io.covDirNew = optarg;
    531                 break;
    532             case 'r':
    533                 hfuzz->mutate.mutationsPerRun = strtoul(optarg, NULL, 10);
    534                 break;
    535             case 'c':
    536                 hfuzz->exe.externalCommand = optarg;
    537                 break;
    538             case 'S':
    539                 hfuzz->sanitizer.enable = true;
    540                 break;
    541             case 0x10B:
    542                 hfuzz->socketFuzzer.enabled = true;
    543                 hfuzz->timing.tmOut = 0;  // Disable process timeout checks
    544                 break;
    545             case 0x10C:
    546                 hfuzz->exe.netDriver = true;
    547                 break;
    548             case 'o':
    549                 hfuzz->cfg.only_printable = true;
    550                 break;
    551             case 'z':
    552                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_SOFT;
    553                 break;
    554             case 'F':
    555                 hfuzz->mutate.maxFileSz = strtoul(optarg, NULL, 0);
    556                 break;
    557             case 't':
    558                 hfuzz->timing.tmOut = atol(optarg);
    559                 break;
    560             case 'R':
    561                 hfuzz->cfg.reportFile = optarg;
    562                 break;
    563             case 'n':
    564                 hfuzz->threads.threadsMax = atol(optarg);
    565                 break;
    566             case 0x109: {
    567                 time_t p = atol(optarg);
    568                 if (p > 0) {
    569                     hfuzz->timing.runEndTime = time(NULL) + p;
    570                 }
    571             } break;
    572             case 'N':
    573                 hfuzz->mutate.mutationsMax = atol(optarg);
    574                 break;
    575             case 0x100:
    576                 hfuzz->exe.asLimit = strtoull(optarg, NULL, 0);
    577                 break;
    578             case 0x101:
    579                 hfuzz->exe.rssLimit = strtoull(optarg, NULL, 0);
    580                 break;
    581             case 0x102:
    582                 hfuzz->exe.dataLimit = strtoull(optarg, NULL, 0);
    583                 break;
    584             case 0x103:
    585                 hfuzz->exe.coreLimit = strtoull(optarg, NULL, 0);
    586                 break;
    587             case 0x104:
    588                 hfuzz->exe.postExternalCommand = optarg;
    589                 break;
    590             case 0x105:
    591                 if ((strcasecmp(optarg, "0") == 0) || (strcasecmp(optarg, "false") == 0)) {
    592                     hfuzz->cfg.monitorSIGABRT = false;
    593                 } else {
    594                     hfuzz->cfg.monitorSIGABRT = true;
    595                 }
    596                 break;
    597             case 0x106:
    598                 hfuzz->feedback.skipFeedbackOnTimeout = true;
    599                 break;
    600             case 0x107:
    601                 hfuzz->cfg.exitUponCrash = true;
    602                 break;
    603             case 0x108:
    604                 hfuzz->exe.clearEnv = true;
    605                 break;
    606             case 'P':
    607                 hfuzz->exe.persistent = true;
    608                 break;
    609             case 'T':
    610                 hfuzz->timing.tmoutVTALRM = true;
    611                 break;
    612             case 'E':
    613                 if (!cmdlineAddEnv(hfuzz, optarg)) {
    614                     return false;
    615                 }
    616                 break;
    617             case 'w':
    618                 hfuzz->mutate.dictionaryFile = optarg;
    619                 break;
    620             case 'B':
    621                 hfuzz->feedback.blacklistFile = optarg;
    622                 break;
    623 #if defined(_HF_ARCH_LINUX)
    624             case 0x500:
    625                 hfuzz->linux.ignoreAddr = (void*)strtoul(optarg, NULL, 0);
    626                 break;
    627             case 0x501:
    628                 hfuzz->linux.disableRandomization = false;
    629                 break;
    630             case 0x503:
    631                 hfuzz->linux.dynamicCutOffAddr = strtoull(optarg, NULL, 0);
    632                 break;
    633             case 0x504:
    634                 hfuzz->linux.symsBlFile = optarg;
    635                 break;
    636             case 0x505:
    637                 hfuzz->linux.symsWlFile = optarg;
    638                 break;
    639             case 0x510:
    640                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_INSTR_COUNT;
    641                 break;
    642             case 0x511:
    643                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_BRANCH_COUNT;
    644                 break;
    645             case 0x513:
    646                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_BTS_EDGE;
    647                 break;
    648             case 0x514:
    649                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_IPT_BLOCK;
    650                 break;
    651             case 0x515:
    652                 hfuzz->linux.kernelOnly = true;
    653                 break;
    654             case 0x530:
    655                 hfuzz->linux.cloneFlags |= (CLONE_NEWUSER | CLONE_NEWNET);
    656                 break;
    657             case 0x531:
    658                 hfuzz->linux.cloneFlags |= (CLONE_NEWUSER | CLONE_NEWPID);
    659                 break;
    660             case 0x532:
    661                 hfuzz->linux.cloneFlags |= (CLONE_NEWUSER | CLONE_NEWIPC);
    662                 break;
    663 #endif /* defined(_HF_ARCH_LINUX) */
    664 #if defined(_HF_ARCH_NETBSD)
    665             case 0x500:
    666                 hfuzz->netbsd.ignoreAddr = (void*)strtoul(optarg, NULL, 0);
    667                 break;
    668             case 0x504:
    669                 hfuzz->netbsd.symsBlFile = optarg;
    670                 break;
    671             case 0x505:
    672                 hfuzz->netbsd.symsWlFile = optarg;
    673                 break;
    674 #endif /* defined(_HF_ARCH_NETBSD) */
    675             default:
    676                 cmdlineUsage(argv[0], custom_opts);
    677                 return false;
    678                 break;
    679         }
    680     }
    681 
    682     logInitLogFile(logfile, -1, ll);
    683 
    684     hfuzz->exe.argc = argc - optind;
    685     hfuzz->exe.cmdline = (const char* const*)&argv[optind];
    686     if (hfuzz->exe.argc <= 0) {
    687         LOG_E("No fuzz command provided");
    688         cmdlineUsage(argv[0], custom_opts);
    689         return false;
    690     }
    691     if (!files_exists(hfuzz->exe.cmdline[0])) {
    692         LOG_E("Your fuzzed binary '%s' doesn't seem to exist", hfuzz->exe.cmdline[0]);
    693         return false;
    694     }
    695     if (!cmdlineVerify(hfuzz)) {
    696         return false;
    697     }
    698 
    699     display_createTargetStr(hfuzz);
    700 
    701     sigemptyset(&hfuzz->exe.waitSigSet);
    702     sigaddset(&hfuzz->exe.waitSigSet, SIGIO);   /* Persistent socket data */
    703     sigaddset(&hfuzz->exe.waitSigSet, SIGUSR1); /* Ping from the signal thread */
    704 
    705     LOG_I("cmdline:'%s', bin:'%s' inputDir:'%s', fuzzStdin:%s, mutationsPerRun:%u, "
    706           "externalCommand:'%s', timeout:%ld, mutationsMax:%zu, threadsMax:%zu",
    707         hfuzz->display.cmdline_txt, hfuzz->exe.cmdline[0], hfuzz->io.inputDir,
    708         cmdlineYesNo(hfuzz->exe.fuzzStdin), hfuzz->mutate.mutationsPerRun,
    709         !hfuzz->exe.externalCommand ? "" : hfuzz->exe.externalCommand, (long)hfuzz->timing.tmOut,
    710         hfuzz->mutate.mutationsMax, hfuzz->threads.threadsMax);
    711 
    712     return true;
    713 }
    714