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 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 43 static DBusString failure_dir; 44 static int total_attempts; 45 static int failures_this_iteration; 46 47 static int 48 random_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 86 static dbus_bool_t 87 try_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 189 static void 190 randomly_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 229 static void 230 randomly_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 252 static void 253 randomly_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 275 static void 276 randomly_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 295 static void 296 randomly_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 335 static void 336 randomly_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 396 static int 397 random_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 421 static void 422 randomly_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 458 static int times_we_did_each_thing[7] = { 0, }; 459 460 static void 461 randomly_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 497 static dbus_bool_t 498 find_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 637 static unsigned int 638 get_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 681 int 682 main (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 } 765