2003-02-13 Anders Carlsson <andersca@codefactory.se>
[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, _DBUS_INT_MAX) ||
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           const char *filename_c;
157           DBusResultCode result;
158
159           _dbus_string_append (&filename, ".message-raw");
160           
161           _dbus_string_get_const_data (&filename, &filename_c);
162           printf ("Child failed, writing %s\n",
163                   filename_c);
164
165           result = _dbus_string_save_to_file (data, &filename);
166
167           if (result != DBUS_RESULT_SUCCESS)
168             {
169               fprintf (stderr, "Failed to save failed message data: %s\n",
170                        dbus_result_to_string (result));
171               exit (1); /* so we can see the seed that was printed out */
172             }
173
174           failures_this_iteration += 1;
175
176           _dbus_string_free (&filename);
177           
178           return FALSE;
179         }
180       else
181         {
182           _dbus_string_free (&filename);          
183           return TRUE;
184         }
185     }
186
187   _dbus_assert_not_reached ("should not be reached");
188   return TRUE;
189 }
190
191 static void
192 randomly_shorten_or_lengthen (const DBusString *orig_data,
193                               DBusString       *mutated)
194 {
195   int delta;
196
197   if (orig_data != mutated)
198     {
199       _dbus_string_set_length (mutated, 0);
200       
201       if (!_dbus_string_copy (orig_data, 0, mutated, 0))
202         _dbus_assert_not_reached ("out of mem");
203     }
204
205   if (_dbus_string_get_length (mutated) == 0)
206     delta = random_int_in_range (0, 10);
207   else
208     delta = random_int_in_range (- _dbus_string_get_length (mutated),
209                                  _dbus_string_get_length (mutated) * 3);
210   
211   if (delta < 0)
212     _dbus_string_shorten (mutated, - delta);
213   else if (delta > 0)
214     {
215       int i = 0;
216
217       i = _dbus_string_get_length (mutated);
218       if (!_dbus_string_lengthen (mutated, delta))
219         _dbus_assert_not_reached ("couldn't lengthen string");
220
221       while (i < _dbus_string_get_length (mutated))
222         {
223           _dbus_string_set_byte (mutated,
224                                  i,
225                                  random_int_in_range (0, 256));
226           ++i;
227         }
228     }
229 }
230
231 static void
232 randomly_change_one_byte (const DBusString *orig_data,
233                           DBusString       *mutated)
234 {
235   int i;
236
237   if (orig_data != mutated)
238     {
239       _dbus_string_set_length (mutated, 0);
240       
241       if (!_dbus_string_copy (orig_data, 0, mutated, 0))
242         _dbus_assert_not_reached ("out of mem");
243     }
244
245   if (_dbus_string_get_length (mutated) == 0)
246     return;
247   
248   i = random_int_in_range (0, _dbus_string_get_length (mutated));
249
250   _dbus_string_set_byte (mutated, i,
251                          random_int_in_range (0, 256));
252 }
253
254 static void
255 randomly_remove_one_byte (const DBusString *orig_data,
256                           DBusString       *mutated)
257 {
258   int i;
259
260   if (orig_data != mutated)
261     {
262       _dbus_string_set_length (mutated, 0);
263       
264       if (!_dbus_string_copy (orig_data, 0, mutated, 0))
265         _dbus_assert_not_reached ("out of mem");
266     }
267
268   if (_dbus_string_get_length (mutated) == 0)
269     return;
270   
271   i = random_int_in_range (0, _dbus_string_get_length (mutated));
272
273   _dbus_string_delete (mutated, i, 1);
274 }
275
276
277 static void
278 randomly_add_one_byte (const DBusString *orig_data,
279                        DBusString       *mutated)
280 {
281   int i;
282
283   if (orig_data != mutated)
284     {
285       _dbus_string_set_length (mutated, 0);
286       
287       if (!_dbus_string_copy (orig_data, 0, mutated, 0))
288         _dbus_assert_not_reached ("out of mem");
289     }
290
291   i = random_int_in_range (0, _dbus_string_get_length (mutated));
292
293   _dbus_string_insert_byte (mutated, i,
294                             random_int_in_range (0, 256));
295 }
296
297 static void
298 randomly_modify_length (const DBusString *orig_data,
299                         DBusString       *mutated)
300 {
301   int i;
302   int byte_order;
303   const char *d;
304   dbus_uint32_t orig;
305   int delta;
306   
307   if (orig_data != mutated)
308     {
309       _dbus_string_set_length (mutated, 0);
310       
311       if (!_dbus_string_copy (orig_data, 0, mutated, 0))
312         _dbus_assert_not_reached ("out of mem");
313     }
314
315   if (_dbus_string_get_length (mutated) < 12)
316     return;
317   
318   _dbus_string_get_const_data (mutated, &d);
319
320   if (!(*d == DBUS_LITTLE_ENDIAN ||
321         *d == DBUS_BIG_ENDIAN))
322     return;
323
324   byte_order = *d;
325   
326   i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
327   i = _DBUS_ALIGN_VALUE (i, 4);
328
329   orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
330
331   delta = random_int_in_range (-10, 10);  
332   
333   _dbus_marshal_set_uint32 (mutated, byte_order, i,
334                             (unsigned) (orig + delta));
335 }
336
337 static void
338 randomly_do_n_things (const DBusString *orig_data,
339                       DBusString       *mutated,
340                       int               n)
341 {
342   int i;
343   void (* functions[]) (const DBusString *orig_data,
344                         DBusString       *mutated) =
345     {
346       randomly_shorten_or_lengthen,
347       randomly_change_one_byte,
348       randomly_add_one_byte,
349       randomly_remove_one_byte,
350       randomly_modify_length
351     };
352
353   _dbus_string_set_length (mutated, 0);
354
355   if (!_dbus_string_copy (orig_data, 0, mutated, 0))
356     _dbus_assert_not_reached ("out of mem");
357
358   i = 0;
359   while (i < n)
360     {
361       int which;
362
363       which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions));
364
365       (* functions[which]) (mutated, mutated);
366       
367       ++i;
368     }
369 }
370
371 static dbus_bool_t
372 find_breaks_based_on (const DBusString   *filename,
373                       dbus_bool_t         is_raw,
374                       DBusMessageValidity expected_validity,
375                       void               *data)
376 {
377   DBusString orig_data;
378   DBusString mutated;
379   const char *filename_c;
380   dbus_bool_t retval;
381   int i;
382
383   _dbus_string_get_const_data (filename, &filename_c);
384
385   retval = FALSE;
386
387   if (!_dbus_string_init (&orig_data, _DBUS_INT_MAX))
388     _dbus_assert_not_reached ("could not allocate string\n");
389
390   if (!_dbus_string_init (&mutated, _DBUS_INT_MAX))
391     _dbus_assert_not_reached ("could not allocate string\n");
392
393   if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
394                                                    &orig_data))
395     {
396       fprintf (stderr, "could not load file %s\n", filename_c);
397       goto failed;
398     }
399
400   i = 0;
401   while (i < 100)
402     {
403       randomly_change_one_byte (&orig_data, &mutated);
404       try_mutated_data (&mutated);
405
406       ++i;
407     }
408
409   i = 0;
410   while (i < 50)
411     {
412       randomly_modify_length (&orig_data, &mutated);
413       try_mutated_data (&mutated);
414
415       ++i;
416     }
417   
418   i = 0;
419   while (i < 50)
420     {
421       randomly_remove_one_byte (&orig_data, &mutated);
422       try_mutated_data (&mutated);
423
424       ++i;
425     }
426
427   i = 0;
428   while (i < 50)
429     {
430       randomly_add_one_byte (&orig_data, &mutated);
431       try_mutated_data (&mutated);
432
433       ++i;
434     }
435   
436   i = 0;
437   while (i < 15)
438     {
439       randomly_shorten_or_lengthen (&orig_data, &mutated);
440       try_mutated_data (&mutated);
441
442       ++i;
443     }
444
445   i = 0;
446   while (i < 42)
447     {
448       randomly_do_n_things (&orig_data, &mutated, 2);
449       try_mutated_data (&mutated);
450
451       ++i;
452     }
453
454   i = 0;
455   while (i < 42)
456     {
457       randomly_do_n_things (&orig_data, &mutated, 3);
458       try_mutated_data (&mutated);
459
460       ++i;
461     }
462
463   i = 0;
464   while (i < 42)
465     {
466       randomly_do_n_things (&orig_data, &mutated, 4);
467       try_mutated_data (&mutated);
468
469       ++i;
470     }
471   
472   retval = TRUE;
473   
474  failed:
475
476   _dbus_string_free (&orig_data);
477   _dbus_string_free (&mutated);
478
479   /* FALSE means end the whole process */
480   return retval;
481 }
482
483 static unsigned int
484 get_random_seed (void)
485 {
486   DBusString bytes;
487   unsigned int seed;
488   int fd;
489   const char *s;
490
491   seed = 0;
492
493   if (!_dbus_string_init (&bytes, _DBUS_INT_MAX))
494     exit (1);
495
496   fd = open ("/dev/urandom", O_RDONLY);
497   if (fd < 0)
498     goto use_fallback;
499
500   if (_dbus_read (fd, &bytes, 4) != 4)
501     goto use_fallback;
502
503   close (fd);
504
505   _dbus_string_get_const_data (&bytes, &s);
506
507   seed = * (unsigned int*) s;
508   goto out;
509
510  use_fallback:
511   {
512     long tv_usec;
513
514     fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n");
515
516     _dbus_get_current_time (NULL, &tv_usec);
517
518     seed = tv_usec;
519   }
520
521  out:
522   _dbus_string_free (&bytes);
523
524   return seed;
525 }
526
527 int
528 main (int    argc,
529       char **argv)
530 {
531   const char *test_data_dir;
532   const char *failure_dir_c;
533   int total_failures_found;
534   
535   if (argc > 1)
536     test_data_dir = argv[1];
537   else
538     {
539       fprintf (stderr, "Must specify a top_srcdir/test/data directory\n");
540       return 1;
541     }
542
543   total_failures_found = 0;
544   total_attempts = 0;
545
546   if (!_dbus_string_init (&failure_dir, _DBUS_INT_MAX))
547     return 1;
548
549   /* so you can leave it overnight safely */
550 #define MAX_FAILURES 1000
551
552   while (total_failures_found < MAX_FAILURES)
553     {
554       unsigned int seed;
555
556       failures_this_iteration = 0;
557
558       seed = get_random_seed ();
559
560       _dbus_string_set_length (&failure_dir, 0);
561
562       if (!_dbus_string_append (&failure_dir, "failures-"))
563         return 1;
564
565       if (!_dbus_string_append_uint (&failure_dir, seed))
566         return 1;
567
568       _dbus_string_get_const_data (&failure_dir, &failure_dir_c);
569
570       if (mkdir (failure_dir_c, 0700) < 0)
571         {
572           if (errno != EEXIST)
573             fprintf (stderr, "didn't mkdir %s: %s\n",
574                      failure_dir_c, strerror (errno));
575         }
576
577       printf ("next seed = %u \ttotal failures %d of %d attempts\n",
578               seed, total_failures_found, total_attempts);
579
580       srand (seed);
581
582       if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
583                                                           find_breaks_based_on,
584                                                           NULL))
585         {
586           fprintf (stderr, "fatal error iterating over message files\n");
587           rmdir (failure_dir_c);
588           return 1;
589         }
590
591       printf ("Found %d failures with seed %u stored in %s\n",
592               failures_this_iteration, seed, failure_dir_c);
593
594       total_failures_found += failures_this_iteration;
595
596       rmdir (failure_dir_c); /* does nothing if non-empty */
597     }
598
599   return 0;
600 }