blob: 7bfa72273663722896b0dcf3d69f07430cde7162 [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001/* -*- 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
3 *
4 * Copyright (C) 2003 Red Hat Inc.
5 *
6 * Licensed under the Academic Free License version 2.1
7 *
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.
12 *
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.
17 *
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
21 *
22 */
23
24#include <config.h>
25#include <dbus/dbus.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <fcntl.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <errno.h>
33#include <sys/wait.h>
34#include <string.h>
35
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
42
43static DBusString failure_dir;
44static int total_attempts;
45static int failures_this_iteration;
46
47static int
48random_int_in_range (int start,
49 int end)
50{
51 /* such elegant math */
52 double gap;
53 double v_double;
54 int v;
55
56 if (start == end)
57 return start;
58
59 _dbus_assert (end > start);
60
61 gap = end - start - 1; /* -1 to not include "end" */
62 v_double = ((double)start) + (((double)rand ())/RAND_MAX) * gap;
63 if (v_double < 0.0)
64 v = (v_double - 0.5);
65 else
66 v = (v_double + 0.5);
67
68 if (v < start)
69 {
70 fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
71 v, start, end);
72 v = start;
73 }
74 else if (v >= end)
75 {
76 fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
77 v, start, end);
78 v = end - 1;
79 }
80
81 /* printf (" %d of [%d,%d)\n", v, start, end); */
82
83 return v;
84}
85
86static dbus_bool_t
87try_mutated_data (const DBusString *data)
88{
89 int pid;
90
91 total_attempts += 1;
92 /* printf (" attempt %d\n", total_attempts); */
93
94 pid = fork ();
95
96 if (pid < 0)
97 {
98 fprintf (stderr, "fork() failed: %s\n",
99 strerror (errno));
100 exit (1);
101 return FALSE;
102 }
103
104 if (pid == 0)
105 {
106 /* Child, try loading the data */
107 if (!dbus_internal_do_not_use_try_message_data (data, _DBUS_MESSAGE_UNKNOWN))
108 exit (1);
109 else
110 exit (0);
111 }
112 else
113 {
114 /* Parent, wait for child */
115 int status;
116 DBusString filename;
117 dbus_bool_t failed;
118
119 if (waitpid (pid, &status, 0) < 0)
120 {
121 fprintf (stderr, "waitpid() failed: %s\n", strerror (errno));
122 exit (1);
123 return FALSE;
124 }
125
126 failed = FALSE;
127
128 if (!_dbus_string_init (&filename) ||
129 !_dbus_string_copy (&failure_dir, 0,
130 &filename, 0) ||
131 !_dbus_string_append_byte (&filename, '/'))
132 {
133 fprintf (stderr, "out of memory\n");
134 exit (1);
135 }
136
137 _dbus_string_append_int (&filename, total_attempts);
138
139 if (WIFEXITED (status))
140 {
141 if (WEXITSTATUS (status) != 0)
142 {
143 _dbus_string_append (&filename, "-exited-");
144 _dbus_string_append_int (&filename, WEXITSTATUS (status));
145 failed = TRUE;
146 }
147 }
148 else if (WIFSIGNALED (status))
149 {
150 _dbus_string_append (&filename, "signaled-");
151 _dbus_string_append_int (&filename, WTERMSIG (status));
152 failed = TRUE;
153 }
154
155 if (failed)
156 {
157 DBusError error;
158
159 _dbus_string_append (&filename, ".message-raw");
160
161 printf ("Child failed, writing %s\n", _dbus_string_get_const_data (&filename));
162
163 dbus_error_init (&error);
164 if (!_dbus_string_save_to_file (data, &filename, FALSE, &error))
165 {
166 fprintf (stderr, "Failed to save failed message data: %s\n",
167 error.message);
168 dbus_error_free (&error);
169 exit (1); /* so we can see the seed that was printed out */
170 }
171
172 failures_this_iteration += 1;
173
174 _dbus_string_free (&filename);
175
176 return FALSE;
177 }
178 else
179 {
180 _dbus_string_free (&filename);
181 return TRUE;
182 }
183 }
184
185 _dbus_assert_not_reached ("should not be reached");
186 return TRUE;
187}
188
189static void
190randomly_shorten_or_lengthen (const DBusString *orig_data,
191 DBusString *mutated)
192{
193 int delta;
194
195 if (orig_data != mutated)
196 {
197 _dbus_string_set_length (mutated, 0);
198
199 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
200 _dbus_assert_not_reached ("out of mem");
201 }
202
203 if (_dbus_string_get_length (mutated) == 0)
204 delta = random_int_in_range (0, 10);
205 else
206 delta = random_int_in_range (- _dbus_string_get_length (mutated),
207 _dbus_string_get_length (mutated) * 3);
208
209 if (delta < 0)
210 _dbus_string_shorten (mutated, - delta);
211 else if (delta > 0)
212 {
213 int i = 0;
214
215 i = _dbus_string_get_length (mutated);
216 if (!_dbus_string_lengthen (mutated, delta))
217 _dbus_assert_not_reached ("couldn't lengthen string");
218
219 while (i < _dbus_string_get_length (mutated))
220 {
221 _dbus_string_set_byte (mutated,
222 i,
223 random_int_in_range (0, 256));
224 ++i;
225 }
226 }
227}
228
229static void
230randomly_change_one_byte (const DBusString *orig_data,
231 DBusString *mutated)
232{
233 int i;
234
235 if (orig_data != mutated)
236 {
237 _dbus_string_set_length (mutated, 0);
238
239 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
240 _dbus_assert_not_reached ("out of mem");
241 }
242
243 if (_dbus_string_get_length (mutated) == 0)
244 return;
245
246 i = random_int_in_range (0, _dbus_string_get_length (mutated));
247
248 _dbus_string_set_byte (mutated, i,
249 random_int_in_range (0, 256));
250}
251
252static void
253randomly_remove_one_byte (const DBusString *orig_data,
254 DBusString *mutated)
255{
256 int i;
257
258 if (orig_data != mutated)
259 {
260 _dbus_string_set_length (mutated, 0);
261
262 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
263 _dbus_assert_not_reached ("out of mem");
264 }
265
266 if (_dbus_string_get_length (mutated) == 0)
267 return;
268
269 i = random_int_in_range (0, _dbus_string_get_length (mutated));
270
271 _dbus_string_delete (mutated, i, 1);
272}
273
274
275static void
276randomly_add_one_byte (const DBusString *orig_data,
277 DBusString *mutated)
278{
279 int i;
280
281 if (orig_data != mutated)
282 {
283 _dbus_string_set_length (mutated, 0);
284
285 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
286 _dbus_assert_not_reached ("out of mem");
287 }
288
289 i = random_int_in_range (0, _dbus_string_get_length (mutated));
290
291 _dbus_string_insert_bytes (mutated, i, 1,
292 random_int_in_range (0, 256));
293}
294
295static void
296randomly_modify_length (const DBusString *orig_data,
297 DBusString *mutated)
298{
299 int i;
300 int byte_order;
301 const char *d;
302 dbus_uint32_t orig;
303 int delta;
304
305 if (orig_data != mutated)
306 {
307 _dbus_string_set_length (mutated, 0);
308
309 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
310 _dbus_assert_not_reached ("out of mem");
311 }
312
313 if (_dbus_string_get_length (mutated) < 12)
314 return;
315
316 d = _dbus_string_get_const_data (mutated);
317
318 if (!(*d == DBUS_LITTLE_ENDIAN ||
319 *d == DBUS_BIG_ENDIAN))
320 return;
321
322 byte_order = *d;
323
324 i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
325 i = _DBUS_ALIGN_VALUE (i, 4);
326
327 orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
328
329 delta = random_int_in_range (-10, 10);
330
331 _dbus_marshal_set_uint32 (mutated, byte_order, i,
332 (unsigned) (orig + delta));
333}
334
335static void
336randomly_set_extreme_ints (const DBusString *orig_data,
337 DBusString *mutated)
338{
339 int i;
340 int byte_order;
341 const char *d;
342 dbus_uint32_t orig;
343 static int which = 0;
344 unsigned int extreme_ints[] = {
345 _DBUS_INT_MAX,
346 _DBUS_UINT_MAX,
347 _DBUS_INT_MAX - 1,
348 _DBUS_UINT_MAX - 1,
349 _DBUS_INT_MAX - 2,
350 _DBUS_UINT_MAX - 2,
351 _DBUS_INT_MAX - 17,
352 _DBUS_UINT_MAX - 17,
353 _DBUS_INT_MAX / 2,
354 _DBUS_INT_MAX / 3,
355 _DBUS_UINT_MAX / 2,
356 _DBUS_UINT_MAX / 3,
357 0, 1, 2, 3,
358 (unsigned int) -1,
359 (unsigned int) -2,
360 (unsigned int) -3
361 };
362
363 if (orig_data != mutated)
364 {
365 _dbus_string_set_length (mutated, 0);
366
367 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
368 _dbus_assert_not_reached ("out of mem");
369 }
370
371 if (_dbus_string_get_length (mutated) < 12)
372 return;
373
374 d = _dbus_string_get_const_data (mutated);
375
376 if (!(*d == DBUS_LITTLE_ENDIAN ||
377 *d == DBUS_BIG_ENDIAN))
378 return;
379
380 byte_order = *d;
381
382 i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
383 i = _DBUS_ALIGN_VALUE (i, 4);
384
385 orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
386
387 which = random_int_in_range (0, _DBUS_N_ELEMENTS (extreme_ints));
388
389 _dbus_assert (which >= 0);
390 _dbus_assert (which < _DBUS_N_ELEMENTS (extreme_ints));
391
392 _dbus_marshal_set_uint32 (mutated, byte_order, i,
393 extreme_ints[which]);
394}
395
396static int
397random_type (void)
398{
399 const char types[] = {
400 DBUS_TYPE_INVALID,
401 DBUS_TYPE_NIL,
402 DBUS_TYPE_BYTE,
403 DBUS_TYPE_BOOLEAN,
404 DBUS_TYPE_INT32,
405 DBUS_TYPE_UINT32,
406 DBUS_TYPE_INT64,
407 DBUS_TYPE_UINT64,
408 DBUS_TYPE_DOUBLE,
409 DBUS_TYPE_STRING,
410 DBUS_TYPE_CUSTOM,
411 DBUS_TYPE_ARRAY,
412 DBUS_TYPE_DICT,
413 DBUS_TYPE_OBJECT_PATH
414 };
415
416 _dbus_assert (_DBUS_N_ELEMENTS (types) == DBUS_NUMBER_OF_TYPES + 1);
417
418 return types[ random_int_in_range (0, _DBUS_N_ELEMENTS (types)) ];
419}
420
421static void
422randomly_change_one_type (const DBusString *orig_data,
423 DBusString *mutated)
424{
425 int i;
426 int len;
427
428 if (orig_data != mutated)
429 {
430 _dbus_string_set_length (mutated, 0);
431
432 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
433 _dbus_assert_not_reached ("out of mem");
434 }
435
436 if (_dbus_string_get_length (mutated) == 0)
437 return;
438
439 len = _dbus_string_get_length (mutated);
440 i = random_int_in_range (0, len);
441
442 /* Look for a type starting at a random location,
443 * and replace with a different type
444 */
445 while (i < len)
446 {
447 int b;
448 b = _dbus_string_get_byte (mutated, i);
449 if (_dbus_type_is_valid (b))
450 {
451 _dbus_string_set_byte (mutated, i, random_type ());
452 return;
453 }
454 ++i;
455 }
456}
457
458static int times_we_did_each_thing[7] = { 0, };
459
460static void
461randomly_do_n_things (const DBusString *orig_data,
462 DBusString *mutated,
463 int n)
464{
465 int i;
466 void (* functions[]) (const DBusString *orig_data,
467 DBusString *mutated) =
468 {
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
476 };
477
478 _dbus_string_set_length (mutated, 0);
479
480 if (!_dbus_string_copy (orig_data, 0, mutated, 0))
481 _dbus_assert_not_reached ("out of mem");
482
483 i = 0;
484 while (i < n)
485 {
486 int which;
487
488 which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions));
489
490 (* functions[which]) (mutated, mutated);
491 times_we_did_each_thing[which] += 1;
492
493 ++i;
494 }
495}
496
497static dbus_bool_t
498find_breaks_based_on (const DBusString *filename,
499 dbus_bool_t is_raw,
500 DBusMessageValidity expected_validity,
501 void *data)
502{
503 DBusString orig_data;
504 DBusString mutated;
505 const char *filename_c;
506 dbus_bool_t retval;
507 int i;
508
509 filename_c = _dbus_string_get_const_data (filename);
510
511 retval = FALSE;
512
513 if (!_dbus_string_init (&orig_data))
514 _dbus_assert_not_reached ("could not allocate string\n");
515
516 if (!_dbus_string_init (&mutated))
517 _dbus_assert_not_reached ("could not allocate string\n");
518
519 if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
520 &orig_data))
521 {
522 fprintf (stderr, "could not load file %s\n", filename_c);
523 goto failed;
524 }
525
526 printf (" changing one random byte 100 times\n");
527 i = 0;
528 while (i < 100)
529 {
530 randomly_change_one_byte (&orig_data, &mutated);
531 try_mutated_data (&mutated);
532
533 ++i;
534 }
535
536 printf (" changing length 50 times\n");
537 i = 0;
538 while (i < 50)
539 {
540 randomly_modify_length (&orig_data, &mutated);
541 try_mutated_data (&mutated);
542
543 ++i;
544 }
545
546 printf (" removing one byte 50 times\n");
547 i = 0;
548 while (i < 50)
549 {
550 randomly_remove_one_byte (&orig_data, &mutated);
551 try_mutated_data (&mutated);
552
553 ++i;
554 }
555
556 printf (" adding one byte 50 times\n");
557 i = 0;
558 while (i < 50)
559 {
560 randomly_add_one_byte (&orig_data, &mutated);
561 try_mutated_data (&mutated);
562
563 ++i;
564 }
565
566 printf (" changing ints to boundary values 50 times\n");
567 i = 0;
568 while (i < 50)
569 {
570 randomly_set_extreme_ints (&orig_data, &mutated);
571 try_mutated_data (&mutated);
572
573 ++i;
574 }
575
576 printf (" changing typecodes 50 times\n");
577 i = 0;
578 while (i < 50)
579 {
580 randomly_change_one_type (&orig_data, &mutated);
581 try_mutated_data (&mutated);
582
583 ++i;
584 }
585
586 printf (" changing message length 15 times\n");
587 i = 0;
588 while (i < 15)
589 {
590 randomly_shorten_or_lengthen (&orig_data, &mutated);
591 try_mutated_data (&mutated);
592
593 ++i;
594 }
595
596 printf (" randomly making 2 of above modifications 42 times\n");
597 i = 0;
598 while (i < 42)
599 {
600 randomly_do_n_things (&orig_data, &mutated, 2);
601 try_mutated_data (&mutated);
602
603 ++i;
604 }
605
606 printf (" randomly making 3 of above modifications 42 times\n");
607 i = 0;
608 while (i < 42)
609 {
610 randomly_do_n_things (&orig_data, &mutated, 3);
611 try_mutated_data (&mutated);
612
613 ++i;
614 }
615
616 printf (" randomly making 4 of above modifications 42 times\n");
617 i = 0;
618 while (i < 42)
619 {
620 randomly_do_n_things (&orig_data, &mutated, 4);
621 try_mutated_data (&mutated);
622
623 ++i;
624 }
625
626 retval = TRUE;
627
628 failed:
629
630 _dbus_string_free (&orig_data);
631 _dbus_string_free (&mutated);
632
633 /* FALSE means end the whole process */
634 return retval;
635}
636
637static unsigned int
638get_random_seed (void)
639{
640 DBusString bytes;
641 unsigned int seed;
642 int fd;
643 const char *s;
644
645 seed = 0;
646
647 if (!_dbus_string_init (&bytes))
648 exit (1);
649
650 fd = open ("/dev/urandom", O_RDONLY);
651 if (fd < 0)
652 goto use_fallback;
653
654 if (_dbus_read (fd, &bytes, 4) != 4)
655 goto use_fallback;
656
657 close (fd);
658
659 s = _dbus_string_get_const_data (&bytes);
660
661 seed = * (unsigned int*) s;
662 goto out;
663
664 use_fallback:
665 {
666 long tv_usec;
667
668 fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n");
669
670 _dbus_get_current_time (NULL, &tv_usec);
671
672 seed = tv_usec;
673 }
674
675 out:
676 _dbus_string_free (&bytes);
677
678 return seed;
679}
680
681int
682main (int argc,
683 char **argv)
684{
685 const char *test_data_dir;
686 const char *failure_dir_c;
687 int total_failures_found;
688
689 if (argc > 1)
690 test_data_dir = argv[1];
691 else
692 {
693 fprintf (stderr, "Must specify a top_srcdir/test/data directory\n");
694 return 1;
695 }
696
697 total_failures_found = 0;
698 total_attempts = 0;
699
700 if (!_dbus_string_init (&failure_dir))
701 return 1;
702
703 /* so you can leave it overnight safely */
704#define MAX_FAILURES 1000
705
706 while (total_failures_found < MAX_FAILURES)
707 {
708 unsigned int seed;
709
710 failures_this_iteration = 0;
711
712 seed = get_random_seed ();
713
714 _dbus_string_set_length (&failure_dir, 0);
715
716 if (!_dbus_string_append (&failure_dir, "failures-"))
717 return 1;
718
719 if (!_dbus_string_append_uint (&failure_dir, seed))
720 return 1;
721
722 failure_dir_c = _dbus_string_get_const_data (&failure_dir);
723
724 if (mkdir (failure_dir_c, 0700) < 0)
725 {
726 if (errno != EEXIST)
727 fprintf (stderr, "didn't mkdir %s: %s\n",
728 failure_dir_c, strerror (errno));
729 }
730
731 printf ("next seed = %u \ttotal failures %d of %d attempts\n",
732 seed, total_failures_found, total_attempts);
733
734 srand (seed);
735
736 if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
737 find_breaks_based_on,
738 NULL))
739 {
740 fprintf (stderr, "fatal error iterating over message files\n");
741 rmdir (failure_dir_c);
742 return 1;
743 }
744
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]);
754
755 printf ("Found %d failures with seed %u stored in %s\n",
756 failures_this_iteration, seed, failure_dir_c);
757
758 total_failures_found += failures_this_iteration;
759
760 rmdir (failure_dir_c); /* does nothing if non-empty */
761 }
762
763 return 0;
764}