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
PoisonRedZones(const Global & g)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
GetAlignedSize(uptr size)58 static uptr GetAlignedSize(uptr size) {
59 return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
60 * kGlobalAndStackRedzone;
61 }
62
DescribeAddressIfGlobal(uptr addr)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.
RegisterGlobal(const Global * g)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
UnregisterGlobal(const Global * g)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.
PoisonGlobalAndRedzones(const Global * g)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
UnpoisonGlobal(const Global * g)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.
__asan_register_global(uptr addr,uptr size,const char * name)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.
__asan_register_globals(__asan_global * globals,uptr n)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.
__asan_unregister_globals(__asan_global * globals,uptr n)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.
__asan_before_dynamic_init(uptr first_addr,uptr last_addr)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.
__asan_after_dynamic_init()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