summaryrefslogtreecommitdiff
path: root/test/break-loader.c
diff options
context:
space:
mode:
authorAnders Carlsson <andersca@codefactory.se>2003-02-05 23:56:39 +0000
committerAnders Carlsson <andersca@codefactory.se>2003-02-05 23:56:39 +0000
commite5631cfe85f125c572f598a414c0659d6ce72684 (patch)
tree9133d17ef2d5b1e808bf2a4bc6d7288ec9830e1f /test/break-loader.c
parent1024a9589f864ac47074c276c8d99541e43f6cf6 (diff)
2003-02-06 Anders Carlsson <andersca@codefactory.se>
* dbus/Makefile.am: * dbus/dbus-break-loader.c: * test/Makefile.am: * test/break-loader.c: Move dbus-break-loader to test/ and rename it to break-loader.
Diffstat (limited to 'test/break-loader.c')
-rw-r--r--test/break-loader.c595
1 files changed, 595 insertions, 0 deletions
diff --git a/test/break-loader.c b/test/break-loader.c
new file mode 100644
index 00000000..95ca808e
--- /dev/null
+++ b/test/break-loader.c
@@ -0,0 +1,595 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-break-loader.c Program to find byte streams that break the message loader
+ *
+ * Copyright (C) 2003 Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <dbus/dbus.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <string.h>
+
+#define DBUS_COMPILATION
+#include <dbus/dbus-string.h>
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-test.h>
+#include <dbus/dbus-marshal.h>
+#undef DBUS_COMPILATION
+
+static DBusString failure_dir;
+static int total_attempts;
+static int failures_this_iteration;
+
+static int
+random_int_in_range (int start,
+ int end)
+{
+ /* such elegant math */
+ double gap;
+ double v_double;
+ int v;
+
+ if (start == end)
+ return start;
+
+ _dbus_assert (end > start);
+
+ gap = end - start - 1; /* -1 to not include "end" */
+ v_double = ((double)start) + (((double)rand ())/RAND_MAX) * gap;
+ if (v_double < 0.0)
+ v = (v_double - 0.5);
+ else
+ v = (v_double + 0.5);
+
+ if (v < start)
+ {
+ fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
+ v, start, end);
+ v = start;
+ }
+ else if (v >= end)
+ {
+ fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
+ v, start, end);
+ v = end - 1;
+ }
+
+ /* printf (" %d of [%d,%d)\n", v, start, end); */
+
+ return v;
+}
+
+static dbus_bool_t
+try_mutated_data (const DBusString *data)
+{
+ int pid;
+
+ total_attempts += 1;
+ /* printf (" attempt %d\n", total_attempts); */
+
+ pid = fork ();
+
+ if (pid < 0)
+ {
+ fprintf (stderr, "fork() failed: %s\n",
+ strerror (errno));
+ exit (1);
+ return FALSE;
+ }
+
+ if (pid == 0)
+ {
+ /* Child, try loading the data */
+ if (!dbus_internal_do_not_use_try_message_data (data, _DBUS_MESSAGE_UNKNOWN))
+ exit (1);
+ else
+ exit (0);
+ }
+ else
+ {
+ /* Parent, wait for child */
+ int status;
+ DBusString filename;
+ dbus_bool_t failed;
+
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ fprintf (stderr, "waitpid() failed: %s\n", strerror (errno));
+ exit (1);
+ return FALSE;
+ }
+
+ failed = FALSE;
+
+ if (!_dbus_string_init (&filename, _DBUS_INT_MAX) ||
+ !_dbus_string_copy (&failure_dir, 0,
+ &filename, 0) ||
+ !_dbus_string_append_byte (&filename, '/'))
+ {
+ fprintf (stderr, "out of memory\n");
+ exit (1);
+ }
+
+ _dbus_string_append_int (&filename, total_attempts);
+
+ if (WIFEXITED (status))
+ {
+ if (WEXITSTATUS (status) != 0)
+ {
+ _dbus_string_append (&filename, "-exited-");
+ _dbus_string_append_int (&filename, WEXITSTATUS (status));
+ failed = TRUE;
+ }
+ }
+ else if (WIFSIGNALED (status))
+ {
+ _dbus_string_append (&filename, "signaled-");
+ _dbus_string_append_int (&filename, WTERMSIG (status));
+ failed = TRUE;
+ }
+
+ if (failed)
+ {
+ const char *filename_c;
+ DBusResultCode result;
+
+ _dbus_string_append (&filename, ".message-raw");
+
+ _dbus_string_get_const_data (&filename, &filename_c);
+ printf ("Child failed, writing %s\n",
+ filename_c);
+
+ result = _dbus_string_save_to_file (data, &filename);
+
+ if (result != DBUS_RESULT_SUCCESS)
+ {
+ fprintf (stderr, "Failed to save failed message data: %s\n",
+ dbus_result_to_string (result));
+ exit (1); /* so we can see the seed that was printed out */
+ }
+
+ failures_this_iteration += 1;
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+ }
+
+ _dbus_assert_not_reached ("should not be reached");
+ return TRUE;
+}
+
+static void
+randomly_shorten_or_lengthen (const DBusString *orig_data,
+ DBusString *mutated)
+{
+ int delta;
+
+ if (orig_data != mutated)
+ {
+ _dbus_string_set_length (mutated, 0);
+
+ if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+ _dbus_assert_not_reached ("out of mem");
+ }
+
+ if (_dbus_string_get_length (mutated) == 0)
+ delta = random_int_in_range (0, 10);
+ else
+ delta = random_int_in_range (- _dbus_string_get_length (mutated),
+ _dbus_string_get_length (mutated) * 3);
+
+ if (delta < 0)
+ _dbus_string_shorten (mutated, - delta);
+ else if (delta > 0)
+ {
+ int i = 0;
+
+ i = _dbus_string_get_length (mutated);
+ if (!_dbus_string_lengthen (mutated, delta))
+ _dbus_assert_not_reached ("couldn't lengthen string");
+
+ while (i < _dbus_string_get_length (mutated))
+ {
+ _dbus_string_set_byte (mutated,
+ i,
+ random_int_in_range (0, 256));
+ ++i;
+ }
+ }
+}
+
+static void
+randomly_change_one_byte (const DBusString *orig_data,
+ DBusString *mutated)
+{
+ int i;
+
+ if (orig_data != mutated)
+ {
+ _dbus_string_set_length (mutated, 0);
+
+ if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+ _dbus_assert_not_reached ("out of mem");
+ }
+
+ if (_dbus_string_get_length (mutated) == 0)
+ return;
+
+ i = random_int_in_range (0, _dbus_string_get_length (mutated));
+
+ _dbus_string_set_byte (mutated, i,
+ random_int_in_range (0, 256));
+}
+
+static void
+randomly_remove_one_byte (const DBusString *orig_data,
+ DBusString *mutated)
+{
+ int i;
+
+ if (orig_data != mutated)
+ {
+ _dbus_string_set_length (mutated, 0);
+
+ if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+ _dbus_assert_not_reached ("out of mem");
+ }
+
+ if (_dbus_string_get_length (mutated) == 0)
+ return;
+
+ i = random_int_in_range (0, _dbus_string_get_length (mutated));
+
+ _dbus_string_delete (mutated, i, 1);
+}
+
+
+static void
+randomly_add_one_byte (const DBusString *orig_data,
+ DBusString *mutated)
+{
+ int i;
+
+ if (orig_data != mutated)
+ {
+ _dbus_string_set_length (mutated, 0);
+
+ if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+ _dbus_assert_not_reached ("out of mem");
+ }
+
+ i = random_int_in_range (0, _dbus_string_get_length (mutated));
+
+ _dbus_string_insert_byte (mutated, i,
+ random_int_in_range (0, 256));
+}
+
+static void
+randomly_modify_length (const DBusString *orig_data,
+ DBusString *mutated)
+{
+ int i;
+ int byte_order;
+ const char *d;
+ dbus_uint32_t orig;
+ int delta;
+
+ if (orig_data != mutated)
+ {
+ _dbus_string_set_length (mutated, 0);
+
+ if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+ _dbus_assert_not_reached ("out of mem");
+ }
+
+ if (_dbus_string_get_length (mutated) < 12)
+ return;
+
+ _dbus_string_get_const_data (mutated, &d);
+
+ if (!(*d == DBUS_LITTLE_ENDIAN ||
+ *d == DBUS_BIG_ENDIAN))
+ return;
+
+ byte_order = *d;
+
+ i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
+ i = _DBUS_ALIGN_VALUE (i, 4);
+
+ orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
+
+ delta = random_int_in_range (-10, 10);
+
+ _dbus_marshal_set_uint32 (mutated, byte_order, i,
+ (unsigned) (orig + delta));
+}
+
+static void
+randomly_do_n_things (const DBusString *orig_data,
+ DBusString *mutated,
+ int n)
+{
+ int i;
+ void (* functions[]) (const DBusString *orig_data,
+ DBusString *mutated) =
+ {
+ randomly_shorten_or_lengthen,
+ randomly_change_one_byte,
+ randomly_add_one_byte,
+ randomly_remove_one_byte,
+ randomly_modify_length
+ };
+
+ _dbus_string_set_length (mutated, 0);
+
+ if (!_dbus_string_copy (orig_data, 0, mutated, 0))
+ _dbus_assert_not_reached ("out of mem");
+
+ i = 0;
+ while (i < n)
+ {
+ int which;
+
+ which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions));
+
+ (* functions[which]) (mutated, mutated);
+
+ ++i;
+ }
+}
+
+static dbus_bool_t
+find_breaks_based_on (const DBusString *filename,
+ dbus_bool_t is_raw,
+ DBusMessageValidity expected_validity,
+ void *data)
+{
+ DBusString orig_data;
+ DBusString mutated;
+ const char *filename_c;
+ dbus_bool_t retval;
+ int i;
+
+ _dbus_string_get_const_data (filename, &filename_c);
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (&orig_data, _DBUS_INT_MAX))
+ _dbus_assert_not_reached ("could not allocate string\n");
+
+ if (!_dbus_string_init (&mutated, _DBUS_INT_MAX))
+ _dbus_assert_not_reached ("could not allocate string\n");
+
+ if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
+ &orig_data))
+ {
+ fprintf (stderr, "could not load file %s\n", filename_c);
+ goto failed;
+ }
+
+ i = 0;
+ while (i < 100)
+ {
+ randomly_change_one_byte (&orig_data, &mutated);
+ try_mutated_data (&mutated);
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < 50)
+ {
+ randomly_modify_length (&orig_data, &mutated);
+ try_mutated_data (&mutated);
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < 50)
+ {
+ randomly_remove_one_byte (&orig_data, &mutated);
+ try_mutated_data (&mutated);
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < 50)
+ {
+ randomly_add_one_byte (&orig_data, &mutated);
+ try_mutated_data (&mutated);
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < 15)
+ {
+ randomly_shorten_or_lengthen (&orig_data, &mutated);
+ try_mutated_data (&mutated);
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < 42)
+ {
+ randomly_do_n_things (&orig_data, &mutated, 2);
+ try_mutated_data (&mutated);
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < 42)
+ {
+ randomly_do_n_things (&orig_data, &mutated, 3);
+ try_mutated_data (&mutated);
+
+ ++i;
+ }
+
+ i = 0;
+ while (i < 42)
+ {
+ randomly_do_n_things (&orig_data, &mutated, 4);
+ try_mutated_data (&mutated);
+
+ ++i;
+ }
+
+ retval = TRUE;
+
+ failed:
+
+ _dbus_string_free (&orig_data);
+ _dbus_string_free (&mutated);
+
+ /* FALSE means end the whole process */
+ return retval;
+}
+
+static unsigned int
+get_random_seed (void)
+{
+ DBusString bytes;
+ unsigned int seed;
+ int fd;
+ const char *s;
+
+ seed = 0;
+
+ if (!_dbus_string_init (&bytes, _DBUS_INT_MAX))
+ exit (1);
+
+ fd = open ("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ goto use_fallback;
+
+ if (_dbus_read (fd, &bytes, 4) != 4)
+ goto use_fallback;
+
+ close (fd);
+
+ _dbus_string_get_const_data (&bytes, &s);
+
+ seed = * (unsigned int*) s;
+ goto out;
+
+ use_fallback:
+ {
+ long tv_usec;
+
+ fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n");
+
+ _dbus_get_current_time (NULL, &tv_usec);
+
+ seed = tv_usec;
+ }
+
+ out:
+ _dbus_string_free (&bytes);
+
+ return seed;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ const char *test_data_dir;
+ const char *failure_dir_c;
+ int total_failures_found;
+
+ if (argc > 1)
+ test_data_dir = argv[1];
+ else
+ {
+ fprintf (stderr, "Must specify a top_srcdir/test/data directory\n");
+ return 1;
+ }
+
+ total_failures_found = 0;
+ total_attempts = 0;
+
+ if (!_dbus_string_init (&failure_dir, _DBUS_INT_MAX))
+ return 1;
+
+ /* so you can leave it overnight safely */
+#define MAX_FAILURES 1000
+
+ while (total_failures_found < MAX_FAILURES)
+ {
+ unsigned int seed;
+
+ failures_this_iteration = 0;
+
+ seed = get_random_seed ();
+
+ _dbus_string_set_length (&failure_dir, 0);
+
+ if (!_dbus_string_append (&failure_dir, "failures-"))
+ return 1;
+
+ if (!_dbus_string_append_uint (&failure_dir, seed))
+ return 1;
+
+ _dbus_string_get_const_data (&failure_dir, &failure_dir_c);
+
+ if (mkdir (failure_dir_c, 0700) < 0)
+ {
+ if (errno != EEXIST)
+ fprintf (stderr, "didn't mkdir %s: %s\n",
+ failure_dir_c, strerror (errno));
+ }
+
+ printf ("next seed = %u \ttotal failures %d of %d attempts\n",
+ seed, total_failures_found, total_attempts);
+
+ srand (seed);
+
+ if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
+ find_breaks_based_on,
+ NULL))
+ {
+ fprintf (stderr, "fatal error iterating over message files\n");
+ rmdir (failure_dir_c);
+ return 1;
+ }
+
+ printf ("Found %d failures with seed %u stored in %s\n",
+ failures_this_iteration, seed, failure_dir_c);
+
+ total_failures_found += failures_this_iteration;
+
+ rmdir (failure_dir_c); /* does nothing if non-empty */
+ }
+
+ return 0;
+}