2003-03-31 Havoc Pennington <hp@redhat.com>
[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_byte (mutated, i,
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     (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
393 static int times_we_did_each_thing[6] = { 0, };
394
395 static void
396 randomly_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
431 static dbus_bool_t
432 find_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
552 static unsigned int
553 get_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
596 int
597 main (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 }