]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add framework for fuzzer
authorAlan T. DeKok <aland@freeradius.org>
Tue, 11 Feb 2025 20:56:26 +0000 (15:56 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 11 Feb 2025 20:56:26 +0000 (15:56 -0500)
configure
configure.ac
src/main/all.mk
src/main/fuzzer.c [new file with mode: 0644]
src/main/fuzzer.mk [new file with mode: 0644]

index f1dfa97c44f600ef564fedd2cac9697c639d1ae6..c648f5a550e2f8d412e1e8755c77cd27626a8c46 100755 (executable)
--- a/configure
+++ b/configure
@@ -12492,7 +12492,7 @@ printf "%s\n" "$ax_cv_cc_weverything_flag" >&6; }
           fsanitizeflags=
 
         if test "x$fuzzer" = "xyes" && test "x$ax_cv_cc_clang" = "xyes"; then
-                fsanitizeflags="$fsanitizeflags,fuzzer"
+                fsanitizeflags="$fsanitizeflags,fuzzer-no-link"
   fi
 
         if test "x$address_sanitizer" = "xyes"; then
index 6f292066466458fda3ec898b16d2d44e324741fb..c3e98cb152a3704713c99fa11794b1b49cf7cdc2 100644 (file)
@@ -1990,7 +1990,7 @@ if test "x$developer" = "xyes"; then
     dnl #
     dnl #  -fsanitize=fuzzer  - Build with fuzzer support
     dnl #
-    fsanitizeflags="$fsanitizeflags,fuzzer"
+    fsanitizeflags="$fsanitizeflags,fuzzer-no-link"
   fi
 
   dnl #
index f3db386a2ad4546bfe0fbbdfe751a410a106c903..0251b85800b882916251bee9e81b5ef6cacf1d0e 100644 (file)
@@ -1,3 +1,3 @@
 SUBMAKEFILES := radclient.mk radiusd.mk radsniff.mk radmin.mk radattr.mk \
        radwho.mk radlast.mk radtest.mk radzap.mk checkrad.mk radsecret.mk \
-       libfreeradius-server.mk unittest.mk
+       libfreeradius-server.mk unittest.mk fuzzer.mk
diff --git a/src/main/fuzzer.c b/src/main/fuzzer.c
new file mode 100644 (file)
index 0000000..f20e5cf
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file src/main/fuzzer.c
+ * @brief Functions to fuzz protocol decoding
+ *
+ * @copyright 2019 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+
+/*
+ *     Run from the source directory via:
+ *
+ *     ./build/make/jlibtool --mode=execute ./build/bin/local/fuzzer_radius -D share/dictionary /path/to/corpus/directory/
+ */
+
+static bool                    init = false;
+
+int LLVMFuzzerInitialize(int *argc, char ***argv);
+int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len);
+
+static void exitHandler(void)
+{
+       dict_free();
+}
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+       char const              *dict_dir       = getenv("FR_DICTIONARY_DIR");
+       char const              *debug_lvl_str  = getenv("FR_DEBUG_LVL");
+       char const              *p;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+       char                    *dict_dir_to_free = NULL;
+#endif
+
+       if (!argc || !argv || !*argv) return -1; /* shut up clang scan */
+
+       if (debug_lvl_str) fr_debug_lvl = atoi(debug_lvl_str);
+
+       /*
+        *      Initialise the error stack _before_ we run any
+        *      tests so there's no chance of the memory
+        *      appearing as a leak the first time an error
+        *      is generated.
+        */
+       fr_strerror_printf("fuzz"); /* allocate the pools */
+       fr_strerror_printf(NULL); /* clears the message, leaves the pools */
+
+       /*
+        *      Setup our own internal atexit handler
+        */
+       if (atexit(exitHandler)) {
+               fr_perror("fuzzer: Failed to register exit handler: %s", strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        *      Look for -D dir
+        *
+        *      If found, nuke it from the argument list.
+        */
+       if (!dict_dir) {
+               int i, j;
+
+               for (i = 0; i < *argc - 1; i++) {
+                       p = (*argv)[i];
+
+                       if ((p[0] == '-') && (p[1] == 'D')) {
+                               dict_dir = (*argv)[i + 1];
+
+                               for (j = i + 2; j < *argc; i++, j++) {
+                                       (*argv)[i] = (*argv)[j];
+                               }
+
+                               *argc -= 2;
+                               break;
+                       }
+               }
+       }
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+       /*
+        *      oss-fuzz puts the dictionaries, etc. into subdirectories named after the location of the
+        *      binary.  So we find the directory of the binary, and append "/dict" or "/lib" to find
+        *      dictionaries and libraries.
+        */
+       p = strrchr((*argv)[0], '/');
+       if (p) {
+               if (!dict_dir) {
+                       dict_dir = dict_dir_to_free = talloc_asprintf(NULL, "%.*s/dict", (int) (p - (*argv)[0]), (*argv)[0]);
+                       if (!dict_dir_to_free) fr_exit_now(EXIT_FAILURE);
+               }
+       }
+#endif
+
+       if (!dict_dir) dict_dir = DICTDIR;
+
+       /*
+        *      When jobs=N is specified the fuzzer spawns worker processes via
+        *      a shell. We have removed any -D dictdir argument that were
+        *      supplied, so we pass it to our children via the environment.
+        */
+       if (setenv("FR_DICTIONARY_DIR", dict_dir, 1)) {
+               fprintf(stderr, "Failed to set FR_DICTIONARY_DIR env variable\n");
+               exit(EXIT_FAILURE);
+       }
+
+       if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+               fr_perror("fuzzer");
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        *      Disable hostname lookups, so we don't produce spurious DNS
+        *      queries, and there's no chance of spurious failures if
+        *      it takes a long time to get a response.
+        */
+       fr_hostname_lookups = fr_dns_lookups = false;
+
+       init = true;
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+       talloc_free(dict_dir_to_free);
+#endif
+
+       return 1;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
+{
+       RADIUS_PACKET           *packet;
+
+       if (!init) LLVMFuzzerInitialize(NULL, NULL);
+
+       packet = rad_alloc(NULL, false);
+
+       memcpy(&packet->data, &buf, sizeof(buf)); /* const issues */
+       packet->data_len = len;
+
+       (void) rad_decode(packet, NULL, "testing123");
+       if (fr_debug_lvl > 3) vp_printlist(stdout, packet->vps);
+
+       packet->data = NULL;
+       packet->data_len = 0;
+       talloc_free(packet);
+
+       /*
+        *      Clear error messages from the run.  Clearing these
+        *      keeps malloc/free balanced, which helps to avoid the
+        *      fuzzers leak heuristics from firing.
+        */
+       fr_strerror_printf(NULL);
+
+       return 0;
+}
diff --git a/src/main/fuzzer.mk b/src/main/fuzzer.mk
new file mode 100644 (file)
index 0000000..eda6cf9
--- /dev/null
@@ -0,0 +1,20 @@
+TARGET         := fuzzer
+SOURCES                := fuzzer.c
+
+SRC_CFLAGS     := -fsanitize=fuzzer
+TGT_LDFLAGS    := -fsanitize=fuzzer
+
+TGT_INSTALLDIR  :=
+TGT_LDLIBS     := $(LIBS)
+TGT_PREREQS    := libfreeradius-radius.a
+
+fuzzer.run: $(BUILD_DIR)/bin/fuzzer
+       ${Q}./build/make/jlibtool --mode=execute ./build/bin/local/fuzzer \
+       -artifact_prefix=src/tests/fuzzer/ \
+       -max_len=512 \
+       -D share \
+       src/tests/fuzzer
+
+.PHONY: fuzzer.help
+fuzzer.help:
+       @echo ./build/make/jlibtool --mode=execute ./build/bin/local/fuzzer -max_len=512 -D share src/tests/fuzzer