diff options
| author | Florian Westphal <fw@strlen.de> | 2025-11-17 23:13:49 +0100 |
|---|---|---|
| committer | Florian Westphal <fw@strlen.de> | 2025-11-20 22:16:43 +0100 |
| commit | 32c994f84904e9854d527217ececf0b97d89410d (patch) | |
| tree | db2a1a6468fc1cf9c9c15b08f27dccb6074a2d44 /src | |
| parent | 6cee2d0e7b4dc3274728ae6681d87e356ddcf31a (diff) | |
src: move fuzzer functionality to separate tool
This means some loss of functionality since you can no longer combine
--fuzzer with options like --debug, --define, --include.
On the upside, this adds new --random-outflags mode which will randomly
switch --terse, --numeric, --echo ... on/off.
Update README to reflect this change.
Signed-off-by: Florian Westphal <fw@strlen.de>
Acked-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src')
| -rw-r--r-- | src/afl++.c | 219 | ||||
| -rw-r--r-- | src/main.c | 97 |
2 files changed, 0 insertions, 316 deletions
diff --git a/src/afl++.c b/src/afl++.c deleted file mode 100644 index 79925952..00000000 --- a/src/afl++.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 (or any - * later) as published by the Free Software Foundation. - */ - -#include <nft.h> -#include <stdio.h> - -#include <errno.h> -#include <ctype.h> -#include <limits.h> -#include <fcntl.h> -#include <unistd.h> -#include <time.h> - -#include <sys/stat.h> -#include <sys/wait.h> - -#include <afl++.h> -#include <nftables.h> - -static const char self_fault_inject_file[] = "/proc/self/make-it-fail"; - -#ifdef __AFL_FUZZ_TESTCASE_LEN -/* the below macro gets passed via afl-cc, declares prototypes - * depending on the afl-cc flavor. - */ -__AFL_FUZZ_INIT(); -#else -/* this lets the source compile without afl-clang-fast/lto */ -static unsigned char fuzz_buf[4096]; -static ssize_t fuzz_len; - -#define __AFL_FUZZ_TESTCASE_LEN fuzz_len -#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf -#define __AFL_FUZZ_INIT() do { } while (0) -#define __AFL_LOOP(x) \ - ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0) -#endif - -struct nft_afl_state { - FILE *make_it_fail_fp; -}; - -static struct nft_afl_state state; - -static char *preprocess(unsigned char *input, ssize_t len) -{ - ssize_t real_len = strnlen((char *)input, len); - - if (real_len == 0) - return NULL; - - if (real_len >= len) - input[len - 1] = 0; - - return (char *)input; -} - -static bool kernel_is_tainted(void) -{ - FILE *fp = fopen("/proc/sys/kernel/tainted", "r"); - unsigned int taint; - bool ret = false; - - if (fp) { - if (fscanf(fp, "%u", &taint) == 1 && taint) { - fprintf(stderr, "Kernel is tainted: 0x%x\n", taint); - sleep(3); /* in case we run under fuzzer, don't restart right away */ - ret = true; - } - - fclose(fp); - } - - return ret; -} - -static void fault_inject_write(FILE *fp, unsigned int v) -{ - rewind(fp); - fprintf(fp, "%u\n", v); - fflush(fp); -} - -static void fault_inject_enable(const struct nft_afl_state *state) -{ - if (state->make_it_fail_fp) - fault_inject_write(state->make_it_fail_fp, 1); -} - -static void fault_inject_disable(const struct nft_afl_state *state) -{ - if (state->make_it_fail_fp) - fault_inject_write(state->make_it_fail_fp, 0); -} - -static bool nft_afl_run_cmd(struct nft_ctx *ctx, const char *input_cmd) -{ - if (kernel_is_tainted()) - return false; - - switch (ctx->afl_ctx_stage) { - case NFT_AFL_FUZZER_PARSER: - case NFT_AFL_FUZZER_EVALUATION: - case NFT_AFL_FUZZER_NETLINK_RO: - nft_run_cmd_from_buffer(ctx, input_cmd); - return true; - case NFT_AFL_FUZZER_NETLINK_RW: - break; - } - - fault_inject_enable(&state); - nft_run_cmd_from_buffer(ctx, input_cmd); - fault_inject_disable(&state); - - return kernel_is_tainted(); -} - -static FILE *fault_inject_open(void) -{ - return fopen(self_fault_inject_file, "r+"); -} - -static bool nft_afl_state_init(struct nft_afl_state *state) -{ - state->make_it_fail_fp = fault_inject_open(); - return true; -} - -int nft_afl_init(struct nft_ctx *ctx, enum nft_afl_fuzzer_stage stage) -{ -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - const char instrumented[] = "afl instrumented"; -#else - const char instrumented[] = "no afl instrumentation"; -#endif - nft_afl_print_build_info(stderr); - - if (!nft_afl_state_init(&state)) - return -1; - - ctx->afl_ctx_stage = stage; - - if (state.make_it_fail_fp) { - unsigned int value; - int ret; - - rewind(state.make_it_fail_fp); - ret = fscanf(state.make_it_fail_fp, "%u", &value); - if (ret != 1 || value != 1) { - fclose(state.make_it_fail_fp); - state.make_it_fail_fp = NULL; - } - - /* if its enabled, disable and then re-enable ONLY - * when submitting data to the kernel. - * - * Otherwise even libnftables memory allocations could fail - * which is not what we want. - */ - fault_inject_disable(&state); - } - - fprintf(stderr, "starting (%s, %s fault injection)", instrumented, state.make_it_fail_fp ? "with" : "no"); - return 0; -} - -int nft_afl_main(struct nft_ctx *ctx) -{ - unsigned char *buf; - ssize_t len; - - if (kernel_is_tainted()) - return -1; - - if (state.make_it_fail_fp) { - FILE *fp = fault_inject_open(); - - /* reopen is needed because /proc/self is a symlink, i.e. - * fp refers to parent process, not "us". - */ - if (!fp) { - fprintf(stderr, "Could not reopen %s: %s", self_fault_inject_file, strerror(errno)); - return -1; - } - - fclose(state.make_it_fail_fp); - state.make_it_fail_fp = fp; - } - - buf = __AFL_FUZZ_TESTCASE_BUF; - - while (__AFL_LOOP(UINT_MAX)) { - char *input; - - len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call! - - input = preprocess(buf, len); - if (!input) - continue; - - /* buf is null terminated at this point */ - if (!nft_afl_run_cmd(ctx, input)) - continue; - - /* Kernel is tainted. - * exit() will cause a restart from afl-fuzz. - * Avoid burning cpu cycles in this case. - */ - sleep(1); - } - - /* afl-fuzz will restart us. */ - return 0; -} @@ -21,7 +21,6 @@ #include <nftables/libnftables.h> #include <utils.h> #include <cli.h> -#include <afl++.h> static struct nft_ctx *nft; @@ -87,11 +86,6 @@ enum opt_vals { OPT_TERSE = 't', OPT_OPTIMIZE = 'o', OPT_INVALID = '?', - -#if HAVE_FUZZER_BUILD - /* keep last */ - OPT_FUZZER = 254 -#endif }; struct nft_opt { @@ -149,10 +143,6 @@ static const struct nft_opt nft_options[] = { "Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"), [IDX_OPTIMIZE] = NFT_OPT("optimize", OPT_OPTIMIZE, NULL, "Optimize ruleset"), -#if HAVE_FUZZER_BUILD - [IDX_FUZZER] = NFT_OPT("fuzzer", OPT_FUZZER, "stage", - "fuzzer stage to run (parser, eval, netlink-ro, netlink-rw)"), -#endif }; #define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0])) @@ -243,7 +233,6 @@ static void show_help(const char *name) print_option(&nft_options[i]); fputs("\n", stdout); - nft_afl_print_build_info(stdout); } static void show_version(void) @@ -286,7 +275,6 @@ static void show_version(void) PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME, cli, json, minigmp, xt); - nft_afl_print_build_info(stdout); } static const struct { @@ -327,38 +315,6 @@ static const struct { }, }; -#if HAVE_FUZZER_BUILD -static const struct { - const char *name; - enum nft_afl_fuzzer_stage stage; -} fuzzer_stage_param[] = { - { - .name = "parser", - .stage = NFT_AFL_FUZZER_PARSER, - }, - { - .name = "eval", - .stage = NFT_AFL_FUZZER_EVALUATION, - }, - { - .name = "netlink-ro", - .stage = NFT_AFL_FUZZER_NETLINK_RO, - }, - { - .name = "netlink-rw", - .stage = NFT_AFL_FUZZER_NETLINK_RW, - }, -}; -static void afl_exit(const char *err) -{ - fprintf(stderr, "Error: fuzzer: %s\n", err); - sleep(60); /* assume we're running under afl-fuzz and would be restarted right away */ - exit(EXIT_FAILURE); -} -#else -static inline void afl_exit(const char *err) { } -#endif - static void nft_options_error(int argc, char * const argv[], int pos) { int i; @@ -407,7 +363,6 @@ static bool nft_options_check(int argc, char * const argv[]) int main(int argc, char * const *argv) { const struct option *options = get_options(); - enum nft_afl_fuzzer_stage fuzzer_stage = 0; bool interactive = false, define = false; const char *optstring = get_optstring(); unsigned int output_flags = 0; @@ -549,26 +504,6 @@ int main(int argc, char * const *argv) case OPT_OPTIMIZE: nft_ctx_set_optimize(nft, 0x1); break; -#if HAVE_FUZZER_BUILD - case OPT_FUZZER: - { - unsigned int i; - - for (i = 0; i < array_size(fuzzer_stage_param); i++) { - if (strcmp(fuzzer_stage_param[i].name, optarg)) - continue; - fuzzer_stage = fuzzer_stage_param[i].stage; - break; - } - - if (i == array_size(fuzzer_stage_param)) { - fprintf(stderr, "invalid fuzzer stage `%s'\n", - optarg); - goto out_fail; - } - } - break; -#endif case OPT_INVALID: goto out_fail; } @@ -581,38 +516,6 @@ int main(int argc, char * const *argv) nft_ctx_output_set_flags(nft, output_flags); - if (fuzzer_stage) { - unsigned int input_flags; - - if (filename || define || interactive) - afl_exit("-D/--define, -f/--filename and -i/--interactive are incompatible options"); - - rc = nft_afl_init(nft, fuzzer_stage); - if (rc != 0) - afl_exit("cannot initialize"); - - input_flags = nft_ctx_input_get_flags(nft); - - /* DNS lookups can result in severe fuzzer slowdown */ - input_flags |= NFT_CTX_INPUT_NO_DNS; - nft_ctx_input_set_flags(nft, input_flags); - - if (fuzzer_stage < NFT_AFL_FUZZER_NETLINK_RW) - nft_ctx_set_dry_run(nft, true); - - fprintf(stderr, "Awaiting fuzzer-generated inputs\n"); - } - - __AFL_INIT(); - - if (fuzzer_stage) { - rc = nft_afl_main(nft); - if (rc != 0) - afl_exit("fatal error"); - - return EXIT_SUCCESS; - } - if (optind != argc) { char *buf; |
