From ed8b9fa89bd25a61c0069c65599e5726353424e3 Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Sun, 8 May 2022 19:09:49 +0200 Subject: [PATCH] dfuzzer: allow specifying a custom set of input strings --- .github/workflows/run-tests.sh | 13 +++++++++ man/dfuzzer.xml | 8 ++++++ src/dfuzzer-test-server.c | 13 ++++++++- src/dfuzzer.c | 14 +++++++++- src/rand.c | 51 ++++++++++++++++++++++++++++++++-- src/rand.h | 5 ++++ 6 files changed, 100 insertions(+), 4 deletions(-) diff --git a/.github/workflows/run-tests.sh b/.github/workflows/run-tests.sh index ad116b8..548ebb8 100755 --- a/.github/workflows/run-tests.sh +++ b/.github/workflows/run-tests.sh @@ -22,6 +22,19 @@ set -e "${dfuzzer[@]}" -s -v -n org.freedesktop.dfuzzerServer -o /org/freedesktop/dfuzzerObject -i org.freedesktop.dfuzzerInterface -t df_complex_sig_1 "${dfuzzer[@]}" -s -v -n org.freedesktop.dfuzzerServer -o /org/freedesktop/dfuzzerObject -i org.freedesktop.dfuzzerInterface -t df_complex_sig_2 +# Crash on a specific string +"${dfuzzer[@]}" -s -v -n org.freedesktop.dfuzzerServer -o /org/freedesktop/dfuzzerObject -i org.freedesktop.dfuzzerInterface -t df_crash_on_leeroy +cat >inputs.txt <<'EOF' +a string +also a string +probably a string +Leeroy Jenkins +you guessed it - also a string +no way this is a string as well +EOF +"${dfuzzer[@]}" -f inputs.txt -s -v -n org.freedesktop.dfuzzerServer -o /org/freedesktop/dfuzzerObject -i org.freedesktop.dfuzzerInterface -t df_crash_on_leeroy && false +rm -f inputs.txt + sudo systemctl stop dfuzzer-test-server # dfuzzer should return 0 by default when services it tests time out diff --git a/man/dfuzzer.xml b/man/dfuzzer.xml index df4ca47..6aa36d5 100644 --- a/man/dfuzzer.xml +++ b/man/dfuzzer.xml @@ -94,6 +94,14 @@ unsuccessfully, fail message is printed with its return value. + + + + + Name of a file with custom dictionary whhich is used as input for fuzzed methods + before generating random data. Currently supports only strings (one per line). + + diff --git a/src/dfuzzer-test-server.c b/src/dfuzzer-test-server.c index c8944d8..edda0a4 100644 --- a/src/dfuzzer-test-server.c +++ b/src/dfuzzer-test-server.c @@ -63,6 +63,9 @@ static const gchar introspection_xml[] = " " " " " " +" " +" " +" " " " " " " " @@ -114,7 +117,15 @@ static void handle_method_call( g_printf("Sending response to Client: [%s]\n", response); } else if (g_strcmp0(method_name, "df_crash") == 0 || g_strcmp0(method_name, "df_variant_crash") == 0) abort(); - else if (g_strcmp0(method_name, "df_hang") == 0) + else if (g_strcmp0(method_name, "df_crash_on_leeroy") == 0) { + gchar *str = NULL; + + g_variant_get(parameters, "(&s)", &str); + if (g_strcmp0(str, "Leeroy Jenkins") == 0) + abort(); + + g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); + } else if (g_strcmp0(method_name, "df_hang") == 0) pause(); else if (g_strcmp0(method_name, "df_noreply") == 0) return; diff --git a/src/dfuzzer.c b/src/dfuzzer.c index 167ec3b..f7d353e 100644 --- a/src/dfuzzer.c +++ b/src/dfuzzer.c @@ -34,6 +34,7 @@ #include "dfuzzer.h" #include "introspection.h" #include "fuzz.h" +#include "rand.h" #include "util.h" @@ -779,6 +780,7 @@ void df_parse_parameters(int argc, char **argv) { "buffer-limit", required_argument, NULL, 'b' }, { "debug", no_argument, NULL, 'd' }, { "command", required_argument, NULL, 'e' }, + { "string-file", required_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, { "interface", required_argument, NULL, 'i' }, { "list", no_argument, NULL, 'l' }, @@ -796,7 +798,7 @@ void df_parse_parameters(int argc, char **argv) {} }; - while ((c = getopt_long(argc, argv, "n:o:i:m:b:t:e:L:x:y:I:sdvlhV", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "n:o:i:m:b:t:e:L:x:y:f:I:sdvlhV", options, NULL)) >= 0) { switch (c) { case 'n': if (strlen(optarg) >= MAXLEN) { @@ -917,6 +919,14 @@ void df_parse_parameters(int argc, char **argv) df_max_iterations = df_min_iterations; + break; + case 'f': + r = df_rand_load_external_dictionary(optarg); + if (r < 0) { + df_fail("Error: failed to load dictionary from file '%s'\n", optarg); + exit(1); + } + break; default: // '?' exit(1); @@ -1137,6 +1147,8 @@ void df_print_help(const char *name) " -I --iterations=ITER Set both the minimum and maximum number of iterations to ITER\n" " See --max-iterations= and --min-iterations= above\n" " -e --command=COMMAND Command/script to execute after each method call.\n" + " -f --dictionary=FILENAME Name of a file with custom dictionary which is used as input\n" + " for fuzzed methods before generating random data.\n" "\nExamples:\n\n" "Test all methods of GNOME Shell. Be verbose.\n" "# %1$s -v -n org.gnome.Shell\n\n" diff --git a/src/rand.c b/src/rand.c index 07d47e7..7e4d5ae 100644 --- a/src/rand.c +++ b/src/rand.c @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include #include #include #include @@ -32,6 +33,7 @@ /** Maximum buffer size for generated strings (in Bytes) */ static size_t df_buf_size; +static struct external_dictionary df_external_dictionary; /** * @function Initializes global flag variables and seeds pseudo-random @@ -49,6 +51,41 @@ void df_rand_init(const long buf_size) df_buf_size = buf_size; } +int df_rand_load_external_dictionary(const char *filename) +{ + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *line = NULL; + char **array = NULL; + size_t allocated = 0, len = 0, i = 0; + ssize_t n; + + assert(filename); + + f = fopen(filename, "r"); + if (!f) + return df_fail_ret(-errno, "Failed to open file '%s': %m\n", filename); + + while ((n = getline(&line, &len, f)) > 0) { + /* Extend the array if we're out of space */ + if (i >= allocated) { + allocated += 10; + array = realloc(array, sizeof(array) * allocated); + if (!array) + return df_oom(); + } + + /* Drop the newline */ + if (line[n - 1] == '\n') + line[n - 1] = 0; + + array[i++] = TAKE_PTR(line); + } + + df_external_dictionary.strings = TAKE_PTR(array); + df_external_dictionary.size = i; + + return 0; +} /** * @return Generated pseudo-random 8-bit unsigned integer value */ @@ -281,11 +318,21 @@ int df_rand_string(gchar **buf, guint64 iteration) _cleanup_(g_freep) gchar *ret = NULL; size_t len; - if (iteration < G_N_ELEMENTS(test_strings)) { + /* If -f/--string-file= was used, use the loaded strings instead of the + * pre-defined ones, before generating random ones. */ + if (df_external_dictionary.size > 0) { + if (iteration < df_external_dictionary.size) { + ret = strdup(df_external_dictionary.strings[iteration]); + if (!ret) + return df_fail_ret(-1, "Could not allocate memory for the random string\n"); + } + } else if (iteration < G_N_ELEMENTS(test_strings)) { ret = strdup(test_strings[iteration]); if (!ret) return df_fail_ret(-1, "Could not allocate memory for the random string\n"); - } else { + } + + if (!ret) { /* Genearate a pseudo-random string length in interval <0, df_buf_size) */ len = (rand() * iteration) % df_buf_size; len = CLAMP(len, 1, df_buf_size); diff --git a/src/rand.h b/src/rand.h index 07baac4..e911500 100644 --- a/src/rand.h +++ b/src/rand.h @@ -34,6 +34,10 @@ /** Maximum length of D-Bus signature string */ #define MAXSIG 255 +struct external_dictionary { + size_t size; + char **strings; +}; /** * @function Initializes global flag variables and seeds pseudo-random @@ -41,6 +45,7 @@ * @param buf_size Maximum buffer size for generated strings (in Bytes) */ void df_rand_init(const long buf_size); +int df_rand_load_external_dictionary(const char *filename); /** * @return Generated pseudo-random 8-bit unsigned integer value