/tests/shell/run-tests.sh.log
/tests/shell/run-tests.sh.trs
+# generated by run-afl.sh
+/tests/afl++/in/
+/tests/afl++/nft.dict
+/tests/afl++/out/
+
# Debian package build temporary files
build-stamp
include/linux/netfilter_ipv6.h \
include/linux/netfilter_ipv6/ip6_tables.h \
\
+ include/afl++.h \
include/cache.h \
include/cli.h \
include/cmd.h \
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
])
AM_CONDITIONAL([BUILD_CLI], [test "x$with_cli" != xno])
+AC_ARG_ENABLE([fuzzer],
+ AS_HELP_STRING([--enable-fuzzer], [Enable fuzzer support. NEVER use this unless you work on nftables project]),
+ [enable_fuzzer=yes], [enable_fuzzer=no])
+
+AM_CONDITIONAL([BUILD_AFL], [test "x$enable_fuzzer" = xyes])
+
+HAVE_FUZZER_BUILD=0
+AS_IF([test "x$enable_fuzzer" != xno], [
+ HAVE_FUZZER_BUILD=1
+])
+AC_DEFINE_UNQUOTED([HAVE_FUZZER_BUILD], [$HAVE_FUZZER_BUILD], [Whether to build with fuzzer support])
+
AC_ARG_WITH([xtables], [AS_HELP_STRING([--with-xtables],
[Use libxtables for iptables interaction])],
[], [with_xtables=no])
else
echo " systemd unit: no"
fi
+
+# Do not print "fuzzer support: no", this is development-only.
+AS_IF([test "x$enable_fuzzer" = xyes ], [
+ echo " fuzzer support: yes"
+ ], [ ])
--- /dev/null
+#ifndef _NFT_AFLPLUSPLUS_H_
+#define _NFT_AFLPLUSPLUS_H_
+
+#include <nftables/libnftables.h>
+
+/*
+ * enum nft_afl_fuzzer_stage - current fuzzer stage
+ *
+ * @NFT_AFL_FUZZER_DISABLED: running without --fuzzer
+ * @NFT_AFL_FUZZER_PARSER: only fuzz the parser, do not run eval step.
+ * @NFT_AFL_FUZZER_EVALUATION: continue to evaluation step, if possible.
+ * @NFT_AFL_FUZZER_NETLINK_RO: convert internal representation to netlink buffer but don't send any changes to the kernel.
+ * @NFT_AFL_FUZZER_NETLINK_RW: send the netlink message to kernel for processing.
+ */
+enum nft_afl_fuzzer_stage {
+ NFT_AFL_FUZZER_DISABLED,
+ NFT_AFL_FUZZER_PARSER,
+ NFT_AFL_FUZZER_EVALUATION,
+ NFT_AFL_FUZZER_NETLINK_RO,
+ 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
void *json_root;
json_t *json_echo;
const char *stdin_buf;
+#if HAVE_FUZZER_BUILD
+ int afl_ctx_stage;
+#endif
};
enum nftables_exit_codes {
--- /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 <nft.h>
#include <nftables/libnftables.h>
+#include <afl++.h>
#include <erec.h>
#include <mnl.h>
#include <parser.h>
#include <sys/stat.h>
#include <libgen.h>
+static int do_mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list,
+ uint32_t num_cmds)
+{
+#if HAVE_FUZZER_BUILD
+ if (ctx->nft->afl_ctx_stage &&
+ ctx->nft->afl_ctx_stage < NFT_AFL_FUZZER_NETLINK_RW)
+ return 0;
+#endif
+ return mnl_batch_talk(ctx, err_list, num_cmds);
+}
+
static int nft_netlink(struct nft_ctx *nft,
struct list_head *cmds, struct list_head *msgs)
{
if (list_empty(cmds))
goto out;
+#if HAVE_FUZZER_BUILD
+ if (nft->afl_ctx_stage &&
+ nft->afl_ctx_stage <= NFT_AFL_FUZZER_EVALUATION)
+ goto out;
+#endif
batch_seqnum = mnl_batch_begin(ctx.batch, mnl_seqnum_inc(&seqnum));
+
list_for_each_entry(cmd, cmds, list) {
ctx.seqnum = cmd->seqnum_from = mnl_seqnum_inc(&seqnum);
ret = do_command(&ctx, cmd);
if (!mnl_batch_ready(ctx.batch))
goto out;
- ret = mnl_batch_talk(&ctx, &err_list, num_cmds);
+ ret = do_mnl_batch_talk(&ctx, &err_list, num_cmds);
if (ret < 0) {
if (ctx.maybe_emsgsize && errno == EMSGSIZE) {
netlink_io_error(&ctx, NULL,
rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds,
&indesc_cmdline);
+#if HAVE_FUZZER_BUILD
+ if (nft->afl_ctx_stage == NFT_AFL_FUZZER_PARSER)
+ goto err;
+#endif
parser_rc = rc;
rc = nft_evaluate(nft, &msgs, &cmds);
#include <nftables/libnftables.h>
#include <utils.h>
#include <cli.h>
+#include <afl++.h>
static struct nft_ctx *nft;
IDX_ECHO,
#define IDX_CMD_OUTPUT_START IDX_ECHO
IDX_JSON,
+#if HAVE_FUZZER_BUILD
+ IDX_FUZZER,
+#endif
IDX_DEBUG,
#define IDX_CMD_OUTPUT_END IDX_DEBUG
};
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)
" libxtables: %s\n",
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;
* later) as published by the Free Software Foundation.
*/
+#include <nft.h>
+
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
--- /dev/null
+First you need to install afl++. If your distro doesn't package it, you can
+get it from https://github.com/AFLplusplus/AFLplusplus
+
+Next build and install afl++, this needs llvm/clang installed.
+
+Nftables configue + compile steps:
+
+To get the best results, build nftables with the following options:
+
+CC=afl-clang-lto LD=afl-clang-lto CFLAGS+=-fsanitize=address ./configure \
+ --disable-shared --with-json --without-xtables \
+ --with-cli=readline --enable-fuzzer --disable-man-doc
+
+[ you might want to enable xtables or use a different cli, your choice ].
+
+Important options are:
+--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 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.
+
+When fuzzing in this mode, then each new input passes through the following
+processing stages:
+
+1: 'parser':
+ Only run / exercise the flex/bison parser.
+
+2: 'eval': stop after the evaluation phase.
+ This attempts to build a complete ruleset in memory, does
+ symbol resolution, adds needed shift/masks to payload instructions
+ etc.
+
+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.
+
+4: 'netlink-rw':
+ Same as 3 but the message will be sent to the kernel.
+ You can combine this option with the '--check' option to send data to the
+ kernel but without committing any changes.
+ Unlike 3), even when combined with '--check', this option can still trigger
+ a kernel crash if there are bugs in the kernel, e.g. during the
+ valiation / transaction / abort stages.
+ When using this without '--check', remember to lauch nft in its own network
+ namespace to prevent VM connectivity loss due to committed 'drop' rules.
+
+Use 'netlink-ro' if you want to prevent nft from ever submitting any
+changes to the kernel or if you are only interested in fuzzing nftables
+and its libraries.
+
+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
+by "/proc/sys/kernel/tainted". If this is non-zero, fuzzing stops.
+
+To run nftables under afl++, run nftables like this:
+
+unshare -n \
+ afl-fuzz -g 16 -G 2000 -t 5000 -i tests/afl++/in -o tests/afl++/out \
+ -- src/nft --fuzzer <arg>
+
+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).
+
+Its also a good idea to do this from tmux/screen so you can disconnect/reattach
+later. You can also spawn multiple instances.
+In that case, add the '-M' option to afl-fuzz for the first instance you start,
+and '-S' for subsequent secondary instances.
+
+This expects a unique directory name as argument, so interesting findings
+from the different instances are cleary separated.
+
+With above default options, outputs will be in 'tests/afl++/out/<variantname>'.
+Please see the afl++ docs for more information about this.
+
+You can use tests/afl++/run-afl.sh script to autogenerate an initial set of valid
+inputs that the fuzzer can start from.
+
+Use
+
+sysctl -f tests/afl++/afl-sysctl.conf
+
+to enable some fuzzer-beneficial sysctl options.
+
+Kernel config:
+When using the 'netlink-rw' option it is best to also use a debug kernel
+with at least:
+
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+CONFIG_KASAN=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_LOCKDEP=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAILSLAB=y
+CONFIG_DEBUG_KMEMLEAK=y
+
+If you want to sample test coverage, then set
+CONFIG_GCOV_KERNEL=y
+
+echo GCOV_PROFILE := y > net/netfilter/Makefile
+
+or enable CONFIG_GCOV_PROFILE_ALL=y.
--- /dev/null
+kernel.core_pattern=core
+fs.suid_dumpable=1
+kernel.core_uses_pid=0
+kernel.randomize_va_space=0
+kernel.sched_autogroup_enabled=1
--- /dev/null
+#!/bin/bash
+
+# convenience script to check afl reproducers. Many reproducers are
+# variations of the same bug/root cause, so this allows to check if
+# we get same assertion.
+
+pts=$(readlink /proc/self/fd/0)
+
+files=$(find tests/afl++/out/*/crashes/ -type f -name 'id:0000*')
+
+TMP=""
+
+cleanup()
+{
+ rm -rf "$TMP"
+}
+
+trap cleanup EXIT
+
+[ -z "$files" ] && exit 0
+
+TMP=$(mktemp -d)
+
+prompt_del()
+{
+ local f="$1"
+
+ read yn < "$pts"
+ if [ "$yn" = "y" ];then
+ echo delete
+ rm -f "$f"
+ elif [ "$yn" = "n" ]; then
+ echo kept.
+ fi
+}
+
+filter_asan()
+{
+ # retrain the backtrace only.
+ # else check_dup_output won't detect duplicates due to PID
+ # and register dump.
+ grep '#' "$TMP/output" > "$TMP/asan_bt"
+ [ -s "$TMP/asan_bt" ] && mv "$TMP/asan_bt" "$TMP/output"
+}
+
+check_dup_output()
+{
+ local f="$1"
+ local sha=""
+
+ if [ ! -s "$TMP/output" ]; then
+ return 0
+ fi
+
+ sha=$(sha256sum "$TMP/output" | cut -f 1 -d " ")
+
+ if [ -f "$TMP/$sha.output" ]; then
+ local dup=$(cat "$TMP/$sha".filename)
+ echo "Duplicate output, identical splat seen from $dup"
+
+ local s1=$(du -sb "$dup"| cut -f 1)
+ local s2=$(du -sb "$f"| cut -f 1)
+
+ # keep the smaller file.
+ if [ "$s2" -lt "$s1" ];then
+ echo "$f" > "$TMP/$sha".filename
+ f="$dup"
+ fi
+
+ echo "Delete $f?"
+ prompt_del "$f"
+ return 1
+ fi
+
+ # New output.
+ mv "$TMP/output" "$TMP/$sha.output"
+ echo "$f" > "$TMP/$sha.filename"
+ return 0
+}
+
+check_input()
+{
+ local NFT="$1"
+ local f="$2"
+
+ if [ ! -x "$NFT" ] ;then
+ return 1
+ fi
+
+ for arg in "" "-j"; do
+ "$NFT" --check $arg -f - < "$f" > "$TMP/output" 2>&1
+ local rv=$?
+
+ if grep AddressSanitizer "$TMP/output"; then
+ echo "ASAN: \"$NFT $arg -f $f\" exited with $rv"
+ filter_asan "$TMP/output"
+ check_dup_output "$f"
+ return 0
+ fi
+
+ [ $rv -eq 0 ] && continue
+ [ $rv -eq 1 ] && continue
+
+ echo "\"$NFT $arg -f $f\" exited with $rv"
+ check_dup_output "$f"
+ return 0
+ done
+
+ return 1
+}
+
+for f in $files;do
+ check_input src/nft-asan "$f" && continue
+ check_input src/nft "$f" && continue
+
+ echo "$f did not trigger a splat. Delete?"
+ prompt_del "$f"
+done
--- /dev/null
+#!/bin/bash
+
+set -e
+
+ME=$(dirname $0)
+SRC_NFT="$(dirname $0)/../../src/nft"
+
+cd $ME/../..
+
+echo "# nft tokens and common strings, autogenerated via $0" > tests/afl++/nft.dict
+grep %token src/parser_bison.y | while read token comment value; do
+ echo $value
+done | grep -v "[A-Z]" | sort | cut -f 1 -d " " | grep '^"' >> tests/afl++/nft.dict
+
+cat >> tests/afl++/nft.dict <<EOF
+"\"quoted\""
+"{"
+"}"
+"["
+"]"
+";"
+":"
+","
+"."
+"192.168.0.1"
+"65535"
+";;"
+"\x09"
+"\x0d\x0a"
+"\x0a"
+EOF
+
+for d in "tests/afl++/in/" "tests/afl++/in-json" "tests/afl++/out/";do
+ test -d "$d" || mkdir "$d"
+done
+
+find tests/shell/testcases -type f -name '*.nft' | while read filename; do
+ install -m 644 $filename "tests/afl++/in/"$(echo $filename | tr / _)
+done
+find tests/shell/testcases -type f -name '*.json-nft' | while read filename; do
+ install -m 644 $filename "tests/afl++/in-json/"$(echo $filename | tr / _)
+done
+
+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"