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:
parent
20bf7692a9
commit
ce49264157
|
|
@ -113,6 +113,12 @@ insttest_SCRIPTS += tests/test-core.js \
|
||||||
testmeta_DATA += test-core.test test-sizes.test test-sysroot.test
|
testmeta_DATA += test-core.test test-sizes.test test-sysroot.test
|
||||||
endif
|
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
|
endif
|
||||||
|
|
||||||
# "make check" do not depend from --enable-installed-tests
|
# "make check" do not depend from --enable-installed-tests
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,8 @@ fi
|
||||||
|
|
||||||
if test -n "$OT_TESTS_VALGRIND"; then
|
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"
|
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
|
fi
|
||||||
|
|
||||||
assert_not_reached () {
|
assert_not_reached () {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue