From ce49264157f9005e664557613cbbf67f54174682 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 1 Jun 2015 22:34:14 -0400 Subject: [PATCH] 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()`. --- Makefile-tests.am | 6 +++ tests/libtest.sh | 2 + tests/readdir-rand.c | 124 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 tests/readdir-rand.c diff --git a/Makefile-tests.am b/Makefile-tests.am index 955cef17..68dcb9b0 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -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 diff --git a/tests/libtest.sh b/tests/libtest.sh index ba435c09..b788bc97 100644 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -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 () { diff --git a/tests/readdir-rand.c b/tests/readdir-rand.c new file mode 100644 index 00000000..70a001fe --- /dev/null +++ b/tests/readdir-rand.c @@ -0,0 +1,124 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +}