[PATCH] Make init-order checker accept access to already initialized globals

Alexey Samsonov samsonov at google.com
Tue Apr 16 09:48:21 PDT 2013


Hi kcc,

This change adds ASan runtime option "strict-init-order" (off by default)
that makes init-order checker bark if global initializer accesses any global from different
translation unit (even if the latter is already initialized). strict init-order checking
doesn't play well with, e.g. LLVM registration machineries, and causes issue 
https://code.google.com/p/address-sanitizer/issues/detail?id=178.

http://llvm-reviews.chandlerc.com/D679

Files:
  lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc
  lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
  lib/asan/lit_tests/init-order-dlopen.cc
  lib/asan/asan_flags.h
  lib/asan/asan_rtl.cc
  lib/asan/asan_globals.cc

Index: lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc
===================================================================
--- /dev/null
+++ lib/asan/lit_tests/SharedLibs/init-order-dlopen-so.cc
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <unistd.h>
+
+void inc_global();
+
+int slow_init() {
+  sleep(1);
+  inc_global();
+  return 42;
+}
+
+int slowly_init_glob = slow_init();
Index: lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
===================================================================
--- lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
+++ lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
@@ -1,12 +1,13 @@
 // Test to make sure basic initialization order errors are caught.
 // Check that on Linux initialization order bugs are caught
-// independently on order in which we list source files.
+// independently on order in which we list source files (if we specify
+// strict init-order checking).
 
 // RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t
-// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \
+// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \
 // RUN:    | %symbolize | FileCheck %s
 // RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t
-// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 \
+// RUN: ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true %t 2>&1 \
 // RUN:    | %symbolize | FileCheck %s
 
 // Do not test with optimization -- the error may be optimized away.
Index: lib/asan/lit_tests/init-order-dlopen.cc
===================================================================
--- /dev/null
+++ lib/asan/lit_tests/init-order-dlopen.cc
@@ -0,0 +1,47 @@
+// Regression test for
+// https://code.google.com/p/address-sanitizer/issues/detail?id=178
+
+// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/init-order-dlopen-so.cc \
+// RUN:     -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O0 %s -o %t -Wl,--export-dynamic
+// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 | FileCheck %s
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+
+#include <string>
+
+using std::string;
+
+int foo() {
+  return 42;
+}
+int global = foo();
+
+__attribute__((visibility("default")))
+void inc_global() {
+  global++;
+}
+
+void *global_poller(void *arg) {
+  while (true) {
+    if (global != 42)
+      break;
+    usleep(100);
+  }
+  return 0;
+}
+
+int main(int argc, char *argv[]) {
+  pthread_t p;
+  pthread_create(&p, 0, global_poller, 0);
+  string path = string(argv[0]) + "-so.so";
+  if (0 == dlopen(path.c_str(), RTLD_NOW)) {
+    fprintf(stderr, "dlerror: %s\n", dlerror());
+    return 1;
+  }
+  pthread_join(p, 0);
+  printf("PASSED\n");
+  // CHECK: PASSED
+  return 0;
+}
Index: lib/asan/asan_flags.h
===================================================================
--- lib/asan/asan_flags.h
+++ lib/asan/asan_flags.h
@@ -113,6 +113,9 @@
   // If true, assume that memcmp(p1, p2, n) always reads n bytes before
   // comparing p1 and p2.
   bool strict_memcmp;
+  // If true, assume that dynamic initializers can never access globals from
+  // other modules, even if the latter are already initialized.
+  bool strict_init_order;
 };
 
 extern Flags asan_flags_dont_use_directly;
Index: lib/asan/asan_rtl.cc
===================================================================
--- lib/asan/asan_rtl.cc
+++ lib/asan/asan_rtl.cc
@@ -125,6 +125,7 @@
   ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch");
   ParseFlag(str, &f->use_stack_depot, "use_stack_depot");
   ParseFlag(str, &f->strict_memcmp, "strict_memcmp");
+  ParseFlag(str, &f->strict_init_order, "strict_init_order");
 }
 
 static const char *asan_external_symbolizer;
@@ -170,6 +171,7 @@
   f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0);;
   f->use_stack_depot = true;  // Only affects allocator2.
   f->strict_memcmp = true;
+  f->strict_init_order = false;
 
   // Override from compile definition.
   ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefiniton());
Index: lib/asan/asan_globals.cc
===================================================================
--- lib/asan/asan_globals.cc
+++ lib/asan/asan_globals.cc
@@ -37,7 +37,11 @@
 static ListOfGlobals *list_of_all_globals;
 
 static const int kDynamicInitGlobalsInitialCapacity = 512;
-typedef InternalVector<Global> VectorOfGlobals;
+struct DynInitGlobal {
+  Global g;
+  bool initialized;
+};
+typedef InternalVector<DynInitGlobal> VectorOfGlobals;
 // Lazy-initialized and never deleted.
 static VectorOfGlobals *dynamic_init_globals;
 
@@ -101,7 +105,8 @@
       dynamic_init_globals = new(mem)
           VectorOfGlobals(kDynamicInitGlobalsInitialCapacity);
     }
-    dynamic_init_globals->push_back(*g);
+    DynInitGlobal dyn_global = { *g, false };
+    dynamic_init_globals->push_back(dyn_global);
   }
 }
 
@@ -150,16 +155,22 @@
   if (!flags()->check_initialization_order ||
       !flags()->poison_heap)
     return;
+  bool strict_init_order = flags()->strict_init_order;
   CHECK(dynamic_init_globals);
   CHECK(module_name);
   CHECK(asan_inited);
   BlockingMutexLock lock(&mu_for_globals);
   if (flags()->report_globals >= 3)
     Printf("DynInitPoison module: %s\n", module_name);
   for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
-    const Global *g = &(*dynamic_init_globals)[i];
-    if (g->module_name != module_name)
+    DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
+    const Global *g = &dyn_g.g;
+    if (dyn_g.initialized)
+      continue;
+    else if (g->module_name != module_name)
       PoisonShadowForGlobal(g, kAsanInitializationOrderMagic);
+    else if (!strict_init_order)
+      dyn_g.initialized = true;
   }
 }
 
@@ -174,10 +185,13 @@
   BlockingMutexLock lock(&mu_for_globals);
   // FIXME: Optionally report that we're unpoisoning globals from a module.
   for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
-    const Global *g = &(*dynamic_init_globals)[i];
-    // Unpoison the whole global.
-    PoisonShadowForGlobal(g, 0);
-    // Poison redzones back.
-    PoisonRedZones(*g);
+    DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
+    const Global *g = &dyn_g.g;
+    if (!dyn_g.initialized) {
+      // Unpoison the whole global.
+      PoisonShadowForGlobal(g, 0);
+      // Poison redzones back.
+      PoisonRedZones(*g);
+    }
   }
 }
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D679.1.patch
Type: text/x-patch
Size: 6441 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20130416/09ddf05e/attachment.bin>


More information about the llvm-commits mailing list