[ostree] tests: Run all tests through a randomized readdir()



commit ce49264157f9005e664557613cbbf67f54174682
Author: Colin Walters <walters verbum org>
Date:   Mon Jun 1 22:34:14 2015 -0400

    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(+), 0 deletions(-)
---
diff --git a/Makefile-tests.am b/Makefile-tests.am
index 955cef1..68dcb9b 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 ba435c0..b788bc9 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 0000000..70a001f
--- /dev/null
+++ b/tests/readdir-rand.c
@@ -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);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]