1 /* -*- mode: C; c-file-style: "gnu" -*- */
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 1.2
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <dbus/dbus.h>
26 #include <sys/types.h>
35 #define DBUS_COMPILATION
36 #include <dbus/dbus-string.h>
37 #include <dbus/dbus-internals.h>
38 #include <dbus/dbus-test.h>
39 #include <dbus/dbus-marshal.h>
40 #undef DBUS_COMPILATION
42 static DBusString failure_dir;
43 static int total_attempts;
44 static int failures_this_iteration;
47 random_int_in_range (int start,
50 /* such elegant math */
58 _dbus_assert (end > start);
60 gap = end - start - 1; /* -1 to not include "end" */
61 v_double = ((double)start) + (((double)rand ())/RAND_MAX) * gap;
69 fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
75 fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
80 /* printf (" %d of [%d,%d)\n", v, start, end); */
86 try_mutated_data (const DBusString *data)
91 /* printf (" attempt %d\n", total_attempts); */
97 fprintf (stderr, "fork() failed: %s\n",
105 /* Child, try loading the data */
106 if (!dbus_internal_do_not_use_try_message_data (data, _DBUS_MESSAGE_UNKNOWN))
113 /* Parent, wait for child */
118 if (waitpid (pid, &status, 0) < 0)
120 fprintf (stderr, "waitpid() failed: %s\n", strerror (errno));
127 if (!_dbus_string_init (&filename) ||
128 !_dbus_string_copy (&failure_dir, 0,
130 !_dbus_string_append_byte (&filename, '/'))
132 fprintf (stderr, "out of memory\n");
136 _dbus_string_append_int (&filename, total_attempts);
138 if (WIFEXITED (status))
140 if (WEXITSTATUS (status) != 0)
142 _dbus_string_append (&filename, "-exited-");
143 _dbus_string_append_int (&filename, WEXITSTATUS (status));
147 else if (WIFSIGNALED (status))
149 _dbus_string_append (&filename, "signaled-");
150 _dbus_string_append_int (&filename, WTERMSIG (status));
158 _dbus_string_append (&filename, ".message-raw");
160 printf ("Child failed, writing %s\n", _dbus_string_get_const_data (&filename));
162 dbus_error_init (&error);
163 if (!_dbus_string_save_to_file (data, &filename, &error))
165 fprintf (stderr, "Failed to save failed message data: %s\n",
167 dbus_error_free (&error);
168 exit (1); /* so we can see the seed that was printed out */
171 failures_this_iteration += 1;
173 _dbus_string_free (&filename);
179 _dbus_string_free (&filename);
184 _dbus_assert_not_reached ("should not be reached");
189 randomly_shorten_or_lengthen (const DBusString *orig_data,
194 if (orig_data != mutated)
196 _dbus_string_set_length (mutated, 0);
198 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
199 _dbus_assert_not_reached ("out of mem");
202 if (_dbus_string_get_length (mutated) == 0)
203 delta = random_int_in_range (0, 10);
205 delta = random_int_in_range (- _dbus_string_get_length (mutated),
206 _dbus_string_get_length (mutated) * 3);
209 _dbus_string_shorten (mutated, - delta);
214 i = _dbus_string_get_length (mutated);
215 if (!_dbus_string_lengthen (mutated, delta))
216 _dbus_assert_not_reached ("couldn't lengthen string");
218 while (i < _dbus_string_get_length (mutated))
220 _dbus_string_set_byte (mutated,
222 random_int_in_range (0, 256));
229 randomly_change_one_byte (const DBusString *orig_data,
234 if (orig_data != mutated)
236 _dbus_string_set_length (mutated, 0);
238 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
239 _dbus_assert_not_reached ("out of mem");
242 if (_dbus_string_get_length (mutated) == 0)
245 i = random_int_in_range (0, _dbus_string_get_length (mutated));
247 _dbus_string_set_byte (mutated, i,
248 random_int_in_range (0, 256));
252 randomly_remove_one_byte (const DBusString *orig_data,
257 if (orig_data != mutated)
259 _dbus_string_set_length (mutated, 0);
261 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
262 _dbus_assert_not_reached ("out of mem");
265 if (_dbus_string_get_length (mutated) == 0)
268 i = random_int_in_range (0, _dbus_string_get_length (mutated));
270 _dbus_string_delete (mutated, i, 1);
275 randomly_add_one_byte (const DBusString *orig_data,
280 if (orig_data != mutated)
282 _dbus_string_set_length (mutated, 0);
284 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
285 _dbus_assert_not_reached ("out of mem");
288 i = random_int_in_range (0, _dbus_string_get_length (mutated));
290 _dbus_string_insert_byte (mutated, i,
291 random_int_in_range (0, 256));
295 randomly_modify_length (const DBusString *orig_data,
304 if (orig_data != mutated)
306 _dbus_string_set_length (mutated, 0);
308 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
309 _dbus_assert_not_reached ("out of mem");
312 if (_dbus_string_get_length (mutated) < 12)
315 d = _dbus_string_get_const_data (mutated);
317 if (!(*d == DBUS_LITTLE_ENDIAN ||
318 *d == DBUS_BIG_ENDIAN))
323 i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
324 i = _DBUS_ALIGN_VALUE (i, 4);
326 orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
328 delta = random_int_in_range (-10, 10);
330 _dbus_marshal_set_uint32 (mutated, byte_order, i,
331 (unsigned) (orig + delta));
335 randomly_set_extreme_ints (const DBusString *orig_data,
342 static int which = 0;
343 unsigned int extreme_ints[] = {
362 if (orig_data != mutated)
364 _dbus_string_set_length (mutated, 0);
366 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
367 _dbus_assert_not_reached ("out of mem");
370 if (_dbus_string_get_length (mutated) < 12)
373 d = _dbus_string_get_const_data (mutated);
375 if (!(*d == DBUS_LITTLE_ENDIAN ||
376 *d == DBUS_BIG_ENDIAN))
381 i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
382 i = _DBUS_ALIGN_VALUE (i, 4);
384 orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
386 which = random_int_in_range (0, _DBUS_N_ELEMENTS (extreme_ints));
388 _dbus_assert (which >= 0);
389 _dbus_assert (which < _DBUS_N_ELEMENTS (extreme_ints));
391 _dbus_marshal_set_uint32 (mutated, byte_order, i,
392 extreme_ints[which]);
396 randomly_change_one_type (const DBusString *orig_data,
402 if (orig_data != mutated)
404 _dbus_string_set_length (mutated, 0);
406 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
407 _dbus_assert_not_reached ("out of mem");
410 if (_dbus_string_get_length (mutated) == 0)
413 len = _dbus_string_get_length (mutated);
414 i = random_int_in_range (0, len);
416 /* Look for a type starting at a random location,
417 * and replace with a different type
422 b = _dbus_string_get_byte (mutated, i);
423 if (b > DBUS_TYPE_INVALID && b <= DBUS_TYPE_LAST)
425 _dbus_string_set_byte (mutated, i,
426 random_int_in_range (DBUS_TYPE_INVALID,
427 DBUS_TYPE_LAST + 1));
434 static int times_we_did_each_thing[6] = { 0, };
437 randomly_do_n_things (const DBusString *orig_data,
442 void (* functions[]) (const DBusString *orig_data,
443 DBusString *mutated) =
445 randomly_shorten_or_lengthen,
446 randomly_change_one_byte,
447 randomly_add_one_byte,
448 randomly_remove_one_byte,
449 randomly_modify_length,
450 randomly_set_extreme_ints,
451 randomly_change_one_type
454 _dbus_string_set_length (mutated, 0);
456 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
457 _dbus_assert_not_reached ("out of mem");
464 which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions));
466 (* functions[which]) (mutated, mutated);
467 times_we_did_each_thing[which] += 1;
474 find_breaks_based_on (const DBusString *filename,
476 DBusMessageValidity expected_validity,
479 DBusString orig_data;
481 const char *filename_c;
485 filename_c = _dbus_string_get_const_data (filename);
489 if (!_dbus_string_init (&orig_data))
490 _dbus_assert_not_reached ("could not allocate string\n");
492 if (!_dbus_string_init (&mutated))
493 _dbus_assert_not_reached ("could not allocate string\n");
495 if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
498 fprintf (stderr, "could not load file %s\n", filename_c);
502 printf (" changing one random byte 100 times\n");
506 randomly_change_one_byte (&orig_data, &mutated);
507 try_mutated_data (&mutated);
512 printf (" changing length 50 times\n");
516 randomly_modify_length (&orig_data, &mutated);
517 try_mutated_data (&mutated);
522 printf (" removing one byte 50 times\n");
526 randomly_remove_one_byte (&orig_data, &mutated);
527 try_mutated_data (&mutated);
532 printf (" adding one byte 50 times\n");
536 randomly_add_one_byte (&orig_data, &mutated);
537 try_mutated_data (&mutated);
542 printf (" changing ints to boundary values 50 times\n");
546 randomly_set_extreme_ints (&orig_data, &mutated);
547 try_mutated_data (&mutated);
552 printf (" changing typecodes 50 times\n");
556 randomly_change_one_type (&orig_data, &mutated);
557 try_mutated_data (&mutated);
562 printf (" changing message length 15 times\n");
566 randomly_shorten_or_lengthen (&orig_data, &mutated);
567 try_mutated_data (&mutated);
572 printf (" randomly making 2 of above modifications 42 times\n");
576 randomly_do_n_things (&orig_data, &mutated, 2);
577 try_mutated_data (&mutated);
582 printf (" randomly making 3 of above modifications 42 times\n");
586 randomly_do_n_things (&orig_data, &mutated, 3);
587 try_mutated_data (&mutated);
592 printf (" randomly making 4 of above modifications 42 times\n");
596 randomly_do_n_things (&orig_data, &mutated, 4);
597 try_mutated_data (&mutated);
606 _dbus_string_free (&orig_data);
607 _dbus_string_free (&mutated);
609 /* FALSE means end the whole process */
614 get_random_seed (void)
623 if (!_dbus_string_init (&bytes))
626 fd = open ("/dev/urandom", O_RDONLY);
630 if (_dbus_read (fd, &bytes, 4) != 4)
635 s = _dbus_string_get_const_data (&bytes);
637 seed = * (unsigned int*) s;
644 fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n");
646 _dbus_get_current_time (NULL, &tv_usec);
652 _dbus_string_free (&bytes);
661 const char *test_data_dir;
662 const char *failure_dir_c;
663 int total_failures_found;
666 test_data_dir = argv[1];
669 fprintf (stderr, "Must specify a top_srcdir/test/data directory\n");
673 total_failures_found = 0;
676 if (!_dbus_string_init (&failure_dir))
679 /* so you can leave it overnight safely */
680 #define MAX_FAILURES 1000
682 while (total_failures_found < MAX_FAILURES)
686 failures_this_iteration = 0;
688 seed = get_random_seed ();
690 _dbus_string_set_length (&failure_dir, 0);
692 if (!_dbus_string_append (&failure_dir, "failures-"))
695 if (!_dbus_string_append_uint (&failure_dir, seed))
698 failure_dir_c = _dbus_string_get_const_data (&failure_dir);
700 if (mkdir (failure_dir_c, 0700) < 0)
703 fprintf (stderr, "didn't mkdir %s: %s\n",
704 failure_dir_c, strerror (errno));
707 printf ("next seed = %u \ttotal failures %d of %d attempts\n",
708 seed, total_failures_found, total_attempts);
712 if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
713 find_breaks_based_on,
716 fprintf (stderr, "fatal error iterating over message files\n");
717 rmdir (failure_dir_c);
721 printf (" did %d random mutations: %d %d %d %d %d %d\n",
722 _DBUS_N_ELEMENTS (times_we_did_each_thing),
723 times_we_did_each_thing[0],
724 times_we_did_each_thing[1],
725 times_we_did_each_thing[2],
726 times_we_did_each_thing[3],
727 times_we_did_each_thing[4],
728 times_we_did_each_thing[5]);
730 printf ("Found %d failures with seed %u stored in %s\n",
731 failures_this_iteration, seed, failure_dir_c);
733 total_failures_found += failures_this_iteration;
735 rmdir (failure_dir_c); /* does nothing if non-empty */