[asan] run-time part of the initialization order checker. Patch by Reid Watson with some bits from kcc. The sub-pass is off by default for now. On simple tests it works fine. 

git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@162278 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h
index e259f11..d58a833 100644
--- a/lib/asan/asan_flags.h
+++ b/lib/asan/asan_flags.h
@@ -43,6 +43,8 @@
   // on globals, 1 - detect buffer overflow, 2 - print data about registered
   // globals).
   int  report_globals;
+  // If set, attempts to catch initialization order issues.
+  bool check_initialization_order;
   // Max number of stack frames kept for each allocation.
   int  malloc_context_size;
   // If set, uses custom wrappers and replacements for libc string functions
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index 87e9909..ff403b5 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -31,8 +31,9 @@
 };
 
 static AsanLock mu_for_globals(LINKER_INITIALIZED);
-static ListOfGlobals *list_of_globals;
 static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED);
+static ListOfGlobals *list_of_all_globals;
+static ListOfGlobals *list_of_dynamic_init_globals;
 
 void PoisonRedZones(const Global &g)  {
   uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE;
@@ -63,7 +64,7 @@
   if (!flags()->report_globals) return false;
   ScopedLock lock(&mu_for_globals);
   bool res = false;
-  for (ListOfGlobals *l = list_of_globals; l; l = l->next) {
+  for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
     const Global &g = *l->g;
     if (flags()->report_globals >= 2)
       Report("Search Global: beg=%p size=%zu name=%s\n",
@@ -78,6 +79,10 @@
 // so we store the globals in a map.
 static void RegisterGlobal(const Global *g) {
   CHECK(asan_inited);
+  if (flags()->report_globals >= 2)
+    Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n",
+           (void*)g->beg, g->size, g->size_with_redzone, g->name,
+           g->has_dynamic_init);
   CHECK(flags()->report_globals);
   CHECK(AddrIsInMem(g->beg));
   CHECK(AddrIsAlignedByGranularity(g->beg));
@@ -86,11 +91,14 @@
   ListOfGlobals *l =
       (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
   l->g = g;
-  l->next = list_of_globals;
-  list_of_globals = l;
-  if (flags()->report_globals >= 2)
-    Report("Added Global: beg=%p size=%zu name=%s\n",
-           (void*)g->beg, g->size, g->name);
+  l->next = list_of_all_globals;
+  list_of_all_globals = l;
+  if (g->has_dynamic_init) {
+    l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
+    l->g = g;
+    l->next = list_of_dynamic_init_globals;
+    list_of_dynamic_init_globals = l;
+  }
 }
 
 static void UnregisterGlobal(const Global *g) {
@@ -105,6 +113,30 @@
   // implementation. It might not be worth doing anyway.
 }
 
+// Poison all shadow memory for a single global.
+static void PoisonGlobalAndRedzones(const Global *g) {
+  CHECK(asan_inited);
+  CHECK(flags()->check_initialization_order);
+  CHECK(AddrIsInMem(g->beg));
+  CHECK(AddrIsAlignedByGranularity(g->beg));
+  CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
+  if (flags()->report_globals >= 3)
+    Printf("DynInitPoison  : %s\n", g->name);
+  PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic);
+}
+
+static void UnpoisonGlobal(const Global *g) {
+  CHECK(asan_inited);
+  CHECK(flags()->check_initialization_order);
+  CHECK(AddrIsInMem(g->beg));
+  CHECK(AddrIsAlignedByGranularity(g->beg));
+  CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
+  if (flags()->report_globals >= 3)
+    Printf("DynInitUnpoison: %s\n", g->name);
+  PoisonShadow(g->beg, g->size_with_redzone, 0);
+  PoisonRedZones(*g);
+}
+
 }  // namespace __asan
 
 // ---------------------- Interface ---------------- {{{1
@@ -133,7 +165,7 @@
 }
 
 // Unregister an array of globals.
-// We must do it when a shared objects gets dlclosed.
+// We must do this when a shared objects gets dlclosed.
 void __asan_unregister_globals(__asan_global *globals, uptr n) {
   if (!flags()->report_globals) return;
   ScopedLock lock(&mu_for_globals);
@@ -141,3 +173,36 @@
     UnregisterGlobal(&globals[i]);
   }
 }
+
+// This method runs immediately prior to dynamic initialization in each TU,
+// when all dynamically initialized globals are unpoisoned.  This method
+// poisons all global variables not defined in this TU, so that a dynamic
+// initializer can only touch global variables in the same TU.
+void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) {
+  if (!flags()->check_initialization_order) return;
+  CHECK(list_of_dynamic_init_globals);
+  ScopedLock lock(&mu_for_globals);
+  bool from_current_tu = false;
+  // The list looks like:
+  // a => ... => b => last_addr => ... => first_addr => c => ...
+  // The globals of the current TU reside between last_addr and first_addr.
+  for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) {
+    if (l->g->beg == last_addr)
+      from_current_tu = true;
+    if (!from_current_tu)
+      PoisonGlobalAndRedzones(l->g);
+    if (l->g->beg == first_addr)
+      from_current_tu = false;
+  }
+  CHECK(!from_current_tu);
+}
+
+// This method runs immediately after dynamic initialization in each TU, when
+// all dynamically initialized globals except for those defined in the current
+// TU are poisoned.  It simply unpoisons all dynamically initialized globals.
+void __asan_after_dynamic_init() {
+  if (!flags()->check_initialization_order) return;
+  ScopedLock lock(&mu_for_globals);
+  for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next)
+    UnpoisonGlobal(l->g);
+}
diff --git a/lib/asan/asan_interface.h b/lib/asan/asan_interface.h
index 40ce3cb..c19dc76 100644
--- a/lib/asan/asan_interface.h
+++ b/lib/asan/asan_interface.h
@@ -48,6 +48,14 @@
   void __asan_unregister_globals(__asan_global *globals, uptr n)
       SANITIZER_INTERFACE_ATTRIBUTE;
 
+  // These two functions should be called before and after dynamic initializers
+  // run, respectively.  They should be called with parameters describing all
+  // dynamically initialized globals defined in the calling TU.
+  void __asan_before_dynamic_init(uptr first_addr, uptr last_addr)
+      SANITIZER_INTERFACE_ATTRIBUTE;
+  void __asan_after_dynamic_init()
+      SANITIZER_INTERFACE_ATTRIBUTE;
+
   // These two functions are used by the instrumented code in the
   // use-after-return mode. __asan_stack_malloc allocates size bytes of
   // fake stack and __asan_stack_free poisons it. real_stack is a pointer to
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index 6f6bb84..d9f8866 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -171,6 +171,7 @@
 const int kAsanStackRightRedzoneMagic = 0xf3;
 const int kAsanStackPartialRedzoneMagic = 0xf4;
 const int kAsanStackAfterReturnMagic = 0xf5;
+const int kAsanInitializationOrderMagic = 0xf6;
 const int kAsanUserPoisonedMemoryMagic = 0xf7;
 const int kAsanGlobalRedzoneMagic = 0xf9;
 const int kAsanInternalHeapMagic = 0xfe;
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
index 1cabbcd..310cbe5 100644
--- a/lib/asan/asan_report.cc
+++ b/lib/asan/asan_report.cc
@@ -371,6 +371,9 @@
       case kAsanStackLeftRedzoneMagic:
         bug_descr = "stack-buffer-underflow";
         break;
+      case kAsanInitializationOrderMagic:
+        bug_descr = "initialization-order-fiasco";
+        break;
       case kAsanStackMidRedzoneMagic:
       case kAsanStackRightRedzoneMagic:
       case kAsanStackPartialRedzoneMagic:
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 0f87253..14101b0 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -79,6 +79,7 @@
 
   ParseFlag(str, &f->debug, "debug");
   ParseFlag(str, &f->report_globals, "report_globals");
+  ParseFlag(str, &f->check_initialization_order, "initialization_order");
   ParseFlag(str, &f->malloc_context_size, "malloc_context_size");
   CHECK(f->malloc_context_size <= kMallocContextSize);
 
@@ -116,6 +117,7 @@
   f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128;
   f->debug = false;
   f->report_globals = 1;
+  f->check_initialization_order = true;
   f->malloc_context_size = kMallocContextSize;
   f->replace_str = true;
   f->replace_intrin = true;