summaryrefslogtreecommitdiffstats
path: root/libsanitizer/asan/asan_globals.cc
blob: 88aeefa8fe5e75ba24e49388a28d43d488a334aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
//===-- asan_globals.cc ---------------------------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Handle globals.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
#include "sanitizer/asan_interface.h"
#include "sanitizer_common/sanitizer_mutex.h"

namespace __asan {

typedef __asan_global Global;

struct ListOfGlobals {
  const Global *g;
  ListOfGlobals *next;
};

static BlockingMutex mu_for_globals(LINKER_INITIALIZED);
static LowLevelAllocator allocator_for_globals;
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;
  CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4);
  // full right redzone
  uptr g_aligned_size = kGlobalAndStackRedzone *
      ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone);
  PoisonShadow(g.beg + g_aligned_size,
               kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic);
  if ((g.size % kGlobalAndStackRedzone) != 0) {
    // partial right redzone
    u64 g_aligned_down_size = kGlobalAndStackRedzone *
        (g.size / kGlobalAndStackRedzone);
    CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone);
    PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size,
                                    g.size % kGlobalAndStackRedzone,
                                    kGlobalAndStackRedzone,
                                    kAsanGlobalRedzoneMagic);
  }
}

bool DescribeAddressIfGlobal(uptr addr) {
  if (!flags()->report_globals) return false;
  BlockingMutexLock lock(&mu_for_globals);
  bool res = false;
  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",
             (void*)g.beg, g.size, (char*)g.name);
    res |= DescribeAddressRelativeToGlobal(addr, g);
  }
  return res;
}

// Register a global variable.
// This function may be called more than once for every global
// 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));
  CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
  PoisonRedZones(*g);
  ListOfGlobals *l =
      (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
  l->g = g;
  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) {
  CHECK(asan_inited);
  CHECK(flags()->report_globals);
  CHECK(AddrIsInMem(g->beg));
  CHECK(AddrIsAlignedByGranularity(g->beg));
  CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
  PoisonShadow(g->beg, g->size_with_redzone, 0);
  // We unpoison the shadow memory for the global but we do not remove it from
  // the list because that would require O(n^2) time with the current list
  // 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
using namespace __asan;  // NOLINT

// Register an array of globals.
void __asan_register_globals(__asan_global *globals, uptr n) {
  if (!flags()->report_globals) return;
  BlockingMutexLock lock(&mu_for_globals);
  for (uptr i = 0; i < n; i++) {
    RegisterGlobal(&globals[i]);
  }
}

// Unregister an array of globals.
// We must do this when a shared objects gets dlclosed.
void __asan_unregister_globals(__asan_global *globals, uptr n) {
  if (!flags()->report_globals) return;
  BlockingMutexLock lock(&mu_for_globals);
  for (uptr i = 0; i < n; i++) {
    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);
  BlockingMutexLock 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;
  BlockingMutexLock lock(&mu_for_globals);
  for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next)
    UnpoisonGlobal(l->g);
}
OpenPOWER on IntegriCloud