lib_LTLIBRARIES =
noinst_LTLIBRARIES =
sbin_PROGRAMS =
+noinst_PROGRAMS =
check_PROGRAMS =
dist_man_MANS =
CLEANFILES =
src_nft_SOURCES = src/main.c
-if BUILD_AFL
-src_nft_SOURCES += src/afl++.c
-endif
-
if BUILD_CLI
src_nft_SOURCES += src/cli.c
endif
###############################################################################
+if BUILD_AFL
+noinst_PROGRAMS += tools/nft-afl
+
+tools_nft_afl_SOURCES = tools/nft-afl.c
+tools_nft_afl_SOURCES += -I$(srcdir)/include
+tools_nft_afl_LDADD = src/libnftables.la
+endif
+
+###############################################################################
+
if BUILD_MAN
dist_man_MANS += \
NFT_AFL_FUZZER_NETLINK_RW,
};
-static inline void nft_afl_print_build_info(FILE *fp)
-{
-#if HAVE_FUZZER_BUILD && defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
- fprintf(fp, "\nWARNING: BUILT WITH FUZZER SUPPORT AND AFL INSTRUMENTATION\n");
-#elif defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
- fprintf(fp, "\nWARNING: BUILT WITH AFL INSTRUMENTATION\n");
-#elif HAVE_FUZZER_BUILD
- fprintf(fp, "\nWARNING: BUILT WITH FUZZER SUPPORT BUT NO AFL INSTRUMENTATION\n");
-#endif
-}
-
-#if HAVE_FUZZER_BUILD
-extern int nft_afl_init(struct nft_ctx *nft, enum nft_afl_fuzzer_stage s);
-extern int nft_afl_main(struct nft_ctx *nft);
-#else
-static inline int nft_afl_main(struct nft_ctx *ctx)
-{
- return -1;
-}
-static inline int nft_afl_init(struct nft_ctx *nft, enum nft_afl_fuzzer_stage s){ return -1; }
-#endif
-
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-#define __AFL_INIT() do { } while (0)
-#endif
#endif
+++ /dev/null
-/*
- * 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;
-}
#include <nftables/libnftables.h>
#include <utils.h>
#include <cli.h>
-#include <afl++.h>
static struct nft_ctx *nft;
OPT_TERSE = 't',
OPT_OPTIMIZE = 'o',
OPT_INVALID = '?',
-
-#if HAVE_FUZZER_BUILD
- /* keep last */
- OPT_FUZZER = 254
-#endif
};
struct nft_opt {
"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]))
print_option(&nft_options[i]);
fputs("\n", stdout);
- nft_afl_print_build_info(stdout);
}
static void show_version(void)
PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME,
cli, json, minigmp, xt);
- nft_afl_print_build_info(stdout);
}
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;
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;
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;
}
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;
--disable-shared, so that libnftables is instrumented too.
--enable-fuzzer
---enable-fuzzer is not strictly required, you can run normal nft builds under
-afl-fuzz too. But the execution speed will be much slower.
+--enable-fuzzer provides tools/nft-afl that allows more fine-grained control
+nft-afl provides a few options to guide the fuzzing process, some are shared
+with nft binary:
---enable-fuzzer also provides the nft --fuzzer command line option that allows
-more fine-grained control over what code paths should be covered by the fuzzing
-process.
+--check
-When fuzzing in this mode, then each new input passes through the following
-processing stages:
+Prevents the fuzzer-generated rulesets from being committed to the kernel.
+
+--random-outflags
+
+Periodically alter output behaviour by enabling or disabling --terse,
+--numeric, --echo and so on.
+
+--json
+
+Format output in JSON
+
+--fuzzer <stage>
+
+Instruct nft-afl to stop fuzzing after reaching the given stage. Stages are:
1: 'parser':
Only run / exercise the flex/bison parser.
3: 'netlink-ro':
Also build/serialize the ruleset into netlink-commands to send to the
kernel, but omit the final write so the kernel will not see the message.
+ This is the default mode.
4: 'netlink-rw':
Same as 3 but the message will be sent to the kernel.
All --fuzzer modes EXCEPT 'netlink-rw' do imply --check as these modes never
alter state in the kernel.
-In rw mode, before each input, nft checks the kernel "taint" status as provided
+Before each input, nft-afl checks the kernel "taint" status as provided
by "/proc/sys/kernel/tainted". If this is non-zero, fuzzing stops.
-To run nftables under afl++, run nftables like this:
+To run libnftables under afl++, run nft-afl like this:
unshare -n \
afl-fuzz -g 16 -G 2000 -t 5000 -i tests/afl++/in -o tests/afl++/out \
- -- src/nft --fuzzer <arg>
+ -- tools/nft-afl
-arg should be either "netlink-ro" (if you only want to exercise nft userspace)
-or "netlink-rw" (if you want to test kernel code paths too).
+arg should be either "netlink-ro" (if you only want to exercise libnftables
+userspace) or "netlink-rw" (if you want to test kernel code paths too).
Its also a good idea to do this from tmux/screen so you can disconnect/reattach
later. You can also spawn multiple instances.
set -e
ME=$(dirname $0)
-SRC_NFT="$(dirname $0)/../../src/nft"
+SRC_NFT="$(dirname $0)/../../tools/nft-afl"
cd $ME/../..
echo "built initial set of inputs to fuzz from shell test case dump files."
echo "sample invocations:"
-echo "unshare -n afl-fuzz -g 16 -G 2000 -t 5000 -i tests/afl++/in -o tests/afl++/out -- src/nft --fuzzer netlink-ro"
-echo "unshare -n afl-fuzz -g 16 -G 2000 -t 5000 -i tests/afl++/in-json -o tests/afl++/out -- src/nft -j --check --fuzzer netlink-rw"
+echo "unshare -n afl-fuzz -g 16 -G 2000 -t 5000 -i tests/afl++/in -o tests/afl++/out -- "$SRC_NFT" --fuzzer netlink-ro"
+echo "unshare -n afl-fuzz -g 16 -G 2000 -t 5000 -i tests/afl++/in-json -o tests/afl++/out -- "$SRC_NFT" -j --check --fuzzer netlink-rw"
nftables.service
+*.o
+.deps/
+.libs/
+nft-afl
--- /dev/null
+/*
+ * 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 <stdbool.h>
+
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <sys/random.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_INIT() do { } while (0)
+#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;
+
+enum nft_fuzzer_opts {
+ OPT_HELP = 'h',
+ OPT_CHECK = 'c',
+ OPT_JSON = 'j',
+ OPT_INVALID = '?',
+
+ /* --long only */
+ OPT_FUZZER = 1,
+ OPT_RANDOUTFLAGS = 2,
+};
+
+static const char optstring[] = "hcj";
+
+static struct option options[] = {
+ {
+ .name = "help",
+ .val = OPT_HELP,
+ }, {
+ .name = "check",
+ .val = OPT_CHECK,
+ }, {
+ .name = "json",
+ .val = OPT_JSON,
+ }, {
+ .name = "fuzzer",
+ .val = OPT_FUZZER,
+ .has_arg = 1,
+ }, {
+ .name = "random-outflags",
+ .val = OPT_RANDOUTFLAGS,
+ }, {
+ }
+};
+
+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 nft_afl_print_build_info(FILE *fp)
+{
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ fprintf(fp, "\nWARNING: BUILT WITH FUZZER SUPPORT AND AFL INSTRUMENTATION\n");
+#else
+ fprintf(fp, "\nWARNING: BUILT WITH FUZZER SUPPORT BUT NO AFL INSTRUMENTATION\n");
+#endif
+}
+
+static void nft_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);
+}
+
+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 void nft_afl_run_cmd(struct nft_ctx *ctx, const char *input_cmd)
+{
+ if (kernel_is_tainted())
+ return;
+
+ 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;
+ case NFT_AFL_FUZZER_NETLINK_RW:
+ break;
+ }
+
+ fault_inject_enable(&state);
+ nft_run_cmd_from_buffer(ctx, input_cmd);
+ fault_inject_disable(&state);
+
+ 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;
+}
+
+static 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
+ unsigned int input_flags;
+
+ 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);
+ }
+
+ input_flags = nft_ctx_input_get_flags(ctx);
+ input_flags |= NFT_CTX_INPUT_NO_DNS;
+ nft_ctx_input_set_flags(ctx, input_flags);
+
+ if (stage < NFT_AFL_FUZZER_NETLINK_RW)
+ nft_ctx_set_dry_run(ctx, true);
+
+ fprintf(stderr, "starting (%s, %s fault injection)", instrumented, state.make_it_fail_fp ? "with" : "no");
+ return 0;
+}
+
+static uint32_t random_u32(void)
+{
+ uint32_t v;
+
+ if (getrandom(&v, sizeof(v), GRND_NONBLOCK) == (ssize_t)sizeof(v))
+ return v;
+
+ v = (uint32_t)time(NULL) + (uint32_t)getpid();
+ srandom(v + random());
+
+ v = random();
+ v += random();
+
+ return v;
+}
+
+static uint32_t random_outflags(void)
+{
+ uint32_t random_value;
+
+ random_value = random_u32();
+
+ /* never enable json automatically, rely on command line for this */
+ return random_value & ~NFT_CTX_OUTPUT_JSON;
+}
+
+static void show_help(const char *name)
+{
+ int i;
+
+ printf("Usage: %s [ options ]\n\nOptions\n", name);
+
+ for (i = 0; i < (int)(sizeof(options) / sizeof(options[0])) - 1; i++) {
+ printf("--%s", options[i].name);
+
+ if (options[i].has_arg)
+ fputs(" <arg>", stdout);
+
+ puts("");
+ }
+
+ puts("");
+ puts("Also see \"nft --help\" for more information on common command line options.");
+}
+
+static void show_help_fuzzer(const char *name)
+{
+ int i;
+
+ show_help(name);
+ puts("");
+
+ for (i = 0; i < (int)(sizeof(fuzzer_stage_param) / sizeof(fuzzer_stage_param[0])); i++)
+ printf("--fuzzer %s\n", fuzzer_stage_param[i].name);
+
+ puts("Hint: combine \"--fuzzer netlink-rw\" with \"--check\" to not apply changes\n");
+}
+
+static 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 */
+ nft_afl_run_cmd(ctx, input);
+ }
+
+ /* afl-fuzz will restart us. */
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ enum nft_afl_fuzzer_stage fuzzer_stage = NFT_AFL_FUZZER_NETLINK_RO;
+ unsigned int json_output_flag = 0;
+ bool random_output_flags = false;
+ int ret = EXIT_SUCCESS;
+ struct nft_ctx *nft;
+ unsigned int i;
+
+ nft = nft_ctx_new(NFT_CTX_DEFAULT);
+
+ while (1) {
+ int val = getopt_long(argc, argv, optstring, options, NULL);
+ if (val == -1)
+ break;
+
+ switch (val) {
+ case OPT_HELP:
+ show_help(argv[0]);
+ goto out;
+ case OPT_CHECK:
+ nft_ctx_set_dry_run(nft, true);
+ break;
+ case OPT_FUZZER:
+ 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 (!strcmp(optarg, "help")) {
+ show_help_fuzzer(argv[0]);
+ goto out;
+ }
+
+ if (i == array_size(fuzzer_stage_param)) {
+ fprintf(stderr, "invalid fuzzer stage `%s'\n",
+ optarg);
+ show_help_fuzzer(argv[0]);
+ goto out_fail;
+ }
+ break;
+ case OPT_RANDOUTFLAGS:
+ random_output_flags = true;
+ break;
+ case OPT_JSON:
+#ifdef HAVE_LIBJANSSON
+ json_output_flag = NFT_CTX_OUTPUT_JSON;
+#else
+ fprintf(stderr, "Error: JSON support not compiled-in\n");
+ goto out_fail;
+#endif
+ case OPT_INVALID:
+ nft_afl_exit("Unknown option");
+ goto out_fail;
+ }
+ }
+
+ ret = nft_afl_init(nft, fuzzer_stage);
+ if (ret != 0)
+ nft_afl_exit("cannot initialize");
+
+ __AFL_INIT();
+
+ if (random_output_flags) {
+ unsigned int output_flags = random_outflags();
+
+ nft_ctx_output_set_flags(nft, output_flags | json_output_flag);
+ }
+
+ ret = nft_afl_main(nft);
+ if (ret != 0)
+ nft_afl_exit("fatal error");
+out:
+ nft_ctx_free(nft);
+ return ret;
+out_fail:
+ nft_ctx_free(nft);
+ return EXIT_FAILURE;
+}