Home | History | Annotate | Download | only in asan
      1 //===-- asan_globals.cc ---------------------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of AddressSanitizer, an address sanity checker.
     11 //
     12 // Handle globals.
     13 //===----------------------------------------------------------------------===//
     14 #include "asan_interceptors.h"
     15 #include "asan_internal.h"
     16 #include "asan_lock.h"
     17 #include "asan_mapping.h"
     18 #include "asan_report.h"
     19 #include "asan_stack.h"
     20 #include "asan_stats.h"
     21 #include "asan_thread.h"
     22 #include "sanitizer/asan_interface.h"
     23 
     24 namespace __asan {
     25 
     26 typedef __asan_global Global;
     27 
     28 struct ListOfGlobals {
     29   const Global *g;
     30   ListOfGlobals *next;
     31 };
     32 
     33 static AsanLock mu_for_globals(LINKER_INITIALIZED);
     34 static LowLevelAllocator allocator_for_globals;
     35 static ListOfGlobals *list_of_all_globals;
     36 static ListOfGlobals *list_of_dynamic_init_globals;
     37 
     38 void PoisonRedZones(const Global &g)  {
     39   uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE;
     40   CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4);
     41   // full right redzone
     42   uptr g_aligned_size = kGlobalAndStackRedzone *
     43       ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone);
     44   PoisonShadow(g.beg + g_aligned_size,
     45                kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic);
     46   if ((g.size % kGlobalAndStackRedzone) != 0) {
     47     // partial right redzone
     48     u64 g_aligned_down_size = kGlobalAndStackRedzone *
     49         (g.size / kGlobalAndStackRedzone);
     50     CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone);
     51     PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size,
     52                                     g.size % kGlobalAndStackRedzone,
     53                                     kGlobalAndStackRedzone,
     54                                     kAsanGlobalRedzoneMagic);
     55   }
     56 }
     57 
     58 static uptr GetAlignedSize(uptr size) {
     59   return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
     60       * kGlobalAndStackRedzone;
     61 }
     62 
     63 bool DescribeAddressIfGlobal(uptr addr) {
     64   if (!flags()->report_globals) return false;
     65   ScopedLock lock(&mu_for_globals);
     66   bool res = false;
     67   for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
     68     const Global &g = *l->g;
     69     if (flags()->report_globals >= 2)
     70       Report("Search Global: beg=%p size=%zu name=%s\n",
     71              (void*)g.beg, g.size, (char*)g.name);
     72     res |= DescribeAddressRelativeToGlobal(addr, g);
     73   }
     74   return res;
     75 }
     76 
     77 // Register a global variable.
     78 // This function may be called more than once for every global
     79 // so we store the globals in a map.
     80 static void RegisterGlobal(const Global *g) {
     81   CHECK(asan_inited);
     82   if (flags()->report_globals >= 2)
     83     Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n",
     84            (void*)g->beg, g->size, g->size_with_redzone, g->name,
     85            g->has_dynamic_init);
     86   CHECK(flags()->report_globals);
     87   CHECK(AddrIsInMem(g->beg));
     88   CHECK(AddrIsAlignedByGranularity(g->beg));
     89   CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
     90   PoisonRedZones(*g);
     91   ListOfGlobals *l =
     92       (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
     93   l->g = g;
     94   l->next = list_of_all_globals;
     95   list_of_all_globals = l;
     96   if (g->has_dynamic_init) {
     97     l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
     98     l->g = g;
     99     l->next = list_of_dynamic_init_globals;
    100     list_of_dynamic_init_globals = l;
    101   }
    102 }
    103 
    104 static void UnregisterGlobal(const Global *g) {
    105   CHECK(asan_inited);
    106   CHECK(flags()->report_globals);
    107   CHECK(AddrIsInMem(g->beg));
    108   CHECK(AddrIsAlignedByGranularity(g->beg));
    109   CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
    110   PoisonShadow(g->beg, g->size_with_redzone, 0);
    111   // We unpoison the shadow memory for the global but we do not remove it from
    112   // the list because that would require O(n^2) time with the current list
    113   // implementation. It might not be worth doing anyway.
    114 }
    115 
    116 // Poison all shadow memory for a single global.
    117 static void PoisonGlobalAndRedzones(const Global *g) {
    118   CHECK(asan_inited);
    119   CHECK(flags()->check_initialization_order);
    120   CHECK(AddrIsInMem(g->beg));
    121   CHECK(AddrIsAlignedByGranularity(g->beg));
    122   CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
    123   if (flags()->report_globals >= 3)
    124     Printf("DynInitPoison  : %s\n", g->name);
    125   PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic);
    126 }
    127 
    128 static void UnpoisonGlobal(const Global *g) {
    129   CHECK(asan_inited);
    130   CHECK(flags()->check_initialization_order);
    131   CHECK(AddrIsInMem(g->beg));
    132   CHECK(AddrIsAlignedByGranularity(g->beg));
    133   CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
    134   if (flags()->report_globals >= 3)
    135     Printf("DynInitUnpoison: %s\n", g->name);
    136   PoisonShadow(g->beg, g->size_with_redzone, 0);
    137   PoisonRedZones(*g);
    138 }
    139 
    140 }  // namespace __asan
    141 
    142 // ---------------------- Interface ---------------- {{{1
    143 using namespace __asan;  // NOLINT
    144 
    145 // Register one global with a default redzone.
    146 void __asan_register_global(uptr addr, uptr size,
    147                             const char *name) {
    148   if (!flags()->report_globals) return;
    149   ScopedLock lock(&mu_for_globals);
    150   Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global));
    151   g->beg = addr;
    152   g->size = size;
    153   g->size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone;
    154   g->name = name;
    155   RegisterGlobal(g);
    156 }
    157 
    158 // Register an array of globals.
    159 void __asan_register_globals(__asan_global *globals, uptr n) {
    160   if (!flags()->report_globals) return;
    161   ScopedLock lock(&mu_for_globals);
    162   for (uptr i = 0; i < n; i++) {
    163     RegisterGlobal(&globals[i]);
    164   }
    165 }
    166 
    167 // Unregister an array of globals.
    168 // We must do this when a shared objects gets dlclosed.
    169 void __asan_unregister_globals(__asan_global *globals, uptr n) {
    170   if (!flags()->report_globals) return;
    171   ScopedLock lock(&mu_for_globals);
    172   for (uptr i = 0; i < n; i++) {
    173     UnregisterGlobal(&globals[i]);
    174   }
    175 }
    176 
    177 // This method runs immediately prior to dynamic initialization in each TU,
    178 // when all dynamically initialized globals are unpoisoned.  This method
    179 // poisons all global variables not defined in this TU, so that a dynamic
    180 // initializer can only touch global variables in the same TU.
    181 void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) {
    182   if (!flags()->check_initialization_order) return;
    183   CHECK(list_of_dynamic_init_globals);
    184   ScopedLock lock(&mu_for_globals);
    185   bool from_current_tu = false;
    186   // The list looks like:
    187   // a => ... => b => last_addr => ... => first_addr => c => ...
    188   // The globals of the current TU reside between last_addr and first_addr.
    189   for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) {
    190     if (l->g->beg == last_addr)
    191       from_current_tu = true;
    192     if (!from_current_tu)
    193       PoisonGlobalAndRedzones(l->g);
    194     if (l->g->beg == first_addr)
    195       from_current_tu = false;
    196   }
    197   CHECK(!from_current_tu);
    198 }
    199 
    200 // This method runs immediately after dynamic initialization in each TU, when
    201 // all dynamically initialized globals except for those defined in the current
    202 // TU are poisoned.  It simply unpoisons all dynamically initialized globals.
    203 void __asan_after_dynamic_init() {
    204   if (!flags()->check_initialization_order) return;
    205   ScopedLock lock(&mu_for_globals);
    206   for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next)
    207     UnpoisonGlobal(l->g);
    208 }
    209