break-loader.c revision fa05de9230d62e7c427b5313796fc6ccd4d0ff60
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) || 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_byte (mutated, i, 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 (unsigned int) (_DBUS_INT_MAX + 1), 351 (unsigned int) (_DBUS_UINT_MAX + 1), 352 _DBUS_INT_MAX + 2, 353 _DBUS_UINT_MAX + 2, 354 0, 1, 2, 3, 355 (unsigned int) -1, 356 (unsigned int) -2, 357 (unsigned int) -3 358 }; 359 360 if (orig_data != mutated) 361 { 362 _dbus_string_set_length (mutated, 0); 363 364 if (!_dbus_string_copy (orig_data, 0, mutated, 0)) 365 _dbus_assert_not_reached ("out of mem"); 366 } 367 368 if (_dbus_string_get_length (mutated) < 12) 369 return; 370 371 d = _dbus_string_get_const_data (mutated); 372 373 if (!(*d == DBUS_LITTLE_ENDIAN || 374 *d == DBUS_BIG_ENDIAN)) 375 return; 376 377 byte_order = *d; 378 379 i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8); 380 i = _DBUS_ALIGN_VALUE (i, 4); 381 382 orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL); 383 384 which = random_int_in_range (0, _DBUS_N_ELEMENTS (extreme_ints)); 385 386 _dbus_assert (which >= 0); 387 _dbus_assert (which < _DBUS_N_ELEMENTS (extreme_ints)); 388 389 _dbus_marshal_set_uint32 (mutated, byte_order, i, 390 extreme_ints[which]); 391} 392 393static int times_we_did_each_thing[6] = { 0, }; 394 395static void 396randomly_do_n_things (const DBusString *orig_data, 397 DBusString *mutated, 398 int n) 399{ 400 int i; 401 void (* functions[]) (const DBusString *orig_data, 402 DBusString *mutated) = 403 { 404 randomly_shorten_or_lengthen, 405 randomly_change_one_byte, 406 randomly_add_one_byte, 407 randomly_remove_one_byte, 408 randomly_modify_length, 409 randomly_set_extreme_ints 410 }; 411 412 _dbus_string_set_length (mutated, 0); 413 414 if (!_dbus_string_copy (orig_data, 0, mutated, 0)) 415 _dbus_assert_not_reached ("out of mem"); 416 417 i = 0; 418 while (i < n) 419 { 420 int which; 421 422 which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions)); 423 424 (* functions[which]) (mutated, mutated); 425 times_we_did_each_thing[which] += 1; 426 427 ++i; 428 } 429} 430 431static dbus_bool_t 432find_breaks_based_on (const DBusString *filename, 433 dbus_bool_t is_raw, 434 DBusMessageValidity expected_validity, 435 void *data) 436{ 437 DBusString orig_data; 438 DBusString mutated; 439 const char *filename_c; 440 dbus_bool_t retval; 441 int i; 442 443 filename_c = _dbus_string_get_const_data (filename); 444 445 retval = FALSE; 446 447 if (!_dbus_string_init (&orig_data)) 448 _dbus_assert_not_reached ("could not allocate string\n"); 449 450 if (!_dbus_string_init (&mutated)) 451 _dbus_assert_not_reached ("could not allocate string\n"); 452 453 if (!dbus_internal_do_not_use_load_message_file (filename, is_raw, 454 &orig_data)) 455 { 456 fprintf (stderr, "could not load file %s\n", filename_c); 457 goto failed; 458 } 459 460 i = 0; 461 while (i < 100) 462 { 463 randomly_change_one_byte (&orig_data, &mutated); 464 try_mutated_data (&mutated); 465 466 ++i; 467 } 468 469 i = 0; 470 while (i < 50) 471 { 472 randomly_modify_length (&orig_data, &mutated); 473 try_mutated_data (&mutated); 474 475 ++i; 476 } 477 478 i = 0; 479 while (i < 50) 480 { 481 randomly_remove_one_byte (&orig_data, &mutated); 482 try_mutated_data (&mutated); 483 484 ++i; 485 } 486 487 i = 0; 488 while (i < 50) 489 { 490 randomly_add_one_byte (&orig_data, &mutated); 491 try_mutated_data (&mutated); 492 493 ++i; 494 } 495 496 i = 0; 497 while (i < 50) 498 { 499 randomly_set_extreme_ints (&orig_data, &mutated); 500 try_mutated_data (&mutated); 501 502 ++i; 503 } 504 505 i = 0; 506 while (i < 15) 507 { 508 randomly_shorten_or_lengthen (&orig_data, &mutated); 509 try_mutated_data (&mutated); 510 511 ++i; 512 } 513 514 i = 0; 515 while (i < 42) 516 { 517 randomly_do_n_things (&orig_data, &mutated, 2); 518 try_mutated_data (&mutated); 519 520 ++i; 521 } 522 523 i = 0; 524 while (i < 42) 525 { 526 randomly_do_n_things (&orig_data, &mutated, 3); 527 try_mutated_data (&mutated); 528 529 ++i; 530 } 531 532 i = 0; 533 while (i < 42) 534 { 535 randomly_do_n_things (&orig_data, &mutated, 4); 536 try_mutated_data (&mutated); 537 538 ++i; 539 } 540 541 retval = TRUE; 542 543 failed: 544 545 _dbus_string_free (&orig_data); 546 _dbus_string_free (&mutated); 547 548 /* FALSE means end the whole process */ 549 return retval; 550} 551 552static unsigned int 553get_random_seed (void) 554{ 555 DBusString bytes; 556 unsigned int seed; 557 int fd; 558 const char *s; 559 560 seed = 0; 561 562 if (!_dbus_string_init (&bytes)) 563 exit (1); 564 565 fd = open ("/dev/urandom", O_RDONLY); 566 if (fd < 0) 567 goto use_fallback; 568 569 if (_dbus_read (fd, &bytes, 4) != 4) 570 goto use_fallback; 571 572 close (fd); 573 574 s = _dbus_string_get_const_data (&bytes); 575 576 seed = * (unsigned int*) s; 577 goto out; 578 579 use_fallback: 580 { 581 long tv_usec; 582 583 fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n"); 584 585 _dbus_get_current_time (NULL, &tv_usec); 586 587 seed = tv_usec; 588 } 589 590 out: 591 _dbus_string_free (&bytes); 592 593 return seed; 594} 595 596int 597main (int argc, 598 char **argv) 599{ 600 const char *test_data_dir; 601 const char *failure_dir_c; 602 int total_failures_found; 603 604 if (argc > 1) 605 test_data_dir = argv[1]; 606 else 607 { 608 fprintf (stderr, "Must specify a top_srcdir/test/data directory\n"); 609 return 1; 610 } 611 612 total_failures_found = 0; 613 total_attempts = 0; 614 615 if (!_dbus_string_init (&failure_dir)) 616 return 1; 617 618 /* so you can leave it overnight safely */ 619#define MAX_FAILURES 1000 620 621 while (total_failures_found < MAX_FAILURES) 622 { 623 unsigned int seed; 624 625 failures_this_iteration = 0; 626 627 seed = get_random_seed (); 628 629 _dbus_string_set_length (&failure_dir, 0); 630 631 if (!_dbus_string_append (&failure_dir, "failures-")) 632 return 1; 633 634 if (!_dbus_string_append_uint (&failure_dir, seed)) 635 return 1; 636 637 failure_dir_c = _dbus_string_get_const_data (&failure_dir); 638 639 if (mkdir (failure_dir_c, 0700) < 0) 640 { 641 if (errno != EEXIST) 642 fprintf (stderr, "didn't mkdir %s: %s\n", 643 failure_dir_c, strerror (errno)); 644 } 645 646 printf ("next seed = %u \ttotal failures %d of %d attempts\n", 647 seed, total_failures_found, total_attempts); 648 649 srand (seed); 650 651 if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir, 652 find_breaks_based_on, 653 NULL)) 654 { 655 fprintf (stderr, "fatal error iterating over message files\n"); 656 rmdir (failure_dir_c); 657 return 1; 658 } 659 660 printf (" did %d random mutations: %d %d %d %d %d %d\n", 661 _DBUS_N_ELEMENTS (times_we_did_each_thing), 662 times_we_did_each_thing[0], 663 times_we_did_each_thing[1], 664 times_we_did_each_thing[2], 665 times_we_did_each_thing[3], 666 times_we_did_each_thing[4], 667 times_we_did_each_thing[5]); 668 669 printf ("Found %d failures with seed %u stored in %s\n", 670 failures_this_iteration, seed, failure_dir_c); 671 672 total_failures_found += failures_this_iteration; 673 674 rmdir (failure_dir_c); /* does nothing if non-empty */ 675 } 676 677 return 0; 678} 679