1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-break-loader.c Program to find byte streams that break the message loader
4 * Copyright (C) 2003 Red Hat Inc.
6 * Licensed under the Academic Free License version 2.1
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <dbus/dbus.h>
27 #include <sys/types.h>
36 #define DBUS_COMPILATION
37 #include <dbus/dbus-string.h>
38 #include <dbus/dbus-internals.h>
39 #include <dbus/dbus-test.h>
40 #include <dbus/dbus-marshal-basic.h>
41 #undef DBUS_COMPILATION
43 static DBusString failure_dir;
44 static int total_attempts;
45 static int failures_this_iteration;
48 random_int_in_range (int start,
51 /* such elegant math */
59 _dbus_assert (end > start);
61 gap = end - start - 1; /* -1 to not include "end" */
62 v_double = ((double)start) + (((double)rand ())/RAND_MAX) * gap;
70 fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
76 fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
81 /* printf (" %d of [%d,%d)\n", v, start, end); */
87 try_mutated_data (const DBusString *data)
92 /* printf (" attempt %d\n", total_attempts); */
98 fprintf (stderr, "fork() failed: %s\n",
106 /* Child, try loading the data */
107 if (!dbus_internal_do_not_use_try_message_data (data, _DBUS_MESSAGE_UNKNOWN))
114 /* Parent, wait for child */
119 if (waitpid (pid, &status, 0) < 0)
121 fprintf (stderr, "waitpid() failed: %s\n", strerror (errno));
128 if (!_dbus_string_init (&filename) ||
129 !_dbus_string_copy (&failure_dir, 0,
131 !_dbus_string_append_byte (&filename, '/'))
133 fprintf (stderr, "out of memory\n");
137 _dbus_string_append_int (&filename, total_attempts);
139 if (WIFEXITED (status))
141 if (WEXITSTATUS (status) != 0)
143 _dbus_string_append (&filename, "-exited-");
144 _dbus_string_append_int (&filename, WEXITSTATUS (status));
148 else if (WIFSIGNALED (status))
150 _dbus_string_append (&filename, "signaled-");
151 _dbus_string_append_int (&filename, WTERMSIG (status));
159 _dbus_string_append (&filename, ".message-raw");
161 printf ("Child failed, writing %s\n", _dbus_string_get_const_data (&filename));
163 dbus_error_init (&error);
164 if (!_dbus_string_save_to_file (data, &filename, FALSE, &error))
166 fprintf (stderr, "Failed to save failed message data: %s\n",
168 dbus_error_free (&error);
169 exit (1); /* so we can see the seed that was printed out */
172 failures_this_iteration += 1;
174 _dbus_string_free (&filename);
180 _dbus_string_free (&filename);
185 _dbus_assert_not_reached ("should not be reached");
190 randomly_shorten_or_lengthen (const DBusString *orig_data,
195 if (orig_data != mutated)
197 _dbus_string_set_length (mutated, 0);
199 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
200 _dbus_assert_not_reached ("out of mem");
203 if (_dbus_string_get_length (mutated) == 0)
204 delta = random_int_in_range (0, 10);
206 delta = random_int_in_range (- _dbus_string_get_length (mutated),
207 _dbus_string_get_length (mutated) * 3);
210 _dbus_string_shorten (mutated, - delta);
215 i = _dbus_string_get_length (mutated);
216 if (!_dbus_string_lengthen (mutated, delta))
217 _dbus_assert_not_reached ("couldn't lengthen string");
219 while (i < _dbus_string_get_length (mutated))
221 _dbus_string_set_byte (mutated,
223 random_int_in_range (0, 256));
230 randomly_change_one_byte (const DBusString *orig_data,
235 if (orig_data != mutated)
237 _dbus_string_set_length (mutated, 0);
239 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
240 _dbus_assert_not_reached ("out of mem");
243 if (_dbus_string_get_length (mutated) == 0)
246 i = random_int_in_range (0, _dbus_string_get_length (mutated));
248 _dbus_string_set_byte (mutated, i,
249 random_int_in_range (0, 256));
253 randomly_remove_one_byte (const DBusString *orig_data,
258 if (orig_data != mutated)
260 _dbus_string_set_length (mutated, 0);
262 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
263 _dbus_assert_not_reached ("out of mem");
266 if (_dbus_string_get_length (mutated) == 0)
269 i = random_int_in_range (0, _dbus_string_get_length (mutated));
271 _dbus_string_delete (mutated, i, 1);
276 randomly_add_one_byte (const DBusString *orig_data,
281 if (orig_data != mutated)
283 _dbus_string_set_length (mutated, 0);
285 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
286 _dbus_assert_not_reached ("out of mem");
289 i = random_int_in_range (0, _dbus_string_get_length (mutated));
291 _dbus_string_insert_bytes (mutated, i, 1,
292 random_int_in_range (0, 256));
296 randomly_modify_length (const DBusString *orig_data,
305 if (orig_data != mutated)
307 _dbus_string_set_length (mutated, 0);
309 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
310 _dbus_assert_not_reached ("out of mem");
313 if (_dbus_string_get_length (mutated) < 12)
316 d = _dbus_string_get_const_data (mutated);
318 if (!(*d == DBUS_LITTLE_ENDIAN ||
319 *d == DBUS_BIG_ENDIAN))
324 i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
325 i = _DBUS_ALIGN_VALUE (i, 4);
327 orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
329 delta = random_int_in_range (-10, 10);
331 _dbus_marshal_set_uint32 (mutated, byte_order, i,
332 (unsigned) (orig + delta));
336 randomly_set_extreme_ints (const DBusString *orig_data,
343 static int which = 0;
344 unsigned int extreme_ints[] = {
363 if (orig_data != mutated)
365 _dbus_string_set_length (mutated, 0);
367 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
368 _dbus_assert_not_reached ("out of mem");
371 if (_dbus_string_get_length (mutated) < 12)
374 d = _dbus_string_get_const_data (mutated);
376 if (!(*d == DBUS_LITTLE_ENDIAN ||
377 *d == DBUS_BIG_ENDIAN))
382 i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
383 i = _DBUS_ALIGN_VALUE (i, 4);
385 orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
387 which = random_int_in_range (0, _DBUS_N_ELEMENTS (extreme_ints));
389 _dbus_assert (which >= 0);
390 _dbus_assert (which < _DBUS_N_ELEMENTS (extreme_ints));
392 _dbus_marshal_set_uint32 (mutated, byte_order, i,
393 extreme_ints[which]);
399 const char types[] = {
413 DBUS_TYPE_OBJECT_PATH
416 _dbus_assert (_DBUS_N_ELEMENTS (types) == DBUS_NUMBER_OF_TYPES + 1);
418 return types[ random_int_in_range (0, _DBUS_N_ELEMENTS (types)) ];
422 randomly_change_one_type (const DBusString *orig_data,
428 if (orig_data != mutated)
430 _dbus_string_set_length (mutated, 0);
432 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
433 _dbus_assert_not_reached ("out of mem");
436 if (_dbus_string_get_length (mutated) == 0)
439 len = _dbus_string_get_length (mutated);
440 i = random_int_in_range (0, len);
442 /* Look for a type starting at a random location,
443 * and replace with a different type
448 b = _dbus_string_get_byte (mutated, i);
449 if (dbus_type_is_valid (b))
451 _dbus_string_set_byte (mutated, i, random_type ());
458 static int times_we_did_each_thing[7] = { 0, };
461 randomly_do_n_things (const DBusString *orig_data,
466 void (* functions[]) (const DBusString *orig_data,
467 DBusString *mutated) =
469 randomly_shorten_or_lengthen,
470 randomly_change_one_byte,
471 randomly_add_one_byte,
472 randomly_remove_one_byte,
473 randomly_modify_length,
474 randomly_set_extreme_ints,
475 randomly_change_one_type
478 _dbus_string_set_length (mutated, 0);
480 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
481 _dbus_assert_not_reached ("out of mem");
488 which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions));
490 (* functions[which]) (mutated, mutated);
491 times_we_did_each_thing[which] += 1;
498 find_breaks_based_on (const DBusString *filename,
500 DBusMessageValidity expected_validity,
503 DBusString orig_data;
505 const char *filename_c;
509 filename_c = _dbus_string_get_const_data (filename);
513 if (!_dbus_string_init (&orig_data))
514 _dbus_assert_not_reached ("could not allocate string\n");
516 if (!_dbus_string_init (&mutated))
517 _dbus_assert_not_reached ("could not allocate string\n");
519 if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
522 fprintf (stderr, "could not load file %s\n", filename_c);
526 printf (" changing one random byte 100 times\n");
530 randomly_change_one_byte (&orig_data, &mutated);
531 try_mutated_data (&mutated);
536 printf (" changing length 50 times\n");
540 randomly_modify_length (&orig_data, &mutated);
541 try_mutated_data (&mutated);
546 printf (" removing one byte 50 times\n");
550 randomly_remove_one_byte (&orig_data, &mutated);
551 try_mutated_data (&mutated);
556 printf (" adding one byte 50 times\n");
560 randomly_add_one_byte (&orig_data, &mutated);
561 try_mutated_data (&mutated);
566 printf (" changing ints to boundary values 50 times\n");
570 randomly_set_extreme_ints (&orig_data, &mutated);
571 try_mutated_data (&mutated);
576 printf (" changing typecodes 50 times\n");
580 randomly_change_one_type (&orig_data, &mutated);
581 try_mutated_data (&mutated);
586 printf (" changing message length 15 times\n");
590 randomly_shorten_or_lengthen (&orig_data, &mutated);
591 try_mutated_data (&mutated);
596 printf (" randomly making 2 of above modifications 42 times\n");
600 randomly_do_n_things (&orig_data, &mutated, 2);
601 try_mutated_data (&mutated);
606 printf (" randomly making 3 of above modifications 42 times\n");
610 randomly_do_n_things (&orig_data, &mutated, 3);
611 try_mutated_data (&mutated);
616 printf (" randomly making 4 of above modifications 42 times\n");
620 randomly_do_n_things (&orig_data, &mutated, 4);
621 try_mutated_data (&mutated);
630 _dbus_string_free (&orig_data);
631 _dbus_string_free (&mutated);
633 /* FALSE means end the whole process */
638 get_random_seed (void)
647 if (!_dbus_string_init (&bytes))
650 fd = open ("/dev/urandom", O_RDONLY);
654 if (_dbus_read (fd, &bytes, 4) != 4)
659 s = _dbus_string_get_const_data (&bytes);
661 seed = * (unsigned int*) s;
668 fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n");
670 _dbus_get_current_time (NULL, &tv_usec);
676 _dbus_string_free (&bytes);
685 const char *test_data_dir;
686 const char *failure_dir_c;
687 int total_failures_found;
690 test_data_dir = argv[1];
693 fprintf (stderr, "Must specify a top_srcdir/test/data directory\n");
697 total_failures_found = 0;
700 if (!_dbus_string_init (&failure_dir))
703 /* so you can leave it overnight safely */
704 #define MAX_FAILURES 1000
706 while (total_failures_found < MAX_FAILURES)
710 failures_this_iteration = 0;
712 seed = get_random_seed ();
714 _dbus_string_set_length (&failure_dir, 0);
716 if (!_dbus_string_append (&failure_dir, "failures-"))
719 if (!_dbus_string_append_uint (&failure_dir, seed))
722 failure_dir_c = _dbus_string_get_const_data (&failure_dir);
724 if (mkdir (failure_dir_c, 0700) < 0)
727 fprintf (stderr, "didn't mkdir %s: %s\n",
728 failure_dir_c, strerror (errno));
731 printf ("next seed = %u \ttotal failures %d of %d attempts\n",
732 seed, total_failures_found, total_attempts);
736 if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
737 find_breaks_based_on,
740 fprintf (stderr, "fatal error iterating over message files\n");
741 rmdir (failure_dir_c);
745 printf (" did %d random mutations: %d %d %d %d %d %d %d\n",
746 _DBUS_N_ELEMENTS (times_we_did_each_thing),
747 times_we_did_each_thing[0],
748 times_we_did_each_thing[1],
749 times_we_did_each_thing[2],
750 times_we_did_each_thing[3],
751 times_we_did_each_thing[4],
752 times_we_did_each_thing[5],
753 times_we_did_each_thing[6]);
755 printf ("Found %d failures with seed %u stored in %s\n",
756 failures_this_iteration, seed, failure_dir_c);
758 total_failures_found += failures_this_iteration;
760 rmdir (failure_dir_c); /* does nothing if non-empty */