asan_globals.cc revision 3945c58f9db42671b1a3b865fde5008f09a3a40e
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_interface.h"
16#include "asan_internal.h"
17#include "asan_lock.h"
18#include "asan_mapping.h"
19#include "asan_report.h"
20#include "asan_stack.h"
21#include "asan_stats.h"
22#include "asan_thread.h"
23
24namespace __asan {
25
26typedef __asan_global Global;
27
28struct ListOfGlobals {
29  const Global *g;
30  ListOfGlobals *next;
31};
32
33static AsanLock mu_for_globals(LINKER_INITIALIZED);
34static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED);
35static ListOfGlobals *list_of_all_globals;
36static ListOfGlobals *list_of_dynamic_init_globals;
37
38void 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
58static uptr GetAlignedSize(uptr size) {
59  return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
60      * kGlobalAndStackRedzone;
61}
62
63bool 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.
80static 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
104static 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.
117static 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
128static 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
143using namespace __asan;  // NOLINT
144
145// Register one global with a default redzone.
146void __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.
159void __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.
169void __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.
181void __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.
203void __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