blob: eed56158b2b3028629b1b26182c2bd3f5faf63f6 [file] [log] [blame]
Robert Swieckia88f96f2015-10-09 16:47:39 +02001/*
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 <grp.h>
29#include <inttypes.h>
30#include <limits.h>
31#include <pwd.h>
32#include <stdbool.h>
33#include <stdlib.h>
34#include <stdio.h>
35#include <string.h>
36#include <strings.h>
37#include <sys/personality.h>
38#include <sys/time.h>
39#include <unistd.h>
40
41#include "common.h"
42#include "log.h"
43
44#define AB ANSI_BOLD
45#define AC ANSI_CLEAR
46#define ANSI_BOLD "\033[1m"
47#define ANSI_CLEAR "\033[0m"
48
49struct custom_option {
50 struct option opt;
51 const char *descr;
52};
53
54static bool checkFor_FILE_PLACEHOLDER(char **args)
55{
56 for (int x = 0; args[x]; x++) {
57 if (strstr(args[x], _HF_FILE_PLACEHOLDER))
58 return true;
59 }
60 return false;
61}
62
63static const char *cmdlineYesNo(bool yes)
64{
65 return (yes ? "true" : "false");
66}
67
68static void cmdlineHelp(const char *pname, struct custom_option *opts)
69{
70 LOG_HELP_BOLD("Usage: %s [options] -- path_to_command [args]", pname);
71 LOG_HELP_BOLD("Options:");
72 for (int i = 0; opts[i].opt.name; i++) {
73 if (isprint(opts[i].opt.val)) {
74 LOG_HELP_BOLD(" --%s%s%c %s", opts[i].opt.name,
75 "|-", opts[i].opt.val,
76 opts[i].opt.has_arg == required_argument ? "[val]" : "");
77 } else {
78 LOG_HELP_BOLD(" --%s %s", opts[i].opt.name,
79 opts[i].opt.has_arg == required_argument ? "[val]" : "");
80 }
81 LOG_HELP("\t%s", opts[i].descr);
82 }
83 LOG_HELP("%s",
84 "\nExamples:\n"
85 " Run the binary over a mutated file chosen from the directory:\n"
86 AB " " PROG_NAME " -f input_dir -- /usr/bin/tiffinfo -D " _HF_FILE_PLACEHOLDER AC "\n"
87 " As above, provide input over STDIN:\n"
88 AB " " PROG_NAME " -f input_dir -s -- /usr/bin/djpeg\n" AC
89#if defined(_HF_ARCH_LINUX)
90 " Run the binary over a dynamic file, maximize total no. of instructions:\n"
91 AB " " PROG_NAME " -LDi -- /usr/bin/tiffinfo -D " _HF_FILE_PLACEHOLDER AC "\n"
92 " Run the binary over a dynamic file, maximize total no. of branches:\n"
93 AB " " PROG_NAME " -LDb -- /usr/bin/tiffinfo -D " _HF_FILE_PLACEHOLDER AC "\n"
94 " Run the binary over a dynamic file, maximize unique code blocks (coverage):\n"
95 AB " " PROG_NAME " -LDp -- /usr/bin/tiffinfo -D " _HF_FILE_PLACEHOLDER AC "\n"
96 " Run the binary over a dynamic file, maximize unique branches (edges):\n"
97 AB " " PROG_NAME " -LDe -- /usr/bin/tiffinfo -D " _HF_FILE_PLACEHOLDER AC "\n"
98 " Run the binary over a dynamic file, maximize custom counters (experimental):\n"
99 AB " " PROG_NAME " -LDf -- /usr/bin/tiffinfo -D " _HF_FILE_PLACEHOLDER AC "\n"
100#endif /* defined(_HF_ARCH_LINUX) */
101 );
102}
103
104static void cmdlineUsage(const char *pname, struct custom_option *opts)
105{
106 cmdlineHelp(pname, opts);
107 exit(0);
108}
109
110static bool cmdlineIsANumber(const char *s)
111{
112 for (int i = 0; s[i]; s++) {
113 if (!isdigit(s[i]) && s[i] != 'x') {
114 return false;
115 }
116 }
117 return true;
118}
119
120rlim_t cmdlineParseRLimit(int res, const char *optarg, unsigned long mul)
121{
122 struct rlimit cur;
123 if (getrlimit(res, &cur) == -1) {
124 PLOG_F("getrlimit(%d)", res);
125 }
126 if (strcasecmp(optarg, "max") == 0) {
127 return cur.rlim_max;
128 }
129 if (strcasecmp(optarg, "def") == 0) {
130 return cur.rlim_cur;
131 }
132 if (cmdlineIsANumber(optarg) == false) {
133 LOG_F("RLIMIT %d needs a numeric or 'max'/'def' value ('%s' provided)", res, optarg);
134 }
135 rlim_t val = strtoul(optarg, NULL, 0) * mul;
136 if (val == ULONG_MAX && errno != 0) {
137 PLOG_F("strtoul('%s', 0)", optarg);
138 }
139 return val;
140}
141
142bool cmdlineParse(int argc, char *argv[], honggfuzz_t * hfuzz)
143{
144 /* *INDENT-OFF* */
145 (*hfuzz) = (honggfuzz_t) {
146 .cmdline = NULL,
147 .inputFile = NULL,
148 .nullifyStdio = false,
149 .useScreen = true,
150 .fuzzStdin = false,
151 .saveUnique = true,
152 .fileExtn = "fuzz",
153 .workDir = ".",
154 .flipRate = 0.001f,
155 .externalCommand = NULL,
156 .dictionaryFile = NULL,
157 .dictionary = NULL,
158 .dictionaryCnt = 0,
159 .blacklistFile = NULL,
160 .blacklistCnt = 0,
161 .blacklist = NULL,
162 .maxFileSz = (1024 * 1024),
163 .tmOut = 3,
164 .mutationsMax = 0,
165 .threadsFinished = 0,
166 .threadsMax = 2,
167 .reportFile = NULL,
168 .asLimit = 0ULL,
169 .files = NULL,
170 .fileCnt = 0,
171 .pid = 0,
172 .envs = {[0 ... (ARRAYSIZE(hfuzz->envs) - 1)] = NULL,},
173
174 .timeStart = time(NULL),
175 .mutationsCnt = 0,
176 .crashesCnt = 0,
177 .uniqueCrashesCnt = 0,
178 .blCrashesCnt = 0,
179 .timeoutedCnt = 0,
180
181 .dynFileMethod = _HF_DYNFILE_NONE,
182 .dynamicFileBest = NULL,
183 .dynamicFileBestSz = 1,
184 .hwCnts = {
185 .cpuInstrCnt = 0ULL,
186 .cpuBranchCnt = 0ULL,
187 .pcCnt = 0ULL,
188 .pathCnt = 0ULL,
189 .customCnt = 0ULL,
190 },
191 .dynamicCutOffAddr = ~(0ULL),
192 .dynamicFile_mutex = PTHREAD_MUTEX_INITIALIZER,
193
194 .disableRandomization = true,
195 .msanReportUMRS = false,
196 .ignoreAddr = NULL,
197 };
198 /* *INDENT-ON* */
199
200 /* *INDENT-OFF* */
201 struct custom_option custom_opts[] = {
202 {{"help", no_argument, NULL, 'h'}, "Help plz.."},
203 {{"input", required_argument, NULL, 'f'}, "Path to the file corpus (file or a directory)"},
204 {{"nullify_stdio", no_argument, NULL, 'q'}, "Null-ify children's stdin, stdout, stderr; make them quiet"},
205 {{"stdin_input", no_argument, NULL, 's'}, "Provide fuzzing input on STDIN, instead of ___FILE___"},
206 {{"save_all", no_argument, NULL, 'u'}, "Save all test-cases (not only the unique ones) by appending the current time-stamp to the filenames"},
207 {{"verbose", no_argument, NULL, 'v'}, "Disable ANSI console; use simple log output"},
208 {{"debug_level", required_argument, NULL, 'd'}, "Debug level (0 - FATAL ... 4 - DEBUG), (default: '3' [INFO])"},
209 {{"extension", required_argument, NULL, 'e'}, "Input file extension (e.g. 'swf'), (default: 'fuzz')"},
210 {{"wokspace", required_argument, NULL, 'W'}, "Workspace directory to save crashes & runtime files (default: '.')"},
211 {{"flip_rate", required_argument, NULL, 'r'}, "Maximal flip rate, (default: '0.001')"},
212 {{"wordlist", required_argument, NULL, 'w'}, "Wordlist file (tokens delimited by NUL-bytes)"},
213 {{"stackhash_bl", required_argument, NULL, 'B'}, "Stackhashes blacklist file (one entry per line)"},
214 {{"mutate_cmd", required_argument, NULL, 'c'}, "External command modifying the input corpus of files, instead of -r/-m parameters"},
215 {{"timeout", required_argument, NULL, 't'}, "Timeout in seconds (default: '3')"},
216 {{"threads", required_argument, NULL, 'n'}, "Number of concurrent fuzzing threads (default: '2')"},
217 {{"iterations", required_argument, NULL, 'N'}, "Number of fuzzing iterations (default: '0' [no limit])"},
218 {{"rlimit_as", required_argument, NULL, 'l'}, "Per process memory limit in MiB (default: '0' [no limit])"},
219 {{"report", required_argument, NULL, 'R'}, "Per process memory limit in MiB (default: '" _HF_REPORT_FILE "')"},
220 {{"max_file_size", required_argument, NULL, 'F'}, "Maximal size of files processed by the fuzzer in bytes (default: '1048576')"},
221 {{"env", required_argument, NULL, 'E'}, "Pass this environment variable, can be used multiple times"},
222
223#if defined(_HF_ARCH_LINUX)
224 {{"linux_pid", required_argument, NULL, 'p'}, "[Linux] Attach to a pid (and its thread group)"},
225 {{"linux_addr_low_limit", required_argument, NULL, 0x500}, "Address limit (from si.si_addr) below which crashes are not reported, (default: '0')"},
226 {{"linux_keep_aslr", no_argument, NULL, 0x501}, "Don't disable ASLR randomization, might be useful with MSAN"},
227 {{"linux_report_msan_umrs", no_argument, NULL, 0x502}, "Report MSAN's UMRS (uninitialized memory access)"},
228 {{"linux_perf_instr", no_argument, NULL, 0x510}, "Use PERF_COUNT_HW_INSTRUCTIONS perf"},
229 {{"linux_perf_branch", no_argument, NULL, 0x511}, "Use PERF_COUNT_HW_BRANCH_INSTRUCTIONS perf"},
230 {{"linux_perf_ip", no_argument, NULL, 0x512}, "Use PERF_SAMPLE_IP perf (newer Intel CPUs only)"},
231 {{"linux_perf_ip_addr", no_argument, NULL, 0x513}, "Use PERF_SAMPLE_IP/PERF_SAMPLE_ADDR perf (newer Intel CPUs only)"},
232 {{"linux_perf_custom", no_argument, NULL, 0x514}, "Custom counter (see the interceptor/ directory for examples)"},
233#endif // defined(_HF_ARCH_LINUX)
234 {{0, 0, 0, 0}, NULL},
235 };
236 /* *INDENT-ON* */
237
238 struct option opts[ARRAYSIZE(custom_opts)];
239 for (unsigned i = 0; i < ARRAYSIZE(custom_opts); i++) {
240 opts[i] = custom_opts[i].opt;
241 }
242
243 enum llevel_t ll = INFO;
244 const char *logfile = NULL;
245 int opt_index = 0;
246 for (;;) {
247 int c = getopt_long(argc, argv, "-?hqvsuf:d:e:W:r:c:F:t:R:n:N:l:p:g:E:w:B:", opts,
248 &opt_index);
249 if (c < 0)
250 break;
251
252 switch (c) {
253 case 'h':
254 case '?':
255 cmdlineUsage(argv[0], custom_opts);
256 break;
257 case 'f':
258 hfuzz->inputFile = optarg;
259 break;
260 case 'q':
261 hfuzz->nullifyStdio = true;
262 break;
263 case 'v':
264 hfuzz->useScreen = false;
265 break;
266 case 's':
267 hfuzz->fuzzStdin = true;
268 break;
269 case 'u':
270 hfuzz->saveUnique = false;
271 break;
272 case 'd':
273 ll = atoi(optarg);
274 break;
275 case 'e':
276 hfuzz->fileExtn = optarg;
277 break;
278 case 'W':
279 hfuzz->workDir = optarg;
280 break;
281 case 'r':
282 hfuzz->flipRate = strtod(optarg, NULL);
283 break;
284 case 'c':
285 hfuzz->externalCommand = optarg;
286 break;
287 case 'F':
288 hfuzz->maxFileSz = strtoul(optarg, NULL, 0);
289 break;
290 case 't':
291 hfuzz->tmOut = atol(optarg);
292 break;
293 case 'R':
294 hfuzz->reportFile = optarg;
295 break;
296 case 'n':
297 hfuzz->threadsMax = atol(optarg);
298 break;
299 case 'N':
300 hfuzz->mutationsMax = atol(optarg);
301 break;
302 case 'l':
303 hfuzz->asLimit = strtoull(optarg, NULL, 0);
304 break;
305 case 'p':
306 hfuzz->pid = atoi(optarg);
307 break;
308 case 'o':
309 break;
310 case 'E':
311 for (size_t i = 0; i < ARRAYSIZE(hfuzz->envs); i++) {
312 if (hfuzz->envs[i] == NULL) {
313 hfuzz->envs[i] = optarg;
314 break;
315 }
316 }
317 break;
318 case 'w':
319 hfuzz->dictionaryFile = optarg;
320 break;
321 case 'B':
322 hfuzz->blacklistFile = optarg;
323 break;
324 case 0x500:
325 hfuzz->ignoreAddr = (void *)strtoul(optarg, NULL, 0);
326 break;
327 case 0x501:
328 hfuzz->disableRandomization = false;
329 break;
330 case 0x502:
331 hfuzz->msanReportUMRS = true;
332 break;
333 case 0x503:
334 hfuzz->dynamicCutOffAddr = strtoull(optarg + 1, NULL, 0);
335 break;
336 case 0x510:
337 hfuzz->dynFileMethod |= _HF_DYNFILE_INSTR_COUNT;
338 break;
339 case 0x511:
340 hfuzz->dynFileMethod |= _HF_DYNFILE_BRANCH_COUNT;
341 break;
342 case 0x512:
343 hfuzz->dynFileMethod |= _HF_DYNFILE_UNIQUE_BLOCK_COUNT;
344 break;
345 case 0x513:
346 hfuzz->dynFileMethod |= _HF_DYNFILE_UNIQUE_EDGE_COUNT;
347 break;
348 case 0x514:
349 hfuzz->dynFileMethod |= _HF_DYNFILE_CUSTOM;
350 break;
351 default:
352 cmdlineUsage(argv[0], custom_opts);
353 return false;
354 break;
355 }
356 }
357 printf("MYLL: %d\n", ll);
358 if (logInitLogFile(logfile, ll) == false) {
359 return false;
360 }
361
362 hfuzz->cmdline = &argv[optind];
363 if (hfuzz->cmdline[0] == NULL) {
364 LOG_E("No fuzz command provided");
365 cmdlineUsage(argv[0], custom_opts);
366 return false;
367 }
368
369 if (hfuzz->dynamicFileBestSz > hfuzz->maxFileSz) {
370 LOG_E("Initial dynamic file size cannot be larger than maximum file size (%zu > %zu)",
371 hfuzz->dynamicFileBestSz, hfuzz->maxFileSz);
372 return false;
373 }
374
375 if ((hfuzz->dynamicFileBest = malloc(hfuzz->maxFileSz)) == NULL) {
376 LOG_E("malloc(%zu) failed", hfuzz->maxFileSz);
377 return false;
378 }
379
380 if (!hfuzz->fuzzStdin && !checkFor_FILE_PLACEHOLDER(hfuzz->cmdline)) {
381 LOG_E("You must specify '" _HF_FILE_PLACEHOLDER
382 "' when the -s (stdin fuzzing) option is not set");
383 return false;
384 }
385
386 if (strchr(hfuzz->fileExtn, '/')) {
387 LOG_E("The file extension contains the '/' character: '%s'", hfuzz->fileExtn);
388 return false;
389 }
390
391 if (hfuzz->pid > 0) {
392 LOG_I("PID=%d specified, lowering maximum number of concurrent threads to 1", hfuzz->pid);
393 hfuzz->threadsMax = 1;
394 }
395
396 LOG_I("inputFile '%s', nullifyStdio: %s, fuzzStdin: %s, saveUnique: %s, flipRate: %lf, "
397 "externalCommand: '%s', tmOut: %ld, mutationsMax: %ld, threadsMax: %ld, fileExtn '%s', ignoreAddr: %p, "
398 "memoryLimit: 0x%" PRIx64 "(MiB), fuzzExe: '%s', fuzzedPid: %d",
399 hfuzz->inputFile,
400 cmdlineYesNo(hfuzz->nullifyStdio), cmdlineYesNo(hfuzz->fuzzStdin),
401 cmdlineYesNo(hfuzz->saveUnique), hfuzz->flipRate,
402 hfuzz->externalCommand == NULL ? "NULL" : hfuzz->externalCommand, hfuzz->tmOut,
403 hfuzz->mutationsMax, hfuzz->threadsMax, hfuzz->fileExtn, hfuzz->ignoreAddr,
404 hfuzz->asLimit, hfuzz->cmdline[0], hfuzz->pid);
405
406 return true;
407}