Merge "Optional autogen.sh flag --enable-kdbus-transport added allowing to compile...
[platform/upstream/dbus.git] / test / break-loader.c
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 }