tests: Run all tests through a randomized readdir()

Having undefined ordering (but in practice rarely changing)
ordering for `readdir()` ended up screwing us over with respect
to bootloader config file read ordering.

Let's make things significantly more likely to fail more quickly in
the future if similar bugs are introduced.  We accomplish this by
introducing a little `LD_PRELOAD` library that randomizes the results
of `readdir()`.
This commit is contained in:
Colin Walters 2015-06-01 22:34:14 -04:00
parent 20bf7692a9
commit ce49264157
3 changed files with 132 additions and 0 deletions

View File

@ -113,6 +113,12 @@ insttest_SCRIPTS += tests/test-core.js \
testmeta_DATA += test-core.test test-sizes.test test-sysroot.test
endif
insttest_LTLIBRARIES = libreaddir-rand.la
libreaddir_rand_la_SOURCES = tests/readdir-rand.c
libreaddir_rand_la_CFLAGS = $(OT_INTERNAL_GIO_UNIX_CFLAGS)
libreaddir_rand_la_LIBADD = $(OT_INTERNAL_GIO_UNIX_LIBS)
libreaddir_rand_la_LDFLAGS = -avoid-version
endif
# "make check" do not depend from --enable-installed-tests

View File

@ -39,6 +39,8 @@ fi
if test -n "$OT_TESTS_VALGRIND"; then
CMD_PREFIX="env G_SLICE=always-malloc valgrind -q --leak-check=full --num-callers=30 --suppressions=${SRCDIR}/ostree-valgrind.supp"
else
CMD_PREFIX="env LD_PRELOAD=${SRCDIR}/libreaddir-rand.so"
fi
assert_not_reached () {

124
tests/readdir-rand.c Normal file
View File

@ -0,0 +1,124 @@
#define _GNU_SOURCE
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <glib.h>
struct linux_dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
#define BUF_SIZE 1024
static GHashTable *direntcache;
static GMutex direntcache_lock;
static gsize initialized;
typedef struct {
GPtrArray *entries;
guint offset;
} DirEntries;
static void
dir_entries_free (gpointer data)
{
DirEntries *d = data;
g_ptr_array_unref (d->entries);
g_free (d);
}
static DirEntries *
dir_entries_new (void)
{
DirEntries *d = g_new0 (DirEntries, 1);
d->entries = g_ptr_array_new_with_free_func (g_free);
return d;
}
static void
ensure_initialized (void)
{
if (g_once_init_enter (&initialized))
{
direntcache = g_hash_table_new_full (NULL, NULL, NULL, dir_entries_free);
g_mutex_init (&direntcache_lock);
g_once_init_leave (&initialized, 1);
}
}
struct dirent *
readdir (DIR *dirp)
{
struct dirent *(*real_readdir)(DIR *dirp) = dlsym (RTLD_NEXT, "readdir");
struct dirent *ret;
gboolean doloop = TRUE;
ensure_initialized ();
while (doloop)
{
DirEntries *de;
GSList *l;
errno = 0;
ret = real_readdir (dirp);
if (ret == NULL && errno != 0)
goto out;
g_mutex_lock (&direntcache_lock);
de = g_hash_table_lookup (direntcache, dirp);
if (ret)
{
if (g_random_boolean ())
{
if (!de)
{
de = dir_entries_new ();
g_hash_table_insert (direntcache, dirp, de);
}
struct dirent *copy;
copy = g_memdup (ret, sizeof (struct dirent));
g_ptr_array_add (de->entries, copy);
}
else
{
doloop = FALSE;
}
}
else
{
if (de && de->offset < de->entries->len)
{
ret = de->entries->pdata[de->offset];
de->offset++;
}
doloop = FALSE;
}
g_mutex_unlock (&direntcache_lock);
}
out:
return ret;
}
int
closedir (DIR *dirp)
{
int (*real_closedir)(DIR *dirp) = dlsym (RTLD_NEXT, "closedir");
ensure_initialized ();
g_mutex_lock (&direntcache_lock);
g_hash_table_remove (direntcache, dirp);
g_mutex_unlock (&direntcache_lock);
return real_closedir (dirp);
}