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