"Fossies" - the Fresh Open Source Software Archive

Member "honggfuzz-2.2/cmdline.c" (23 Apr 2020, 33389 Bytes) of package /linux/privat/honggfuzz-2.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cmdline.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1_vs_2.2.

    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)) {
   58             return true;
   59         }
   60     }
   61     return false;
   62 }
   63 
   64 static bool cmdlineCheckBinaryType(honggfuzz_t* hfuzz) {
   65     int fd;
   66     off_t fileSz;
   67     uint8_t* map = files_mapFile(hfuzz->exe.cmdline[0], &fileSz, &fd, /* isWriteable= */ false);
   68     if (!map) {
   69         /* It's not a critical error */
   70         return true;
   71     }
   72     defer {
   73         if (munmap(map, fileSz) == -1) {
   74             PLOG_W("munmap(%p, %zu)", map, (size_t)fileSz);
   75         }
   76         close(fd);
   77     };
   78 
   79     if (memmem(map, fileSz, _HF_PERSISTENT_SIG, strlen(_HF_PERSISTENT_SIG))) {
   80         LOG_I("Persistent signature found in '%s'. Enabling persistent fuzzing mode",
   81             hfuzz->exe.cmdline[0]);
   82         hfuzz->exe.persistent = true;
   83     }
   84     if (memmem(map, fileSz, _HF_NETDRIVER_SIG, strlen(_HF_NETDRIVER_SIG))) {
   85         LOG_I("NetDriver signature found '%s'", hfuzz->exe.cmdline[0]);
   86         hfuzz->exe.netDriver = true;
   87     }
   88     return true;
   89 }
   90 
   91 static void cmdlineHelp(const char* pname, struct custom_option* opts) {
   92     LOG_HELP_BOLD("Usage: %s [options] -- path_to_command [args]", pname);
   93     LOG_HELP_BOLD("Options:");
   94     for (int i = 0; opts[i].opt.name; i++) {
   95         if (isprint(opts[i].opt.val) && opts[i].opt.val < 0x80) {
   96             LOG_HELP_BOLD(" --%s%s%c %s", opts[i].opt.name, "|-", opts[i].opt.val,
   97                 opts[i].opt.has_arg == required_argument ? "VALUE" : "");
   98         } else {
   99             LOG_HELP_BOLD(" --%s %s", opts[i].opt.name,
  100                 opts[i].opt.has_arg == required_argument ? "VALUE" : "");
  101         }
  102         LOG_HELP("\t%s", opts[i].descr);
  103     }
  104     LOG_HELP_BOLD("\nExamples:");
  105     LOG_HELP(
  106         " Run the binary over a mutated file chosen from the directory. Disable fuzzing feedback "
  107         "(static mode):");
  108     LOG_HELP_BOLD("  " PROG_NAME " -i input_dir -x -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
  109     LOG_HELP(" As above, provide input over STDIN:");
  110     LOG_HELP_BOLD("  " PROG_NAME " -i input_dir -x -s -- /usr/bin/djpeg");
  111     LOG_HELP(" Use compile-time instrumentation (-fsanitize-coverage=trace-pc-guard,...):");
  112     LOG_HELP_BOLD("  " PROG_NAME " -i input_dir -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
  113     LOG_HELP(" Use persistent mode w/o instrumentation:");
  114     LOG_HELP_BOLD("  " PROG_NAME " -i input_dir -P -x -- /usr/bin/djpeg_persistent_mode");
  115     LOG_HELP(" Use persistent mode and compile-time (-fsanitize-coverage=trace-pc-guard,...) "
  116              "instrumentation:");
  117     LOG_HELP_BOLD("  " PROG_NAME " -i input_dir -P -- /usr/bin/djpeg_persistent_mode");
  118 #if defined(_HF_ARCH_LINUX)
  119     LOG_HELP(
  120         " Run the binary with dynamically generate inputs, maximize total no. of instructions:");
  121     LOG_HELP_BOLD("  " PROG_NAME " --linux_perf_instr -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
  122     LOG_HELP(" As above, maximize total no. of branches:");
  123     LOG_HELP_BOLD("  " PROG_NAME " --linux_perf_branch -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
  124     LOG_HELP(" As above, maximize unique branches (edges) via Intel BTS:");
  125     LOG_HELP_BOLD("  " PROG_NAME " --linux_perf_bts_edge -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
  126     LOG_HELP(
  127         " As above, maximize unique code blocks via Intel Processor Trace (requires libipt.so):");
  128     LOG_HELP_BOLD("  " PROG_NAME " --linux_perf_ipt_block -- /usr/bin/djpeg " _HF_FILE_PLACEHOLDER);
  129 #endif /* defined(_HF_ARCH_LINUX) */
  130 }
  131 
  132 static void cmdlineUsage(const char* pname, struct custom_option* opts) {
  133     cmdlineHelp(pname, opts);
  134     exit(0);
  135 }
  136 
  137 bool cmdlineAddEnv(honggfuzz_t* hfuzz, char* env) {
  138     size_t enveqlen = strlen(env);
  139     const char* eqpos = strchr(env, '=');
  140     if (eqpos) {
  141         enveqlen = (uintptr_t)eqpos - (uintptr_t)env + 1;
  142     }
  143 
  144     for (size_t i = 0; i < ARRAYSIZE(hfuzz->exe.env_ptrs); i++) {
  145         if (hfuzz->exe.env_ptrs[i] == NULL) {
  146             LOG_D("Adding envar '%s' at pos: %zu", env, i);
  147             hfuzz->exe.env_ptrs[i] = hfuzz->exe.env_vals[i];
  148             snprintf(hfuzz->exe.env_vals[i], sizeof(hfuzz->exe.env_vals[i]), "%s", env);
  149             return true;
  150         }
  151         if (strncmp(hfuzz->exe.env_vals[i], env, enveqlen) == 0) {
  152             LOG_W("Replacing envar '%s' with '%s'", hfuzz->exe.env_vals[i], env);
  153             snprintf(hfuzz->exe.env_vals[i], sizeof(hfuzz->exe.env_vals[i]), "%s", env);
  154             hfuzz->exe.env_ptrs[i] = hfuzz->exe.env_vals[i];
  155             return true;
  156         }
  157     }
  158     LOG_E("No more space for new envars (max.%zu)", ARRAYSIZE(hfuzz->exe.env_ptrs));
  159     return false;
  160 }
  161 
  162 tristate_t cmdlineParseTriState(const char* optname, const char* optarg) {
  163     if (!optarg) {
  164         LOG_F("Option '--%s' needs an argument (true|false|maybe)", optname);
  165     }
  166     /* Probably '-' belong to the next option */
  167     if (optarg[0] == '-') {
  168         LOG_F("Option '--%s' needs an argument (true|false|maybe)", optname);
  169     }
  170     if ((strcasecmp(optarg, "0") == 0) || (strcasecmp(optarg, "false") == 0) ||
  171         (strcasecmp(optarg, "n") == 0) || (strcasecmp(optarg, "no") == 0)) {
  172         return false;
  173     }
  174     if ((strcasecmp(optarg, "1") == 0) || (strcasecmp(optarg, "true") == 0) ||
  175         (strcasecmp(optarg, "y") == 0) || (strcasecmp(optarg, "yes") == 0)) {
  176         return true;
  177     }
  178     if ((strcasecmp(optarg, "-1") == 0) || (strcasecmp(optarg, "maybe") == 0) ||
  179         (strcasecmp(optarg, "m") == 0) || (strcasecmp(optarg, "if_supported") == 0)) {
  180         return true;
  181     }
  182     LOG_F("Unknown value for option --%s=%s. Use true, false or maybe", optname, optarg);
  183     return false;
  184 }
  185 
  186 bool cmdlineParseTrueFalse(const char* optname, const char* optarg) {
  187     if (!optarg) {
  188         LOG_F("Option '--%s' needs an argument (true|false)", optname);
  189     }
  190     /* Probably '-' belong to the next option */
  191     if (optarg[0] == '-') {
  192         LOG_F("Option '--%s' needs an argument (true|false)", optname);
  193     }
  194     if ((strcasecmp(optarg, "0") == 0) || (strcasecmp(optarg, "false") == 0) ||
  195         (strcasecmp(optarg, "n") == 0) || (strcasecmp(optarg, "no") == 0)) {
  196         return false;
  197     }
  198     if ((strcasecmp(optarg, "1") == 0) || (strcasecmp(optarg, "true") == 0) ||
  199         (strcasecmp(optarg, "y") == 0) || (strcasecmp(optarg, "yes") == 0)) {
  200         return true;
  201     }
  202     LOG_F("Unknown value for option --%s=%s. Use true or false", optname, optarg);
  203     return false;
  204 }
  205 
  206 rlim_t cmdlineParseRLimit(int res, const char* optarg, unsigned long mul) {
  207     struct rlimit cur;
  208     if (getrlimit(res, &cur) == -1) {
  209         PLOG_F("getrlimit(%d)", res);
  210     }
  211     if (strcasecmp(optarg, "max") == 0) {
  212         return cur.rlim_max;
  213     }
  214     if (strcasecmp(optarg, "def") == 0) {
  215         return cur.rlim_cur;
  216     }
  217     if (util_isANumber(optarg) == false) {
  218         LOG_F("RLIMIT %d needs a numeric or 'max'/'def' value ('%s' provided)", res, optarg);
  219     }
  220     rlim_t val = strtoul(optarg, NULL, 0) * mul;
  221     if ((unsigned long)val == ULONG_MAX && errno != 0) {
  222         PLOG_F("strtoul('%s', 0)", optarg);
  223     }
  224     return val;
  225 }
  226 
  227 static bool cmdlineVerify(honggfuzz_t* hfuzz) {
  228     if (!cmdlineCheckBinaryType(hfuzz)) {
  229         LOG_E("Couldn't test binary for signatures");
  230         return false;
  231     }
  232     if (hfuzz->exe.netDriver && hfuzz->arch_linux.useNetNs == HF_MAYBE) {
  233         LOG_I("The binary uses netdriver, disabling network namespacing");
  234         hfuzz->arch_linux.useNetNs = HF_NO;
  235     }
  236 
  237     if (!hfuzz->exe.fuzzStdin && !hfuzz->exe.persistent &&
  238         !checkFor_FILE_PLACEHOLDER(hfuzz->exe.cmdline)) {
  239         LOG_E("You must specify '" _HF_FILE_PLACEHOLDER
  240               "' if the -s (stdin fuzzing) or --persistent options are not set");
  241         return false;
  242     }
  243 
  244     if (hfuzz->threads.threadsMax >= _HF_THREAD_MAX) {
  245         LOG_E("Too many fuzzing threads specified %zu (>= _HF_THREAD_MAX (%u))",
  246             hfuzz->threads.threadsMax, _HF_THREAD_MAX);
  247         return false;
  248     }
  249     if (hfuzz->threads.threadsMax == 0) {
  250         LOG_E("Too few fuzzing threads specified: %zu", hfuzz->threads.threadsMax);
  251         return false;
  252     }
  253 
  254     if (strchr(hfuzz->io.fileExtn, '/')) {
  255         LOG_E("The file extension contains the '/' character: '%s'", hfuzz->io.fileExtn);
  256         return false;
  257     }
  258 
  259     if (strlen(hfuzz->io.workDir) == 0) {
  260         if (getcwd(hfuzz->io.workDir, sizeof(hfuzz->io.workDir)) == NULL) {
  261             PLOG_W("getcwd() failed. Using '.'");
  262             snprintf(hfuzz->io.workDir, sizeof(hfuzz->io.workDir), ".");
  263         }
  264     }
  265     if (mkdir(hfuzz->io.workDir, 0700) == -1 && errno != EEXIST) {
  266         PLOG_E("Couldn't create the workspace directory '%s'", hfuzz->io.workDir);
  267         return false;
  268     }
  269     if (hfuzz->io.crashDir == NULL) {
  270         hfuzz->io.crashDir = hfuzz->io.workDir;
  271     }
  272     if (mkdir(hfuzz->io.crashDir, 0700) && errno != EEXIST) {
  273         PLOG_E("Couldn't create the crash directory '%s'", hfuzz->io.crashDir);
  274         return false;
  275     }
  276 
  277     if (hfuzz->mutate.mutationsPerRun == 0U && hfuzz->cfg.useVerifier) {
  278         LOG_I("Verifier enabled with mutationsPerRun == 0, activating the dry run mode");
  279     }
  280 
  281     if (hfuzz->io.maxFileSz > _HF_INPUT_MAX_SIZE) {
  282         LOG_E("Maximum file size '%zu' bigger than the maximum size '%zu'", hfuzz->io.maxFileSz,
  283             (size_t)_HF_INPUT_MAX_SIZE);
  284         return false;
  285     }
  286 
  287     return true;
  288 }
  289 
  290 bool cmdlineParse(int argc, char* argv[], honggfuzz_t* hfuzz) {
  291     *hfuzz = (honggfuzz_t){
  292         .threads =
  293             {
  294                 .threadsFinished = 0,
  295                 .threadsMax = ({
  296                     long ncpus = sysconf(_SC_NPROCESSORS_ONLN);
  297                     (ncpus <= 1 ? 1 : ncpus / 2);
  298                 }),
  299                 .threadsActiveCnt = 0,
  300                 .mainThread = pthread_self(),
  301                 .mainPid = getpid(),
  302             },
  303         .io =
  304             {
  305                 .inputDir = NULL,
  306                 .outputDir = NULL,
  307                 .inputDirPtr = NULL,
  308                 .fileCnt = 0,
  309                 .testedFileCnt = 0,
  310                 .maxFileSz = 0,
  311                 .newUnitsAdded = 0,
  312                 .fileExtn = "fuzz",
  313                 .workDir = {},
  314                 .crashDir = NULL,
  315                 .covDirNew = NULL,
  316                 .saveUnique = true,
  317                 .dynfileqMaxSz = 0U,
  318                 .dynfileqCnt = 0U,
  319                 .dynfileq_mutex = PTHREAD_RWLOCK_INITIALIZER,
  320                 .dynfileqCurrent = NULL,
  321                 .dynfileq2Current = NULL,
  322                 .exportFeedback = false,
  323             },
  324         .exe =
  325             {
  326                 .argc = 0,
  327                 .cmdline = NULL,
  328                 .nullifyStdio = true,
  329                 .fuzzStdin = false,
  330                 .externalCommand = NULL,
  331                 .postExternalCommand = NULL,
  332                 .feedbackMutateCommand = NULL,
  333                 .persistent = false,
  334                 .netDriver = false,
  335                 .asLimit = 0U,
  336                 .rssLimit = 0U,
  337                 .dataLimit = 0U,
  338                 .stackLimit = 0U,
  339                 .clearEnv = false,
  340                 .env_ptrs = {},
  341                 .env_vals = {},
  342             },
  343         .timing =
  344             {
  345                 .timeStart = time(NULL),
  346                 .runEndTime = 0,
  347                 .tmOut = 1,
  348                 .lastCovUpdate = time(NULL),
  349                 .timeOfLongestUnitUSecs = 0,
  350                 .tmoutVTALRM = false,
  351             },
  352         .mutate =
  353             {
  354                 .mutationsMax = 0,
  355                 .dictionary = {},
  356                 .dictionaryCnt = 0,
  357                 .dictionaryFile = NULL,
  358                 .mutationsPerRun = 5,
  359                 .maxInputSz = 0,
  360             },
  361         .display =
  362             {
  363                 .useScreen = true,
  364                 .lastDisplayUSecs = util_timeNowUSecs(),
  365                 .cmdline_txt[0] = '\0',
  366             },
  367         .cfg =
  368             {
  369                 .useVerifier = false,
  370                 .exitUponCrash = false,
  371                 .report_mutex = PTHREAD_MUTEX_INITIALIZER,
  372                 .reportFile = NULL,
  373                 .dynFileIterExpire = 0,
  374                 .only_printable = false,
  375                 .minimize = false,
  376                 .switchingToFDM = false,
  377             },
  378         .sanitizer =
  379             {
  380                 .enable = false,
  381                 .del_report = false,
  382             },
  383         .feedback =
  384             {
  385                 .covFeedbackMap = NULL,
  386                 .covFeedbackFd = -1,
  387                 .covFeedback_mutex = PTHREAD_MUTEX_INITIALIZER,
  388                 .cmpFeedbackMap = NULL,
  389                 .cmpFeedbackFd = -1,
  390                 .cmpFeedback = true,
  391                 .blacklistFile = NULL,
  392                 .blacklist = NULL,
  393                 .blacklistCnt = 0,
  394                 .skipFeedbackOnTimeout = false,
  395                 .dynFileMethod = _HF_DYNFILE_SOFT,
  396                 .state = _HF_STATE_UNSET,
  397                 .hwCnts =
  398                     {
  399                         .cpuInstrCnt = 0ULL,
  400                         .cpuBranchCnt = 0ULL,
  401                         .bbCnt = 0ULL,
  402                         .newBBCnt = 0ULL,
  403                         .softCntPc = 0ULL,
  404                         .softCntCmp = 0ULL,
  405                     },
  406             },
  407         .cnts =
  408             {
  409                 .mutationsCnt = 0,
  410                 .crashesCnt = 0,
  411                 .uniqueCrashesCnt = 0,
  412                 .verifiedCrashesCnt = 0,
  413                 .blCrashesCnt = 0,
  414                 .timeoutedCnt = 0,
  415             },
  416         .socketFuzzer =
  417             {
  418                 .enabled = false,
  419                 .serverSocket = -1,
  420                 .clientSocket = -1,
  421             },
  422 
  423         /* Linux code */
  424         .arch_linux =
  425             {
  426                 .exeFd = -1,
  427                 .dynamicCutOffAddr = ~(0ULL),
  428                 .disableRandomization = true,
  429                 .ignoreAddr = NULL,
  430                 .symsBlFile = NULL,
  431                 .symsBlCnt = 0,
  432                 .symsBl = NULL,
  433                 .symsWlFile = NULL,
  434                 .symsWlCnt = 0,
  435                 .symsWl = NULL,
  436                 .cloneFlags = 0,
  437                 .useNetNs = HF_MAYBE,
  438                 .kernelOnly = false,
  439                 .useClone = true,
  440             },
  441         /* NetBSD code */
  442         .arch_netbsd =
  443             {
  444                 .ignoreAddr = NULL,
  445                 .symsBlFile = NULL,
  446                 .symsBlCnt = 0,
  447                 .symsBl = NULL,
  448                 .symsWlFile = NULL,
  449                 .symsWlCnt = 0,
  450                 .symsWl = NULL,
  451             },
  452     };
  453 
  454     TAILQ_INIT(&hfuzz->io.dynfileq);
  455 
  456     // clang-format off
  457     struct custom_option custom_opts[] = {
  458         { { "help", no_argument, NULL, 'h' }, "Help plz.." },
  459         { { "input", required_argument, NULL, 'i' }, "Path to a directory containing initial file corpus" },
  460         { { "output", required_argument, NULL, 'o' }, "Output data (new dynamic coverage corpus, or the minimized coverage corpus) is written to this directory (default: input directory is re-used)" },
  461         { { "persistent", no_argument, NULL, 'P' }, "Enable persistent fuzzing (use hfuzz_cc/hfuzz-clang to compile code). This will be auto-detected!!!" },
  462         { { "instrument", no_argument, NULL, 'z' }, "*DEFAULT-MODE-BY-DEFAULT* Enable compile-time instrumentation (use hfuzz_cc/hfuzz-clang to compile code)" },
  463         { { "minimize", no_argument, NULL, 'M' }, "Minimize the input corpus. It will most likely delete some corpus files (from the --input directory) if no --output is used!" },
  464         { { "noinst", no_argument, NULL, 'x' }, "Static mode only, disable any instrumentation (hw/sw) feedback" },
  465         { { "keep_output", no_argument, NULL, 'Q' }, "Don't close children's stdin, stdout, stderr; can be noisy" },
  466         { { "timeout", required_argument, NULL, 't' }, "Timeout in seconds (default: 1 (second))" },
  467         { { "threads", required_argument, NULL, 'n' }, "Number of concurrent fuzzing threads (default: number of CPUs / 2)" },
  468         { { "stdin_input", no_argument, NULL, 's' }, "Provide fuzzing input on STDIN, instead of " _HF_FILE_PLACEHOLDER },
  469         { { "mutations_per_run", required_argument, NULL, 'r' }, "Maximal number of mutations per one run (default: 6)" },
  470         { { "logfile", required_argument, NULL, 'l' }, "Log file" },
  471         { { "verbose", no_argument, NULL, 'v' }, "Disable ANSI console; use simple log output" },
  472         { { "verifier", no_argument, NULL, 'V' }, "Enable crashes verifier" },
  473         { { "debug", no_argument, NULL, 'd' }, "Show debug messages (level >= 4)" },
  474         { { "quiet", no_argument, NULL, 'q' }, "Show only warnings and more serious messages (level <= 1)" },
  475         { { "extension", required_argument, NULL, 'e' }, "Input file extension (e.g. 'swf'), (default: 'fuzz')" },
  476         { { "workspace", required_argument, NULL, 'W' }, "Workspace directory to save crashes & runtime files (default: '.')" },
  477         { { "crashdir", required_argument, NULL, 0x600 }, "Directory where crashes are saved to (default: workspace directory)" },
  478         { { "covdir_all", required_argument, NULL, 'o' }, "** DEPRECATED ** use --output" },
  479         { { "covdir_new", required_argument, NULL, 0x602 }, "New coverage (beyond the dry-run fuzzing phase) is written to this separate directory" },
  480         { { "dict", required_argument, NULL, 'w' }, "Dictionary file. Format:http://llvm.org/docs/LibFuzzer.html#dictionaries" },
  481         { { "stackhash_bl", required_argument, NULL, 'B' }, "Stackhashes blacklist file (one entry per line)" },
  482         { { "mutate_cmd", required_argument, NULL, 'c' }, "External command producing fuzz files (instead of internal mutators)" },
  483         { { "pprocess_cmd", required_argument, NULL, 0x111 }, "External command postprocessing files produced by internal mutators" },
  484         { { "ffmutate_cmd", required_argument, NULL, 0x110 }, "External command mutating files which have effective coverage feedback" },
  485         { { "run_time", required_argument, NULL, 0x109 }, "Number of seconds this fuzzing session will last (default: 0 [no limit])" },
  486         { { "iterations", required_argument, NULL, 'N' }, "Number of fuzzing iterations (default: 0 [no limit])" },
  487         { { "rlimit_as", required_argument, NULL, 0x100 }, "Per process RLIMIT_AS in MiB (default: 0 [default limit])" },
  488         { { "rlimit_rss", required_argument, NULL, 0x101 }, "Per process RLIMIT_RSS in MiB (default: 0 [default limit]). It will also set *SAN's soft_rss_limit_mb" },
  489         { { "rlimit_data", required_argument, NULL, 0x102 }, "Per process RLIMIT_DATA in MiB (default: 0 [default limit])" },
  490         { { "rlimit_core", required_argument, NULL, 0x103 }, "Per process RLIMIT_CORE in MiB (default: 0 [no cores are produced])" },
  491         { { "rlimit_stack", required_argument, NULL, 0x104 }, "Per process RLIMIT_STACK in MiB (default: 0 [default limit])" },
  492         { { "report", required_argument, NULL, 'R' }, "Write report to this file (default: '<workdir>/" _HF_REPORT_FILE "')" },
  493         { { "max_file_size", required_argument, NULL, 'F' }, "Maximal size of files processed by the fuzzer in bytes (default: 134217728 = 128MB)" },
  494         { { "clear_env", no_argument, NULL, 0x108 }, "Clear all environment variables before executing the binary" },
  495         { { "env", required_argument, NULL, 'E' }, "Pass this environment variable, can be used multiple times" },
  496         { { "save_all", no_argument, NULL, 'u' }, "Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames" },
  497         { { "tmout_sigvtalrm", no_argument, NULL, 'T' }, "Use SIGVTALRM to kill timeouting processes (default: use SIGKILL)" },
  498         { { "sanitizers", no_argument, NULL, 'S' }, "** DEPRECATED ** Enable sanitizers settings (default: false)" },
  499         { { "sanitizers_del_report", required_argument, NULL, 0x10F }, "Delete sanitizer report after use (default: false)" },
  500         { { "monitor_sigabrt", required_argument, NULL, 0x105 }, "** DEPRECATED ** SIGABRT is always monitored" },
  501         { { "no_fb_timeout", required_argument, NULL, 0x106 }, "Skip feedback if the process has timeouted (default: false)" },
  502         { { "exit_upon_crash", no_argument, NULL, 0x107 }, "Exit upon seeing the first crash (default: false)" },
  503         { { "socket_fuzzer", no_argument, NULL, 0x10B }, "Instrument external fuzzer via socket" },
  504         { { "netdriver", no_argument, NULL, 0x10C }, "Use netdriver (libhfnetdriver/). In most cases it will be autodetected through a binary signature" },
  505         { { "only_printable", no_argument, NULL, 0x10D }, "Only generate printable inputs" },
  506         { { "export_feedback", no_argument, NULL, 0x10E }, "Export the coverage feedback structure as ./hfuzz-feedback" },
  507         { { "const_feedback", required_argument, NULL, 0x112 }, "Use constant integer/string values from fuzzed programs to mangle input files via a dynamic dictionary (default: true)" },
  508 
  509 #if defined(_HF_ARCH_LINUX)
  510         { { "linux_symbols_bl", required_argument, NULL, 0x504 }, "Symbols blacklist filter file (one entry per line)" },
  511         { { "linux_symbols_wl", required_argument, NULL, 0x505 }, "Symbols whitelist filter file (one entry per line)" },
  512         { { "linux_addr_low_limit", required_argument, NULL, 0x500 }, "Address limit (from si.si_addr) below which crashes are not reported, (default: 0)" },
  513         { { "linux_keep_aslr", no_argument, NULL, 0x501 }, "Don't disable ASLR randomization, might be useful with MSAN" },
  514         { { "linux_perf_ignore_above", required_argument, NULL, 0x503 }, "Ignore perf events which report IPs above this address" },
  515         { { "linux_perf_instr", no_argument, NULL, 0x510 }, "Use PERF_COUNT_HW_INSTRUCTIONS perf" },
  516         { { "linux_perf_branch", no_argument, NULL, 0x511 }, "Use PERF_COUNT_HW_BRANCH_INSTRUCTIONS perf" },
  517         { { "linux_perf_bts_edge", no_argument, NULL, 0x513 }, "Use Intel BTS to count unique edges" },
  518         { { "linux_perf_ipt_block", no_argument, NULL, 0x514 }, "Use Intel Processor Trace to count unique blocks (requires libipt.so)" },
  519         { { "linux_perf_kernel_only", no_argument, NULL, 0x515 }, "Gather kernel-only coverage with Intel PT and with Intel BTS" },
  520         { { "linux_ns_net", required_argument, NULL, 0x0530 }, "Use Linux NET namespace isolation (yes/no/maybe [default:maybe/if_supported])" },
  521         { { "linux_ns_pid", no_argument, NULL, 0x0531 }, "Use Linux PID namespace isolation" },
  522         { { "linux_ns_ipc", no_argument, NULL, 0x0532 }, "Use Linux IPC namespace isolation" },
  523 #endif // defined(_HF_ARCH_LINUX)
  524 
  525 #if defined(_HF_ARCH_NETBSD)
  526         { { "netbsd_symbols_bl", required_argument, NULL, 0x504 }, "Symbols blacklist filter file (one entry per line)" },
  527         { { "netbsd_symbols_wl", required_argument, NULL, 0x505 }, "Symbols whitelist filter file (one entry per line)" },
  528         { { "netbsd_addr_low_limit", required_argument, NULL, 0x500 }, "Address limit (from si.si_addr) below which crashes are not reported, (default: 0)" },
  529 #endif // defined(_HF_ARCH_NETBSD)
  530         { { 0, 0, 0, 0 }, NULL },
  531     };
  532     // clang-format on
  533 
  534     struct option opts[ARRAYSIZE(custom_opts)];
  535     for (size_t i = 0; i < ARRAYSIZE(custom_opts); i++) {
  536         opts[i] = custom_opts[i].opt;
  537     }
  538 
  539     enum llevel_t ll = INFO;
  540     const char* logfile = NULL;
  541     int opt_index = 0;
  542     for (;;) {
  543         int c = getopt_long(
  544             argc, argv, "-?hQvVsuPxf:i:dqe:W:r:c:F:t:R:n:N:l:p:g:E:w:B:zMTS", opts, &opt_index);
  545         if (c < 0) {
  546             break;
  547         }
  548 
  549         switch (c) {
  550             case 'h':
  551             case '?':
  552                 cmdlineUsage(argv[0], custom_opts);
  553                 break;
  554             case 'i':
  555             case 'f': /* Synonym for -i, stands for -f(iles) */
  556                 hfuzz->io.inputDir = optarg;
  557                 break;
  558             case 'x':
  559                 hfuzz->feedback.dynFileMethod = _HF_DYNFILE_NONE;
  560                 break;
  561             case 'Q':
  562                 hfuzz->exe.nullifyStdio = false;
  563                 break;
  564             case 'v':
  565                 hfuzz->display.useScreen = false;
  566                 break;
  567             case 'V':
  568                 hfuzz->cfg.useVerifier = true;
  569                 break;
  570             case 's':
  571                 hfuzz->exe.fuzzStdin = true;
  572                 break;
  573             case 'u':
  574                 hfuzz->io.saveUnique = false;
  575                 break;
  576             case 'l':
  577                 logfile = optarg;
  578                 break;
  579             case 'd':
  580                 ll = DEBUG;
  581                 break;
  582             case 'q':
  583                 ll = WARNING;
  584                 break;
  585             case 'e':
  586                 hfuzz->io.fileExtn = optarg;
  587                 break;
  588             case 'W':
  589                 snprintf(hfuzz->io.workDir, sizeof(hfuzz->io.workDir), "%s", optarg);
  590                 break;
  591             case 0x600:
  592                 hfuzz->io.crashDir = optarg;
  593                 break;
  594             case 'o':
  595                 hfuzz->io.outputDir = optarg;
  596                 break;
  597             case 0x602:
  598                 hfuzz->io.covDirNew = optarg;
  599                 break;
  600             case 'r':
  601                 hfuzz->mutate.mutationsPerRun = strtoul(optarg, NULL, 10);
  602                 break;
  603             case 'c':
  604                 hfuzz->exe.externalCommand = optarg;
  605                 break;
  606             case 'S':
  607                 hfuzz->sanitizer.enable = true;
  608                 break;
  609             case 0x10F:
  610                 hfuzz->sanitizer.del_report = cmdlineParseTrueFalse(opts[opt_index].name, optarg);
  611                 break;
  612             case 0x10B:
  613                 hfuzz->socketFuzzer.enabled = true;
  614                 hfuzz->timing.tmOut = 0; /* Disable process timeout checks */
  615                 break;
  616             case 0x10C:
  617                 hfuzz->exe.netDriver = true;
  618                 break;
  619             case 0x10D:
  620                 hfuzz->cfg.only_printable = true;
  621                 break;
  622             case 0x10E:
  623                 hfuzz->io.exportFeedback = true;
  624                 break;
  625             case 0x112:
  626                 hfuzz->feedback.cmpFeedback = cmdlineParseTrueFalse(opts[opt_index].name, optarg);
  627                 break;
  628             case 'z':
  629                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_SOFT;
  630                 break;
  631             case 'M':
  632                 hfuzz->cfg.minimize = true;
  633                 break;
  634             case 'F':
  635                 hfuzz->io.maxFileSz = strtoul(optarg, NULL, 0);
  636                 break;
  637             case 't':
  638                 hfuzz->timing.tmOut = atol(optarg);
  639                 break;
  640             case 'R':
  641                 hfuzz->cfg.reportFile = optarg;
  642                 break;
  643             case 'n':
  644                 if (optarg[0] == 'a') {
  645                     long ncpus = sysconf(_SC_NPROCESSORS_ONLN);
  646                     hfuzz->threads.threadsMax = (ncpus < 1 ? 1 : ncpus);
  647                 } else {
  648                     if (!util_isANumber(optarg)) {
  649                         LOG_E("'-n %s' is not a number", optarg);
  650                         return false;
  651                     }
  652                     hfuzz->threads.threadsMax = strtoul(optarg, NULL, 0);
  653                 }
  654                 break;
  655             case 0x109: {
  656                 time_t p = atol(optarg);
  657                 if (p > 0) {
  658                     hfuzz->timing.runEndTime = time(NULL) + p;
  659                 }
  660             } break;
  661             case 'N':
  662                 hfuzz->mutate.mutationsMax = atol(optarg);
  663                 break;
  664             case 0x100:
  665                 hfuzz->exe.asLimit = strtoull(optarg, NULL, 0);
  666                 break;
  667             case 0x101:
  668                 hfuzz->exe.rssLimit = strtoull(optarg, NULL, 0);
  669                 break;
  670             case 0x102:
  671                 hfuzz->exe.dataLimit = strtoull(optarg, NULL, 0);
  672                 break;
  673             case 0x103:
  674                 hfuzz->exe.coreLimit = strtoull(optarg, NULL, 0);
  675                 break;
  676             case 0x104:
  677                 hfuzz->exe.stackLimit = strtoull(optarg, NULL, 0);
  678                 break;
  679             case 0x111:
  680                 hfuzz->exe.postExternalCommand = optarg;
  681                 break;
  682             case 0x110:
  683                 hfuzz->exe.feedbackMutateCommand = optarg;
  684                 break;
  685             case 0x106:
  686                 hfuzz->feedback.skipFeedbackOnTimeout = true;
  687                 break;
  688             case 0x107:
  689                 hfuzz->cfg.exitUponCrash = true;
  690                 break;
  691             case 0x108:
  692                 hfuzz->exe.clearEnv = true;
  693                 break;
  694             case 'P':
  695                 hfuzz->exe.persistent = true;
  696                 break;
  697             case 'T':
  698                 hfuzz->timing.tmoutVTALRM = true;
  699                 break;
  700             case 'E':
  701                 if (!cmdlineAddEnv(hfuzz, optarg)) {
  702                     return false;
  703                 }
  704                 break;
  705             case 'w':
  706                 hfuzz->mutate.dictionaryFile = optarg;
  707                 break;
  708             case 'B':
  709                 hfuzz->feedback.blacklistFile = optarg;
  710                 break;
  711 #if defined(_HF_ARCH_LINUX)
  712             case 0x500:
  713                 hfuzz->arch_linux.ignoreAddr = (void*)strtoul(optarg, NULL, 0);
  714                 break;
  715             case 0x501:
  716                 hfuzz->arch_linux.disableRandomization = false;
  717                 break;
  718             case 0x503:
  719                 hfuzz->arch_linux.dynamicCutOffAddr = strtoull(optarg, NULL, 0);
  720                 break;
  721             case 0x504:
  722                 hfuzz->arch_linux.symsBlFile = optarg;
  723                 break;
  724             case 0x505:
  725                 hfuzz->arch_linux.symsWlFile = optarg;
  726                 break;
  727             case 0x510:
  728                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_INSTR_COUNT;
  729                 break;
  730             case 0x511:
  731                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_BRANCH_COUNT;
  732                 break;
  733             case 0x513:
  734                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_BTS_EDGE;
  735                 break;
  736             case 0x514:
  737                 hfuzz->feedback.dynFileMethod |= _HF_DYNFILE_IPT_BLOCK;
  738                 break;
  739             case 0x515:
  740                 hfuzz->arch_linux.kernelOnly = true;
  741                 break;
  742             case 0x530:
  743                 hfuzz->arch_linux.useNetNs = cmdlineParseTriState(opts[opt_index].name, optarg);
  744                 if (hfuzz->arch_linux.useNetNs == HF_YES) {
  745                     hfuzz->arch_linux.cloneFlags |= (CLONE_NEWUSER | CLONE_NEWNET);
  746                 }
  747                 break;
  748             case 0x531:
  749                 hfuzz->arch_linux.cloneFlags |= (CLONE_NEWUSER | CLONE_NEWPID);
  750                 break;
  751             case 0x532:
  752                 hfuzz->arch_linux.cloneFlags |= (CLONE_NEWUSER | CLONE_NEWIPC);
  753                 break;
  754 #endif /* defined(_HF_ARCH_LINUX) */
  755 #if defined(_HF_ARCH_NETBSD)
  756             case 0x500:
  757                 hfuzz->arch_netbsd.ignoreAddr = (void*)strtoul(optarg, NULL, 0);
  758                 break;
  759             case 0x504:
  760                 hfuzz->arch_netbsd.symsBlFile = optarg;
  761                 break;
  762             case 0x505:
  763                 hfuzz->arch_netbsd.symsWlFile = optarg;
  764                 break;
  765 #endif /* defined(_HF_ARCH_NETBSD) */
  766             default:
  767                 cmdlineUsage(argv[0], custom_opts);
  768                 return false;
  769                 break;
  770         }
  771     }
  772 
  773     logInitLogFile(logfile, -1, ll);
  774 
  775     hfuzz->exe.argc = argc - optind;
  776     hfuzz->exe.cmdline = (const char* const*)&argv[optind];
  777     if (hfuzz->exe.argc <= 0) {
  778         LOG_E("No fuzz command provided");
  779         cmdlineUsage(argv[0], custom_opts);
  780         return false;
  781     }
  782     if (!files_exists(hfuzz->exe.cmdline[0])) {
  783         LOG_E("Your fuzzed binary '%s' doesn't seem to exist", hfuzz->exe.cmdline[0]);
  784         return false;
  785     }
  786     if (!cmdlineVerify(hfuzz)) {
  787         return false;
  788     }
  789 
  790     display_createTargetStr(hfuzz);
  791 
  792     return true;
  793 }