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