2003-09-25 Mark McLoughlin <mark@skynet.ie>
[platform/upstream/dbus.git] / test / break-loader.c
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
42 static DBusString failure_dir;
43 static int total_attempts;
44 static int failures_this_iteration;
45
46 static int
47 random_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
85 static dbus_bool_t
86 try_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
188 static void
189 randomly_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
228 static void
229 randomly_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
251 static void
252 randomly_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
274 static void
275 randomly_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
294 static void
295 randomly_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
334 static void
335 randomly_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
395 static void
396 randomly_change_one_type (const DBusString *orig_data,
397                           DBusString       *mutated)
398 {
399   int i;
400   int len;
401   
402   if (orig_data != mutated)
403     {
404       _dbus_string_set_length (mutated, 0);
405       
406       if (!_dbus_string_copy (orig_data, 0, mutated, 0))
407         _dbus_assert_not_reached ("out of mem");
408     }
409
410   if (_dbus_string_get_length (mutated) == 0)
411     return;
412
413   len = _dbus_string_get_length (mutated);
414   i = random_int_in_range (0, len);
415
416   /* Look for a type starting at a random location,
417    * and replace with a different type
418    */
419   while (i < len)
420     {
421       int b;
422       b = _dbus_string_get_byte (mutated, i);
423       if (b > DBUS_TYPE_INVALID && b <= DBUS_TYPE_LAST)
424         {
425           _dbus_string_set_byte (mutated, i,
426                                  random_int_in_range (DBUS_TYPE_INVALID,
427                                                       DBUS_TYPE_LAST + 1));
428           return;
429         }
430       ++i;
431     }
432 }
433
434 static int times_we_did_each_thing[6] = { 0, };
435
436 static void
437 randomly_do_n_things (const DBusString *orig_data,
438                       DBusString       *mutated,
439                       int               n)
440 {
441   int i;
442   void (* functions[]) (const DBusString *orig_data,
443                         DBusString       *mutated) =
444     {
445       randomly_shorten_or_lengthen,
446       randomly_change_one_byte,
447       randomly_add_one_byte,
448       randomly_remove_one_byte,
449       randomly_modify_length,
450       randomly_set_extreme_ints,
451       randomly_change_one_type
452     };
453
454   _dbus_string_set_length (mutated, 0);
455
456   if (!_dbus_string_copy (orig_data, 0, mutated, 0))
457     _dbus_assert_not_reached ("out of mem");
458
459   i = 0;
460   while (i < n)
461     {
462       int which;
463
464       which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions));
465
466       (* functions[which]) (mutated, mutated);
467       times_we_did_each_thing[which] += 1;
468       
469       ++i;
470     }
471 }
472
473 static dbus_bool_t
474 find_breaks_based_on (const DBusString   *filename,
475                       dbus_bool_t         is_raw,
476                       DBusMessageValidity expected_validity,
477                       void               *data)
478 {
479   DBusString orig_data;
480   DBusString mutated;
481   const char *filename_c;
482   dbus_bool_t retval;
483   int i;
484
485   filename_c = _dbus_string_get_const_data (filename);
486
487   retval = FALSE;
488
489   if (!_dbus_string_init (&orig_data))
490     _dbus_assert_not_reached ("could not allocate string\n");
491
492   if (!_dbus_string_init (&mutated))
493     _dbus_assert_not_reached ("could not allocate string\n");
494
495   if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
496                                                    &orig_data))
497     {
498       fprintf (stderr, "could not load file %s\n", filename_c);
499       goto failed;
500     }
501
502   printf ("        changing one random byte 100 times\n");
503   i = 0;
504   while (i < 100)
505     {
506       randomly_change_one_byte (&orig_data, &mutated);
507       try_mutated_data (&mutated);
508
509       ++i;
510     }
511
512   printf ("        changing length 50 times\n");
513   i = 0;
514   while (i < 50)
515     {
516       randomly_modify_length (&orig_data, &mutated);
517       try_mutated_data (&mutated);
518
519       ++i;
520     }
521
522   printf ("        removing one byte 50 times\n");
523   i = 0;
524   while (i < 50)
525     {
526       randomly_remove_one_byte (&orig_data, &mutated);
527       try_mutated_data (&mutated);
528
529       ++i;
530     }
531
532   printf ("        adding one byte 50 times\n");
533   i = 0;
534   while (i < 50)
535     {
536       randomly_add_one_byte (&orig_data, &mutated);
537       try_mutated_data (&mutated);
538
539       ++i;
540     }
541
542   printf ("        changing ints to boundary values 50 times\n");
543   i = 0;
544   while (i < 50)
545     {
546       randomly_set_extreme_ints (&orig_data, &mutated);
547       try_mutated_data (&mutated);
548
549       ++i;
550     }
551
552   printf ("        changing typecodes 50 times\n");
553   i = 0;
554   while (i < 50)
555     {
556       randomly_change_one_type (&orig_data, &mutated);
557       try_mutated_data (&mutated);
558
559       ++i;
560     }
561
562   printf ("        changing message length 15 times\n");
563   i = 0;
564   while (i < 15)
565     {
566       randomly_shorten_or_lengthen (&orig_data, &mutated);
567       try_mutated_data (&mutated);
568
569       ++i;
570     }
571
572   printf ("        randomly making 2 of above modifications 42 times\n");
573   i = 0;
574   while (i < 42)
575     {
576       randomly_do_n_things (&orig_data, &mutated, 2);
577       try_mutated_data (&mutated);
578
579       ++i;
580     }
581
582   printf ("        randomly making 3 of above modifications 42 times\n");
583   i = 0;
584   while (i < 42)
585     {
586       randomly_do_n_things (&orig_data, &mutated, 3);
587       try_mutated_data (&mutated);
588
589       ++i;
590     }
591
592   printf ("        randomly making 4 of above modifications 42 times\n");
593   i = 0;
594   while (i < 42)
595     {
596       randomly_do_n_things (&orig_data, &mutated, 4);
597       try_mutated_data (&mutated);
598
599       ++i;
600     }
601   
602   retval = TRUE;
603   
604  failed:
605
606   _dbus_string_free (&orig_data);
607   _dbus_string_free (&mutated);
608
609   /* FALSE means end the whole process */
610   return retval;
611 }
612
613 static unsigned int
614 get_random_seed (void)
615 {
616   DBusString bytes;
617   unsigned int seed;
618   int fd;
619   const char *s;
620
621   seed = 0;
622
623   if (!_dbus_string_init (&bytes))
624     exit (1);
625
626   fd = open ("/dev/urandom", O_RDONLY);
627   if (fd < 0)
628     goto use_fallback;
629
630   if (_dbus_read (fd, &bytes, 4) != 4)
631     goto use_fallback;
632
633   close (fd);
634
635   s = _dbus_string_get_const_data (&bytes);
636
637   seed = * (unsigned int*) s;
638   goto out;
639
640  use_fallback:
641   {
642     long tv_usec;
643
644     fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n");
645
646     _dbus_get_current_time (NULL, &tv_usec);
647
648     seed = tv_usec;
649   }
650
651  out:
652   _dbus_string_free (&bytes);
653
654   return seed;
655 }
656
657 int
658 main (int    argc,
659       char **argv)
660 {
661   const char *test_data_dir;
662   const char *failure_dir_c;
663   int total_failures_found;
664   
665   if (argc > 1)
666     test_data_dir = argv[1];
667   else
668     {
669       fprintf (stderr, "Must specify a top_srcdir/test/data directory\n");
670       return 1;
671     }
672
673   total_failures_found = 0;
674   total_attempts = 0;
675
676   if (!_dbus_string_init (&failure_dir))
677     return 1;
678
679   /* so you can leave it overnight safely */
680 #define MAX_FAILURES 1000
681
682   while (total_failures_found < MAX_FAILURES)
683     {
684       unsigned int seed;
685
686       failures_this_iteration = 0;
687
688       seed = get_random_seed ();
689
690       _dbus_string_set_length (&failure_dir, 0);
691
692       if (!_dbus_string_append (&failure_dir, "failures-"))
693         return 1;
694
695       if (!_dbus_string_append_uint (&failure_dir, seed))
696         return 1;
697
698       failure_dir_c = _dbus_string_get_const_data (&failure_dir);
699
700       if (mkdir (failure_dir_c, 0700) < 0)
701         {
702           if (errno != EEXIST)
703             fprintf (stderr, "didn't mkdir %s: %s\n",
704                      failure_dir_c, strerror (errno));
705         }
706
707       printf ("next seed = %u \ttotal failures %d of %d attempts\n",
708               seed, total_failures_found, total_attempts);
709
710       srand (seed);
711
712       if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
713                                                           find_breaks_based_on,
714                                                           NULL))
715         {
716           fprintf (stderr, "fatal error iterating over message files\n");
717           rmdir (failure_dir_c);
718           return 1;
719         }
720
721       printf ("  did %d random mutations: %d %d %d %d %d %d\n",
722               _DBUS_N_ELEMENTS (times_we_did_each_thing),
723               times_we_did_each_thing[0],
724               times_we_did_each_thing[1],
725               times_we_did_each_thing[2],
726               times_we_did_each_thing[3],
727               times_we_did_each_thing[4],
728               times_we_did_each_thing[5]);
729       
730       printf ("Found %d failures with seed %u stored in %s\n",
731               failures_this_iteration, seed, failure_dir_c);
732
733       total_failures_found += failures_this_iteration;
734
735       rmdir (failure_dir_c); /* does nothing if non-empty */
736     }
737
738   return 0;
739 }