break-loader.c revision f7d96bdf80129d95cf33f26a778ce2c94a818bd0
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 1.2 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, _DBUS_INT_MAX) || 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 const char *filename_c; 157 DBusResultCode result; 158 159 _dbus_string_append (&filename, ".message-raw"); 160 161 _dbus_string_get_const_data (&filename, &filename_c); 162 printf ("Child failed, writing %s\n", 163 filename_c); 164 165 result = _dbus_string_save_to_file (data, &filename); 166 167 if (result != DBUS_RESULT_SUCCESS) 168 { 169 fprintf (stderr, "Failed to save failed message data: %s\n", 170 dbus_result_to_string (result)); 171 exit (1); /* so we can see the seed that was printed out */ 172 } 173 174 failures_this_iteration += 1; 175 176 _dbus_string_free (&filename); 177 178 return FALSE; 179 } 180 else 181 { 182 _dbus_string_free (&filename); 183 return TRUE; 184 } 185 } 186 187 _dbus_assert_not_reached ("should not be reached"); 188 return TRUE; 189} 190 191static void 192randomly_shorten_or_lengthen (const DBusString *orig_data, 193 DBusString *mutated) 194{ 195 int delta; 196 197 if (orig_data != mutated) 198 { 199 _dbus_string_set_length (mutated, 0); 200 201 if (!_dbus_string_copy (orig_data, 0, mutated, 0)) 202 _dbus_assert_not_reached ("out of mem"); 203 } 204 205 if (_dbus_string_get_length (mutated) == 0) 206 delta = random_int_in_range (0, 10); 207 else 208 delta = random_int_in_range (- _dbus_string_get_length (mutated), 209 _dbus_string_get_length (mutated) * 3); 210 211 if (delta < 0) 212 _dbus_string_shorten (mutated, - delta); 213 else if (delta > 0) 214 { 215 int i = 0; 216 217 i = _dbus_string_get_length (mutated); 218 if (!_dbus_string_lengthen (mutated, delta)) 219 _dbus_assert_not_reached ("couldn't lengthen string"); 220 221 while (i < _dbus_string_get_length (mutated)) 222 { 223 _dbus_string_set_byte (mutated, 224 i, 225 random_int_in_range (0, 256)); 226 ++i; 227 } 228 } 229} 230 231static void 232randomly_change_one_byte (const DBusString *orig_data, 233 DBusString *mutated) 234{ 235 int i; 236 237 if (orig_data != mutated) 238 { 239 _dbus_string_set_length (mutated, 0); 240 241 if (!_dbus_string_copy (orig_data, 0, mutated, 0)) 242 _dbus_assert_not_reached ("out of mem"); 243 } 244 245 if (_dbus_string_get_length (mutated) == 0) 246 return; 247 248 i = random_int_in_range (0, _dbus_string_get_length (mutated)); 249 250 _dbus_string_set_byte (mutated, i, 251 random_int_in_range (0, 256)); 252} 253 254static void 255randomly_remove_one_byte (const DBusString *orig_data, 256 DBusString *mutated) 257{ 258 int i; 259 260 if (orig_data != mutated) 261 { 262 _dbus_string_set_length (mutated, 0); 263 264 if (!_dbus_string_copy (orig_data, 0, mutated, 0)) 265 _dbus_assert_not_reached ("out of mem"); 266 } 267 268 if (_dbus_string_get_length (mutated) == 0) 269 return; 270 271 i = random_int_in_range (0, _dbus_string_get_length (mutated)); 272 273 _dbus_string_delete (mutated, i, 1); 274} 275 276 277static void 278randomly_add_one_byte (const DBusString *orig_data, 279 DBusString *mutated) 280{ 281 int i; 282 283 if (orig_data != mutated) 284 { 285 _dbus_string_set_length (mutated, 0); 286 287 if (!_dbus_string_copy (orig_data, 0, mutated, 0)) 288 _dbus_assert_not_reached ("out of mem"); 289 } 290 291 i = random_int_in_range (0, _dbus_string_get_length (mutated)); 292 293 _dbus_string_insert_byte (mutated, i, 294 random_int_in_range (0, 256)); 295} 296 297static void 298randomly_modify_length (const DBusString *orig_data, 299 DBusString *mutated) 300{ 301 int i; 302 int byte_order; 303 const char *d; 304 dbus_uint32_t orig; 305 int delta; 306 307 if (orig_data != mutated) 308 { 309 _dbus_string_set_length (mutated, 0); 310 311 if (!_dbus_string_copy (orig_data, 0, mutated, 0)) 312 _dbus_assert_not_reached ("out of mem"); 313 } 314 315 if (_dbus_string_get_length (mutated) < 12) 316 return; 317 318 _dbus_string_get_const_data (mutated, &d); 319 320 if (!(*d == DBUS_LITTLE_ENDIAN || 321 *d == DBUS_BIG_ENDIAN)) 322 return; 323 324 byte_order = *d; 325 326 i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8); 327 i = _DBUS_ALIGN_VALUE (i, 4); 328 329 orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL); 330 331 delta = random_int_in_range (-10, 10); 332 333 _dbus_marshal_set_uint32 (mutated, byte_order, i, 334 (unsigned) (orig + delta)); 335} 336 337static void 338randomly_set_extreme_ints (const DBusString *orig_data, 339 DBusString *mutated) 340{ 341 int i; 342 int byte_order; 343 const char *d; 344 dbus_uint32_t orig; 345 static int which = 0; 346 unsigned int extreme_ints[] = { 347 _DBUS_INT_MAX, 348 _DBUS_UINT_MAX, 349 _DBUS_INT_MAX - 1, 350 _DBUS_UINT_MAX - 1, 351 _DBUS_INT_MAX - 2, 352 _DBUS_UINT_MAX - 2, 353 (unsigned int) (_DBUS_INT_MAX + 1), 354 (unsigned int) (_DBUS_UINT_MAX + 1), 355 _DBUS_INT_MAX + 2, 356 _DBUS_UINT_MAX + 2, 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 _dbus_string_get_const_data (mutated, &d); 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 times_we_did_each_thing[6] = { 0, }; 397 398static void 399randomly_do_n_things (const DBusString *orig_data, 400 DBusString *mutated, 401 int n) 402{ 403 int i; 404 void (* functions[]) (const DBusString *orig_data, 405 DBusString *mutated) = 406 { 407 randomly_shorten_or_lengthen, 408 randomly_change_one_byte, 409 randomly_add_one_byte, 410 randomly_remove_one_byte, 411 randomly_modify_length, 412 randomly_set_extreme_ints 413 }; 414 415 _dbus_string_set_length (mutated, 0); 416 417 if (!_dbus_string_copy (orig_data, 0, mutated, 0)) 418 _dbus_assert_not_reached ("out of mem"); 419 420 i = 0; 421 while (i < n) 422 { 423 int which; 424 425 which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions)); 426 427 (* functions[which]) (mutated, mutated); 428 times_we_did_each_thing[which] += 1; 429 430 ++i; 431 } 432} 433 434static dbus_bool_t 435find_breaks_based_on (const DBusString *filename, 436 dbus_bool_t is_raw, 437 DBusMessageValidity expected_validity, 438 void *data) 439{ 440 DBusString orig_data; 441 DBusString mutated; 442 const char *filename_c; 443 dbus_bool_t retval; 444 int i; 445 446 _dbus_string_get_const_data (filename, &filename_c); 447 448 retval = FALSE; 449 450 if (!_dbus_string_init (&orig_data, _DBUS_INT_MAX)) 451 _dbus_assert_not_reached ("could not allocate string\n"); 452 453 if (!_dbus_string_init (&mutated, _DBUS_INT_MAX)) 454 _dbus_assert_not_reached ("could not allocate string\n"); 455 456 if (!dbus_internal_do_not_use_load_message_file (filename, is_raw, 457 &orig_data)) 458 { 459 fprintf (stderr, "could not load file %s\n", filename_c); 460 goto failed; 461 } 462 463 i = 0; 464 while (i < 100) 465 { 466 randomly_change_one_byte (&orig_data, &mutated); 467 try_mutated_data (&mutated); 468 469 ++i; 470 } 471 472 i = 0; 473 while (i < 50) 474 { 475 randomly_modify_length (&orig_data, &mutated); 476 try_mutated_data (&mutated); 477 478 ++i; 479 } 480 481 i = 0; 482 while (i < 50) 483 { 484 randomly_remove_one_byte (&orig_data, &mutated); 485 try_mutated_data (&mutated); 486 487 ++i; 488 } 489 490 i = 0; 491 while (i < 50) 492 { 493 randomly_add_one_byte (&orig_data, &mutated); 494 try_mutated_data (&mutated); 495 496 ++i; 497 } 498 499 i = 0; 500 while (i < 50) 501 { 502 randomly_set_extreme_ints (&orig_data, &mutated); 503 try_mutated_data (&mutated); 504 505 ++i; 506 } 507 508 i = 0; 509 while (i < 15) 510 { 511 randomly_shorten_or_lengthen (&orig_data, &mutated); 512 try_mutated_data (&mutated); 513 514 ++i; 515 } 516 517 i = 0; 518 while (i < 42) 519 { 520 randomly_do_n_things (&orig_data, &mutated, 2); 521 try_mutated_data (&mutated); 522 523 ++i; 524 } 525 526 i = 0; 527 while (i < 42) 528 { 529 randomly_do_n_things (&orig_data, &mutated, 3); 530 try_mutated_data (&mutated); 531 532 ++i; 533 } 534 535 i = 0; 536 while (i < 42) 537 { 538 randomly_do_n_things (&orig_data, &mutated, 4); 539 try_mutated_data (&mutated); 540 541 ++i; 542 } 543 544 retval = TRUE; 545 546 failed: 547 548 _dbus_string_free (&orig_data); 549 _dbus_string_free (&mutated); 550 551 /* FALSE means end the whole process */ 552 return retval; 553} 554 555static unsigned int 556get_random_seed (void) 557{ 558 DBusString bytes; 559 unsigned int seed; 560 int fd; 561 const char *s; 562 563 seed = 0; 564 565 if (!_dbus_string_init (&bytes, _DBUS_INT_MAX)) 566 exit (1); 567 568 fd = open ("/dev/urandom", O_RDONLY); 569 if (fd < 0) 570 goto use_fallback; 571 572 if (_dbus_read (fd, &bytes, 4) != 4) 573 goto use_fallback; 574 575 close (fd); 576 577 _dbus_string_get_const_data (&bytes, &s); 578 579 seed = * (unsigned int*) s; 580 goto out; 581 582 use_fallback: 583 { 584 long tv_usec; 585 586 fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n"); 587 588 _dbus_get_current_time (NULL, &tv_usec); 589 590 seed = tv_usec; 591 } 592 593 out: 594 _dbus_string_free (&bytes); 595 596 return seed; 597} 598 599int 600main (int argc, 601 char **argv) 602{ 603 const char *test_data_dir; 604 const char *failure_dir_c; 605 int total_failures_found; 606 607 if (argc > 1) 608 test_data_dir = argv[1]; 609 else 610 { 611 fprintf (stderr, "Must specify a top_srcdir/test/data directory\n"); 612 return 1; 613 } 614 615 total_failures_found = 0; 616 total_attempts = 0; 617 618 if (!_dbus_string_init (&failure_dir, _DBUS_INT_MAX)) 619 return 1; 620 621 /* so you can leave it overnight safely */ 622#define MAX_FAILURES 1000 623 624 while (total_failures_found < MAX_FAILURES) 625 { 626 unsigned int seed; 627 628 failures_this_iteration = 0; 629 630 seed = get_random_seed (); 631 632 _dbus_string_set_length (&failure_dir, 0); 633 634 if (!_dbus_string_append (&failure_dir, "failures-")) 635 return 1; 636 637 if (!_dbus_string_append_uint (&failure_dir, seed)) 638 return 1; 639 640 _dbus_string_get_const_data (&failure_dir, &failure_dir_c); 641 642 if (mkdir (failure_dir_c, 0700) < 0) 643 { 644 if (errno != EEXIST) 645 fprintf (stderr, "didn't mkdir %s: %s\n", 646 failure_dir_c, strerror (errno)); 647 } 648 649 printf ("next seed = %u \ttotal failures %d of %d attempts\n", 650 seed, total_failures_found, total_attempts); 651 652 srand (seed); 653 654 if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir, 655 find_breaks_based_on, 656 NULL)) 657 { 658 fprintf (stderr, "fatal error iterating over message files\n"); 659 rmdir (failure_dir_c); 660 return 1; 661 } 662 663 printf (" did %d random mutations: %d %d %d %d %d %d\n", 664 _DBUS_N_ELEMENTS (times_we_did_each_thing), 665 times_we_did_each_thing[0], 666 times_we_did_each_thing[1], 667 times_we_did_each_thing[2], 668 times_we_did_each_thing[3], 669 times_we_did_each_thing[4], 670 times_we_did_each_thing[5]); 671 672 printf ("Found %d failures with seed %u stored in %s\n", 673 failures_this_iteration, seed, failure_dir_c); 674 675 total_failures_found += failures_this_iteration; 676 677 rmdir (failure_dir_c); /* does nothing if non-empty */ 678 } 679 680 return 0; 681} 682