764 lines
18 KiB
C
764 lines
18 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
/* dbus-break-loader.c Program to find byte streams that break the message loader
|
|
*
|
|
* Copyright (C) 2003 Red Hat Inc.
|
|
*
|
|
* Licensed under the Academic Free License version 2.1
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <dbus/dbus.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/wait.h>
|
|
#include <string.h>
|
|
|
|
#define DBUS_COMPILATION
|
|
#include <dbus/dbus-string.h>
|
|
#include <dbus/dbus-internals.h>
|
|
#include <dbus/dbus-test.h>
|
|
#include <dbus/dbus-marshal-basic.h>
|
|
#undef DBUS_COMPILATION
|
|
|
|
static DBusString failure_dir;
|
|
static int total_attempts;
|
|
static int failures_this_iteration;
|
|
|
|
static int
|
|
random_int_in_range (int start,
|
|
int end)
|
|
{
|
|
/* such elegant math */
|
|
double gap;
|
|
double v_double;
|
|
int v;
|
|
|
|
if (start == end)
|
|
return start;
|
|
|
|
_dbus_assert (end > start);
|
|
|
|
gap = end - start - 1; /* -1 to not include "end" */
|
|
v_double = ((double)start) + (((double)rand ())/RAND_MAX) * gap;
|
|
if (v_double < 0.0)
|
|
v = (v_double - 0.5);
|
|
else
|
|
v = (v_double + 0.5);
|
|
|
|
if (v < start)
|
|
{
|
|
fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
|
|
v, start, end);
|
|
v = start;
|
|
}
|
|
else if (v >= end)
|
|
{
|
|
fprintf (stderr, "random_int_in_range() generated %d for range [%d,%d)\n",
|
|
v, start, end);
|
|
v = end - 1;
|
|
}
|
|
|
|
/* printf (" %d of [%d,%d)\n", v, start, end); */
|
|
|
|
return v;
|
|
}
|
|
|
|
static dbus_bool_t
|
|
try_mutated_data (const DBusString *data)
|
|
{
|
|
int pid;
|
|
|
|
total_attempts += 1;
|
|
/* printf (" attempt %d\n", total_attempts); */
|
|
|
|
pid = fork ();
|
|
|
|
if (pid < 0)
|
|
{
|
|
fprintf (stderr, "fork() failed: %s\n",
|
|
strerror (errno));
|
|
exit (1);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pid == 0)
|
|
{
|
|
/* Child, try loading the data */
|
|
if (!dbus_internal_do_not_use_try_message_data (data, _DBUS_MESSAGE_UNKNOWN))
|
|
exit (1);
|
|
else
|
|
exit (0);
|
|
}
|
|
else
|
|
{
|
|
/* Parent, wait for child */
|
|
int status;
|
|
DBusString filename;
|
|
dbus_bool_t failed;
|
|
|
|
if (waitpid (pid, &status, 0) < 0)
|
|
{
|
|
fprintf (stderr, "waitpid() failed: %s\n", strerror (errno));
|
|
exit (1);
|
|
return FALSE;
|
|
}
|
|
|
|
failed = FALSE;
|
|
|
|
if (!_dbus_string_init (&filename) ||
|
|
!_dbus_string_copy (&failure_dir, 0,
|
|
&filename, 0) ||
|
|
!_dbus_string_append_byte (&filename, '/'))
|
|
{
|
|
fprintf (stderr, "out of memory\n");
|
|
exit (1);
|
|
}
|
|
|
|
_dbus_string_append_int (&filename, total_attempts);
|
|
|
|
if (WIFEXITED (status))
|
|
{
|
|
if (WEXITSTATUS (status) != 0)
|
|
{
|
|
_dbus_string_append (&filename, "-exited-");
|
|
_dbus_string_append_int (&filename, WEXITSTATUS (status));
|
|
failed = TRUE;
|
|
}
|
|
}
|
|
else if (WIFSIGNALED (status))
|
|
{
|
|
_dbus_string_append (&filename, "signaled-");
|
|
_dbus_string_append_int (&filename, WTERMSIG (status));
|
|
failed = TRUE;
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
DBusError error;
|
|
|
|
_dbus_string_append (&filename, ".message-raw");
|
|
|
|
printf ("Child failed, writing %s\n", _dbus_string_get_const_data (&filename));
|
|
|
|
dbus_error_init (&error);
|
|
if (!_dbus_string_save_to_file (data, &filename, FALSE, &error))
|
|
{
|
|
fprintf (stderr, "Failed to save failed message data: %s\n",
|
|
error.message);
|
|
dbus_error_free (&error);
|
|
exit (1); /* so we can see the seed that was printed out */
|
|
}
|
|
|
|
failures_this_iteration += 1;
|
|
|
|
_dbus_string_free (&filename);
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
_dbus_string_free (&filename);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
_dbus_assert_not_reached ("should not be reached");
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
randomly_shorten_or_lengthen (const DBusString *orig_data,
|
|
DBusString *mutated)
|
|
{
|
|
int delta;
|
|
|
|
if (orig_data != mutated)
|
|
{
|
|
_dbus_string_set_length (mutated, 0);
|
|
|
|
if (!_dbus_string_copy (orig_data, 0, mutated, 0))
|
|
_dbus_assert_not_reached ("out of mem");
|
|
}
|
|
|
|
if (_dbus_string_get_length (mutated) == 0)
|
|
delta = random_int_in_range (0, 10);
|
|
else
|
|
delta = random_int_in_range (- _dbus_string_get_length (mutated),
|
|
_dbus_string_get_length (mutated) * 3);
|
|
|
|
if (delta < 0)
|
|
_dbus_string_shorten (mutated, - delta);
|
|
else if (delta > 0)
|
|
{
|
|
int i = 0;
|
|
|
|
i = _dbus_string_get_length (mutated);
|
|
if (!_dbus_string_lengthen (mutated, delta))
|
|
_dbus_assert_not_reached ("couldn't lengthen string");
|
|
|
|
while (i < _dbus_string_get_length (mutated))
|
|
{
|
|
_dbus_string_set_byte (mutated,
|
|
i,
|
|
random_int_in_range (0, 256));
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
randomly_change_one_byte (const DBusString *orig_data,
|
|
DBusString *mutated)
|
|
{
|
|
int i;
|
|
|
|
if (orig_data != mutated)
|
|
{
|
|
_dbus_string_set_length (mutated, 0);
|
|
|
|
if (!_dbus_string_copy (orig_data, 0, mutated, 0))
|
|
_dbus_assert_not_reached ("out of mem");
|
|
}
|
|
|
|
if (_dbus_string_get_length (mutated) == 0)
|
|
return;
|
|
|
|
i = random_int_in_range (0, _dbus_string_get_length (mutated));
|
|
|
|
_dbus_string_set_byte (mutated, i,
|
|
random_int_in_range (0, 256));
|
|
}
|
|
|
|
static void
|
|
randomly_remove_one_byte (const DBusString *orig_data,
|
|
DBusString *mutated)
|
|
{
|
|
int i;
|
|
|
|
if (orig_data != mutated)
|
|
{
|
|
_dbus_string_set_length (mutated, 0);
|
|
|
|
if (!_dbus_string_copy (orig_data, 0, mutated, 0))
|
|
_dbus_assert_not_reached ("out of mem");
|
|
}
|
|
|
|
if (_dbus_string_get_length (mutated) == 0)
|
|
return;
|
|
|
|
i = random_int_in_range (0, _dbus_string_get_length (mutated));
|
|
|
|
_dbus_string_delete (mutated, i, 1);
|
|
}
|
|
|
|
|
|
static void
|
|
randomly_add_one_byte (const DBusString *orig_data,
|
|
DBusString *mutated)
|
|
{
|
|
int i;
|
|
|
|
if (orig_data != mutated)
|
|
{
|
|
_dbus_string_set_length (mutated, 0);
|
|
|
|
if (!_dbus_string_copy (orig_data, 0, mutated, 0))
|
|
_dbus_assert_not_reached ("out of mem");
|
|
}
|
|
|
|
i = random_int_in_range (0, _dbus_string_get_length (mutated));
|
|
|
|
_dbus_string_insert_bytes (mutated, i, 1,
|
|
random_int_in_range (0, 256));
|
|
}
|
|
|
|
static void
|
|
randomly_modify_length (const DBusString *orig_data,
|
|
DBusString *mutated)
|
|
{
|
|
int i;
|
|
int byte_order;
|
|
const char *d;
|
|
dbus_uint32_t orig;
|
|
int delta;
|
|
|
|
if (orig_data != mutated)
|
|
{
|
|
_dbus_string_set_length (mutated, 0);
|
|
|
|
if (!_dbus_string_copy (orig_data, 0, mutated, 0))
|
|
_dbus_assert_not_reached ("out of mem");
|
|
}
|
|
|
|
if (_dbus_string_get_length (mutated) < 12)
|
|
return;
|
|
|
|
d = _dbus_string_get_const_data (mutated);
|
|
|
|
if (!(*d == DBUS_LITTLE_ENDIAN ||
|
|
*d == DBUS_BIG_ENDIAN))
|
|
return;
|
|
|
|
byte_order = *d;
|
|
|
|
i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
|
|
i = _DBUS_ALIGN_VALUE (i, 4);
|
|
|
|
orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
|
|
|
|
delta = random_int_in_range (-10, 10);
|
|
|
|
_dbus_marshal_set_uint32 (mutated, byte_order, i,
|
|
(unsigned) (orig + delta));
|
|
}
|
|
|
|
static void
|
|
randomly_set_extreme_ints (const DBusString *orig_data,
|
|
DBusString *mutated)
|
|
{
|
|
int i;
|
|
int byte_order;
|
|
const char *d;
|
|
dbus_uint32_t orig;
|
|
static int which = 0;
|
|
unsigned int extreme_ints[] = {
|
|
_DBUS_INT_MAX,
|
|
_DBUS_UINT_MAX,
|
|
_DBUS_INT_MAX - 1,
|
|
_DBUS_UINT_MAX - 1,
|
|
_DBUS_INT_MAX - 2,
|
|
_DBUS_UINT_MAX - 2,
|
|
_DBUS_INT_MAX - 17,
|
|
_DBUS_UINT_MAX - 17,
|
|
_DBUS_INT_MAX / 2,
|
|
_DBUS_INT_MAX / 3,
|
|
_DBUS_UINT_MAX / 2,
|
|
_DBUS_UINT_MAX / 3,
|
|
0, 1, 2, 3,
|
|
(unsigned int) -1,
|
|
(unsigned int) -2,
|
|
(unsigned int) -3
|
|
};
|
|
|
|
if (orig_data != mutated)
|
|
{
|
|
_dbus_string_set_length (mutated, 0);
|
|
|
|
if (!_dbus_string_copy (orig_data, 0, mutated, 0))
|
|
_dbus_assert_not_reached ("out of mem");
|
|
}
|
|
|
|
if (_dbus_string_get_length (mutated) < 12)
|
|
return;
|
|
|
|
d = _dbus_string_get_const_data (mutated);
|
|
|
|
if (!(*d == DBUS_LITTLE_ENDIAN ||
|
|
*d == DBUS_BIG_ENDIAN))
|
|
return;
|
|
|
|
byte_order = *d;
|
|
|
|
i = random_int_in_range (4, _dbus_string_get_length (mutated) - 8);
|
|
i = _DBUS_ALIGN_VALUE (i, 4);
|
|
|
|
orig = _dbus_demarshal_uint32 (mutated, byte_order, i, NULL);
|
|
|
|
which = random_int_in_range (0, _DBUS_N_ELEMENTS (extreme_ints));
|
|
|
|
_dbus_assert (which >= 0);
|
|
_dbus_assert (which < _DBUS_N_ELEMENTS (extreme_ints));
|
|
|
|
_dbus_marshal_set_uint32 (mutated, byte_order, i,
|
|
extreme_ints[which]);
|
|
}
|
|
|
|
static int
|
|
random_type (void)
|
|
{
|
|
const char types[] = {
|
|
DBUS_TYPE_INVALID,
|
|
DBUS_TYPE_NIL,
|
|
DBUS_TYPE_BYTE,
|
|
DBUS_TYPE_BOOLEAN,
|
|
DBUS_TYPE_INT32,
|
|
DBUS_TYPE_UINT32,
|
|
DBUS_TYPE_INT64,
|
|
DBUS_TYPE_UINT64,
|
|
DBUS_TYPE_DOUBLE,
|
|
DBUS_TYPE_STRING,
|
|
DBUS_TYPE_CUSTOM,
|
|
DBUS_TYPE_ARRAY,
|
|
DBUS_TYPE_DICT,
|
|
DBUS_TYPE_OBJECT_PATH
|
|
};
|
|
|
|
_dbus_assert (_DBUS_N_ELEMENTS (types) == DBUS_NUMBER_OF_TYPES + 1);
|
|
|
|
return types[ random_int_in_range (0, _DBUS_N_ELEMENTS (types)) ];
|
|
}
|
|
|
|
static void
|
|
randomly_change_one_type (const DBusString *orig_data,
|
|
DBusString *mutated)
|
|
{
|
|
int i;
|
|
int len;
|
|
|
|
if (orig_data != mutated)
|
|
{
|
|
_dbus_string_set_length (mutated, 0);
|
|
|
|
if (!_dbus_string_copy (orig_data, 0, mutated, 0))
|
|
_dbus_assert_not_reached ("out of mem");
|
|
}
|
|
|
|
if (_dbus_string_get_length (mutated) == 0)
|
|
return;
|
|
|
|
len = _dbus_string_get_length (mutated);
|
|
i = random_int_in_range (0, len);
|
|
|
|
/* Look for a type starting at a random location,
|
|
* and replace with a different type
|
|
*/
|
|
while (i < len)
|
|
{
|
|
int b;
|
|
b = _dbus_string_get_byte (mutated, i);
|
|
if (dbus_type_is_valid (b))
|
|
{
|
|
_dbus_string_set_byte (mutated, i, random_type ());
|
|
return;
|
|
}
|
|
++i;
|
|
}
|
|
}
|
|
|
|
static int times_we_did_each_thing[7] = { 0, };
|
|
|
|
static void
|
|
randomly_do_n_things (const DBusString *orig_data,
|
|
DBusString *mutated,
|
|
int n)
|
|
{
|
|
int i;
|
|
void (* functions[]) (const DBusString *orig_data,
|
|
DBusString *mutated) =
|
|
{
|
|
randomly_shorten_or_lengthen,
|
|
randomly_change_one_byte,
|
|
randomly_add_one_byte,
|
|
randomly_remove_one_byte,
|
|
randomly_modify_length,
|
|
randomly_set_extreme_ints,
|
|
randomly_change_one_type
|
|
};
|
|
|
|
_dbus_string_set_length (mutated, 0);
|
|
|
|
if (!_dbus_string_copy (orig_data, 0, mutated, 0))
|
|
_dbus_assert_not_reached ("out of mem");
|
|
|
|
i = 0;
|
|
while (i < n)
|
|
{
|
|
int which;
|
|
|
|
which = random_int_in_range (0, _DBUS_N_ELEMENTS (functions));
|
|
|
|
(* functions[which]) (mutated, mutated);
|
|
times_we_did_each_thing[which] += 1;
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
static dbus_bool_t
|
|
find_breaks_based_on (const DBusString *filename,
|
|
dbus_bool_t is_raw,
|
|
DBusMessageValidity expected_validity,
|
|
void *data)
|
|
{
|
|
DBusString orig_data;
|
|
DBusString mutated;
|
|
const char *filename_c;
|
|
dbus_bool_t retval;
|
|
int i;
|
|
|
|
filename_c = _dbus_string_get_const_data (filename);
|
|
|
|
retval = FALSE;
|
|
|
|
if (!_dbus_string_init (&orig_data))
|
|
_dbus_assert_not_reached ("could not allocate string\n");
|
|
|
|
if (!_dbus_string_init (&mutated))
|
|
_dbus_assert_not_reached ("could not allocate string\n");
|
|
|
|
if (!dbus_internal_do_not_use_load_message_file (filename, is_raw,
|
|
&orig_data))
|
|
{
|
|
fprintf (stderr, "could not load file %s\n", filename_c);
|
|
goto failed;
|
|
}
|
|
|
|
printf (" changing one random byte 100 times\n");
|
|
i = 0;
|
|
while (i < 100)
|
|
{
|
|
randomly_change_one_byte (&orig_data, &mutated);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" changing length 50 times\n");
|
|
i = 0;
|
|
while (i < 50)
|
|
{
|
|
randomly_modify_length (&orig_data, &mutated);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" removing one byte 50 times\n");
|
|
i = 0;
|
|
while (i < 50)
|
|
{
|
|
randomly_remove_one_byte (&orig_data, &mutated);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" adding one byte 50 times\n");
|
|
i = 0;
|
|
while (i < 50)
|
|
{
|
|
randomly_add_one_byte (&orig_data, &mutated);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" changing ints to boundary values 50 times\n");
|
|
i = 0;
|
|
while (i < 50)
|
|
{
|
|
randomly_set_extreme_ints (&orig_data, &mutated);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" changing typecodes 50 times\n");
|
|
i = 0;
|
|
while (i < 50)
|
|
{
|
|
randomly_change_one_type (&orig_data, &mutated);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" changing message length 15 times\n");
|
|
i = 0;
|
|
while (i < 15)
|
|
{
|
|
randomly_shorten_or_lengthen (&orig_data, &mutated);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" randomly making 2 of above modifications 42 times\n");
|
|
i = 0;
|
|
while (i < 42)
|
|
{
|
|
randomly_do_n_things (&orig_data, &mutated, 2);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" randomly making 3 of above modifications 42 times\n");
|
|
i = 0;
|
|
while (i < 42)
|
|
{
|
|
randomly_do_n_things (&orig_data, &mutated, 3);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
printf (" randomly making 4 of above modifications 42 times\n");
|
|
i = 0;
|
|
while (i < 42)
|
|
{
|
|
randomly_do_n_things (&orig_data, &mutated, 4);
|
|
try_mutated_data (&mutated);
|
|
|
|
++i;
|
|
}
|
|
|
|
retval = TRUE;
|
|
|
|
failed:
|
|
|
|
_dbus_string_free (&orig_data);
|
|
_dbus_string_free (&mutated);
|
|
|
|
/* FALSE means end the whole process */
|
|
return retval;
|
|
}
|
|
|
|
static unsigned int
|
|
get_random_seed (void)
|
|
{
|
|
DBusString bytes;
|
|
unsigned int seed;
|
|
int fd;
|
|
const char *s;
|
|
|
|
seed = 0;
|
|
|
|
if (!_dbus_string_init (&bytes))
|
|
exit (1);
|
|
|
|
fd = open ("/dev/urandom", O_RDONLY);
|
|
if (fd < 0)
|
|
goto use_fallback;
|
|
|
|
if (_dbus_read (fd, &bytes, 4) != 4)
|
|
goto use_fallback;
|
|
|
|
close (fd);
|
|
|
|
s = _dbus_string_get_const_data (&bytes);
|
|
|
|
seed = * (unsigned int*) s;
|
|
goto out;
|
|
|
|
use_fallback:
|
|
{
|
|
long tv_usec;
|
|
|
|
fprintf (stderr, "could not open/read /dev/urandom, using current time for seed\n");
|
|
|
|
_dbus_get_monotonic_time (NULL, &tv_usec);
|
|
|
|
seed = tv_usec;
|
|
}
|
|
|
|
out:
|
|
_dbus_string_free (&bytes);
|
|
|
|
return seed;
|
|
}
|
|
|
|
int
|
|
main (int argc,
|
|
char **argv)
|
|
{
|
|
const char *test_data_dir;
|
|
const char *failure_dir_c;
|
|
int total_failures_found;
|
|
|
|
if (argc > 1)
|
|
test_data_dir = argv[1];
|
|
else
|
|
{
|
|
fprintf (stderr, "Must specify a top_srcdir/test/data directory\n");
|
|
return 1;
|
|
}
|
|
|
|
total_failures_found = 0;
|
|
total_attempts = 0;
|
|
|
|
if (!_dbus_string_init (&failure_dir))
|
|
return 1;
|
|
|
|
/* so you can leave it overnight safely */
|
|
#define MAX_FAILURES 1000
|
|
|
|
while (total_failures_found < MAX_FAILURES)
|
|
{
|
|
unsigned int seed;
|
|
|
|
failures_this_iteration = 0;
|
|
|
|
seed = get_random_seed ();
|
|
|
|
_dbus_string_set_length (&failure_dir, 0);
|
|
|
|
if (!_dbus_string_append (&failure_dir, "failures-"))
|
|
return 1;
|
|
|
|
if (!_dbus_string_append_uint (&failure_dir, seed))
|
|
return 1;
|
|
|
|
failure_dir_c = _dbus_string_get_const_data (&failure_dir);
|
|
|
|
if (mkdir (failure_dir_c, 0700) < 0)
|
|
{
|
|
if (errno != EEXIST)
|
|
fprintf (stderr, "didn't mkdir %s: %s\n",
|
|
failure_dir_c, strerror (errno));
|
|
}
|
|
|
|
printf ("next seed = %u \ttotal failures %d of %d attempts\n",
|
|
seed, total_failures_found, total_attempts);
|
|
|
|
srand (seed);
|
|
|
|
if (!dbus_internal_do_not_use_foreach_message_file (test_data_dir,
|
|
find_breaks_based_on,
|
|
NULL))
|
|
{
|
|
fprintf (stderr, "fatal error iterating over message files\n");
|
|
rmdir (failure_dir_c);
|
|
return 1;
|
|
}
|
|
|
|
printf (" did %d random mutations: %d %d %d %d %d %d %d\n",
|
|
_DBUS_N_ELEMENTS (times_we_did_each_thing),
|
|
times_we_did_each_thing[0],
|
|
times_we_did_each_thing[1],
|
|
times_we_did_each_thing[2],
|
|
times_we_did_each_thing[3],
|
|
times_we_did_each_thing[4],
|
|
times_we_did_each_thing[5],
|
|
times_we_did_each_thing[6]);
|
|
|
|
printf ("Found %d failures with seed %u stored in %s\n",
|
|
failures_this_iteration, seed, failure_dir_c);
|
|
|
|
total_failures_found += failures_this_iteration;
|
|
|
|
rmdir (failure_dir_c); /* does nothing if non-empty */
|
|
}
|
|
|
|
return 0;
|
|
}
|