Home | History | Annotate | Download | only in test
      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_monotonic_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