1 | //===-- asan_globals.cpp --------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of AddressSanitizer, an address sanity checker. |
10 | // |
11 | // Handle globals. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "asan_interceptors.h" |
15 | #include "asan_internal.h" |
16 | #include "asan_mapping.h" |
17 | #include "asan_poisoning.h" |
18 | #include "asan_report.h" |
19 | #include "asan_stack.h" |
20 | #include "asan_stats.h" |
21 | #include "asan_suppressions.h" |
22 | #include "asan_thread.h" |
23 | #include "sanitizer_common/sanitizer_common.h" |
24 | #include "sanitizer_common/sanitizer_mutex.h" |
25 | #include "sanitizer_common/sanitizer_placement_new.h" |
26 | #include "sanitizer_common/sanitizer_stackdepot.h" |
27 | #include "sanitizer_common/sanitizer_symbolizer.h" |
28 | |
29 | namespace __asan { |
30 | |
31 | typedef __asan_global Global; |
32 | |
33 | struct ListOfGlobals { |
34 | const Global *g; |
35 | ListOfGlobals *next; |
36 | }; |
37 | |
38 | static Mutex mu_for_globals; |
39 | static ListOfGlobals *list_of_all_globals; |
40 | |
41 | static const int kDynamicInitGlobalsInitialCapacity = 512; |
42 | struct DynInitGlobal { |
43 | Global g; |
44 | bool initialized; |
45 | }; |
46 | typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals; |
47 | // Lazy-initialized and never deleted. |
48 | static VectorOfGlobals *dynamic_init_globals; |
49 | |
50 | // We want to remember where a certain range of globals was registered. |
51 | struct GlobalRegistrationSite { |
52 | u32 stack_id; |
53 | Global *g_first, *g_last; |
54 | }; |
55 | typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector; |
56 | static GlobalRegistrationSiteVector *global_registration_site_vector; |
57 | |
58 | ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { |
59 | FastPoisonShadow(aligned_beg: g->beg, aligned_size: g->size_with_redzone, value); |
60 | } |
61 | |
62 | ALWAYS_INLINE void PoisonRedZones(const Global &g) { |
63 | uptr aligned_size = RoundUpTo(size: g.size, ASAN_SHADOW_GRANULARITY); |
64 | FastPoisonShadow(aligned_beg: g.beg + aligned_size, aligned_size: g.size_with_redzone - aligned_size, |
65 | value: kAsanGlobalRedzoneMagic); |
66 | if (g.size != aligned_size) { |
67 | FastPoisonShadowPartialRightRedzone( |
68 | aligned_addr: g.beg + RoundDownTo(x: g.size, ASAN_SHADOW_GRANULARITY), |
69 | size: g.size % ASAN_SHADOW_GRANULARITY, ASAN_SHADOW_GRANULARITY, |
70 | value: kAsanGlobalRedzoneMagic); |
71 | } |
72 | } |
73 | |
74 | const uptr kMinimalDistanceFromAnotherGlobal = 64; |
75 | |
76 | static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { |
77 | if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; |
78 | if (addr >= g.beg + g.size_with_redzone) return false; |
79 | return true; |
80 | } |
81 | |
82 | static void ReportGlobal(const Global &g, const char *prefix) { |
83 | DataInfo info; |
84 | bool symbolized = Symbolizer::GetOrInit()->SymbolizeData(address: g.beg, info: &info); |
85 | Report( |
86 | format: "%s Global[%p]: beg=%p size=%zu/%zu name=%s source=%s module=%s " |
87 | "dyn_init=%zu " |
88 | "odr_indicator=%p\n" , |
89 | prefix, (void *)&g, (void *)g.beg, g.size, g.size_with_redzone, g.name, |
90 | g.module_name, (symbolized ? info.module : "?" ), g.has_dynamic_init, |
91 | (void *)g.odr_indicator); |
92 | |
93 | if (symbolized && info.line != 0) { |
94 | Report(format: " location: name=%s, %d\n" , info.file, static_cast<int>(info.line)); |
95 | } else if (g.gcc_location != 0) { |
96 | // Fallback to Global::gcc_location |
97 | Report(format: " location: name=%s, %d\n" , g.gcc_location->filename, g.gcc_location->line_no); |
98 | } |
99 | } |
100 | |
101 | static u32 FindRegistrationSite(const Global *g) { |
102 | mu_for_globals.CheckLocked(); |
103 | CHECK(global_registration_site_vector); |
104 | for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) { |
105 | GlobalRegistrationSite &grs = (*global_registration_site_vector)[i]; |
106 | if (g >= grs.g_first && g <= grs.g_last) |
107 | return grs.stack_id; |
108 | } |
109 | return 0; |
110 | } |
111 | |
112 | int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites, |
113 | int max_globals) { |
114 | if (!flags()->report_globals) return 0; |
115 | Lock lock(&mu_for_globals); |
116 | int res = 0; |
117 | for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { |
118 | const Global &g = *l->g; |
119 | if (flags()->report_globals >= 2) |
120 | ReportGlobal(g, prefix: "Search" ); |
121 | if (IsAddressNearGlobal(addr, g)) { |
122 | internal_memcpy(dest: &globals[res], src: &g, n: sizeof(g)); |
123 | if (reg_sites) |
124 | reg_sites[res] = FindRegistrationSite(g: &g); |
125 | res++; |
126 | if (res == max_globals) |
127 | break; |
128 | } |
129 | } |
130 | return res; |
131 | } |
132 | |
133 | enum GlobalSymbolState { |
134 | UNREGISTERED = 0, |
135 | REGISTERED = 1 |
136 | }; |
137 | |
138 | // Check ODR violation for given global G via special ODR indicator. We use |
139 | // this method in case compiler instruments global variables through their |
140 | // local aliases. |
141 | static void CheckODRViolationViaIndicator(const Global *g) { |
142 | // Instrumentation requests to skip ODR check. |
143 | if (g->odr_indicator == UINTPTR_MAX) |
144 | return; |
145 | u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator); |
146 | if (*odr_indicator == UNREGISTERED) { |
147 | *odr_indicator = REGISTERED; |
148 | return; |
149 | } |
150 | // If *odr_indicator is DEFINED, some module have already registered |
151 | // externally visible symbol with the same name. This is an ODR violation. |
152 | for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { |
153 | if (g->odr_indicator == l->g->odr_indicator && |
154 | (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && |
155 | !IsODRViolationSuppressed(global_var_name: g->name)) |
156 | ReportODRViolation(g1: g, stack_id1: FindRegistrationSite(g), |
157 | g2: l->g, stack_id2: FindRegistrationSite(g: l->g)); |
158 | } |
159 | } |
160 | |
161 | // Check ODR violation for given global G by checking if it's already poisoned. |
162 | // We use this method in case compiler doesn't use private aliases for global |
163 | // variables. |
164 | static void CheckODRViolationViaPoisoning(const Global *g) { |
165 | if (__asan_region_is_poisoned(beg: g->beg, size: g->size_with_redzone)) { |
166 | // This check may not be enough: if the first global is much larger |
167 | // the entire redzone of the second global may be within the first global. |
168 | for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { |
169 | if (g->beg == l->g->beg && |
170 | (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && |
171 | !IsODRViolationSuppressed(global_var_name: g->name)) |
172 | ReportODRViolation(g1: g, stack_id1: FindRegistrationSite(g), |
173 | g2: l->g, stack_id2: FindRegistrationSite(g: l->g)); |
174 | } |
175 | } |
176 | } |
177 | |
178 | // Clang provides two different ways for global variables protection: |
179 | // it can poison the global itself or its private alias. In former |
180 | // case we may poison same symbol multiple times, that can help us to |
181 | // cheaply detect ODR violation: if we try to poison an already poisoned |
182 | // global, we have ODR violation error. |
183 | // In latter case, we poison each symbol exactly once, so we use special |
184 | // indicator symbol to perform similar check. |
185 | // In either case, compiler provides a special odr_indicator field to Global |
186 | // structure, that can contain two kinds of values: |
187 | // 1) Non-zero value. In this case, odr_indicator is an address of |
188 | // corresponding indicator variable for given global. |
189 | // 2) Zero. This means that we don't use private aliases for global variables |
190 | // and can freely check ODR violation with the first method. |
191 | // |
192 | // This routine chooses between two different methods of ODR violation |
193 | // detection. |
194 | static inline bool UseODRIndicator(const Global *g) { |
195 | return g->odr_indicator > 0; |
196 | } |
197 | |
198 | // Register a global variable. |
199 | // This function may be called more than once for every global |
200 | // so we store the globals in a map. |
201 | static void RegisterGlobal(const Global *g) { |
202 | CHECK(AsanInited()); |
203 | if (flags()->report_globals >= 2) |
204 | ReportGlobal(g: *g, prefix: "Added" ); |
205 | CHECK(flags()->report_globals); |
206 | CHECK(AddrIsInMem(g->beg)); |
207 | if (!AddrIsAlignedByGranularity(a: g->beg)) { |
208 | Report(format: "The following global variable is not properly aligned.\n" ); |
209 | Report(format: "This may happen if another global with the same name\n" ); |
210 | Report(format: "resides in another non-instrumented module.\n" ); |
211 | Report(format: "Or the global comes from a C file built w/o -fno-common.\n" ); |
212 | Report(format: "In either case this is likely an ODR violation bug,\n" ); |
213 | Report(format: "but AddressSanitizer can not provide more details.\n" ); |
214 | ReportODRViolation(g1: g, stack_id1: FindRegistrationSite(g), g2: g, stack_id2: FindRegistrationSite(g)); |
215 | CHECK(AddrIsAlignedByGranularity(g->beg)); |
216 | } |
217 | CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); |
218 | if (flags()->detect_odr_violation) { |
219 | // Try detecting ODR (One Definition Rule) violation, i.e. the situation |
220 | // where two globals with the same name are defined in different modules. |
221 | if (UseODRIndicator(g)) |
222 | CheckODRViolationViaIndicator(g); |
223 | else |
224 | CheckODRViolationViaPoisoning(g); |
225 | } |
226 | if (CanPoisonMemory()) |
227 | PoisonRedZones(g: *g); |
228 | ListOfGlobals *l = new (GetGlobalLowLevelAllocator()) ListOfGlobals; |
229 | l->g = g; |
230 | l->next = list_of_all_globals; |
231 | list_of_all_globals = l; |
232 | if (g->has_dynamic_init) { |
233 | if (!dynamic_init_globals) { |
234 | dynamic_init_globals = new (GetGlobalLowLevelAllocator()) VectorOfGlobals; |
235 | dynamic_init_globals->reserve(new_size: kDynamicInitGlobalsInitialCapacity); |
236 | } |
237 | DynInitGlobal dyn_global = { .g: *g, .initialized: false }; |
238 | dynamic_init_globals->push_back(element: dyn_global); |
239 | } |
240 | } |
241 | |
242 | static void UnregisterGlobal(const Global *g) { |
243 | CHECK(AsanInited()); |
244 | if (flags()->report_globals >= 2) |
245 | ReportGlobal(g: *g, prefix: "Removed" ); |
246 | CHECK(flags()->report_globals); |
247 | CHECK(AddrIsInMem(g->beg)); |
248 | CHECK(AddrIsAlignedByGranularity(g->beg)); |
249 | CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); |
250 | if (CanPoisonMemory()) |
251 | PoisonShadowForGlobal(g, value: 0); |
252 | // We unpoison the shadow memory for the global but we do not remove it from |
253 | // the list because that would require O(n^2) time with the current list |
254 | // implementation. It might not be worth doing anyway. |
255 | |
256 | // Release ODR indicator. |
257 | if (UseODRIndicator(g) && g->odr_indicator != UINTPTR_MAX) { |
258 | u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator); |
259 | *odr_indicator = UNREGISTERED; |
260 | } |
261 | } |
262 | |
263 | void StopInitOrderChecking() { |
264 | Lock lock(&mu_for_globals); |
265 | if (!flags()->check_initialization_order || !dynamic_init_globals) |
266 | return; |
267 | flags()->check_initialization_order = false; |
268 | for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { |
269 | DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; |
270 | const Global *g = &dyn_g.g; |
271 | // Unpoison the whole global. |
272 | PoisonShadowForGlobal(g, value: 0); |
273 | // Poison redzones back. |
274 | PoisonRedZones(g: *g); |
275 | } |
276 | } |
277 | |
278 | static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; } |
279 | |
280 | const char *MaybeDemangleGlobalName(const char *name) { |
281 | // We can spoil names of globals with C linkage, so use an heuristic |
282 | // approach to check if the name should be demangled. |
283 | bool should_demangle = false; |
284 | if (name[0] == '_' && name[1] == 'Z') |
285 | should_demangle = true; |
286 | else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?') |
287 | should_demangle = true; |
288 | |
289 | return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name; |
290 | } |
291 | |
292 | // Check if the global is a zero-terminated ASCII string. If so, print it. |
293 | void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) { |
294 | for (uptr p = g.beg; p < g.beg + g.size - 1; p++) { |
295 | unsigned char c = *(unsigned char *)p; |
296 | if (c == '\0' || !IsASCII(c)) return; |
297 | } |
298 | if (*(char *)(g.beg + g.size - 1) != '\0') return; |
299 | str->AppendF(format: " '%s' is ascii string '%s'\n" , MaybeDemangleGlobalName(name: g.name), |
300 | (char *)g.beg); |
301 | } |
302 | |
303 | void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g, |
304 | bool print_module_name) { |
305 | DataInfo info; |
306 | if (Symbolizer::GetOrInit()->SymbolizeData(address: g.beg, info: &info) && info.line != 0) { |
307 | str->AppendF(format: "%s:%d" , info.file, static_cast<int>(info.line)); |
308 | } else if (g.gcc_location != 0) { |
309 | // Fallback to Global::gcc_location |
310 | str->AppendF(format: "%s" , g.gcc_location->filename ? g.gcc_location->filename |
311 | : g.module_name); |
312 | if (g.gcc_location->line_no) |
313 | str->AppendF(format: ":%d" , g.gcc_location->line_no); |
314 | if (g.gcc_location->column_no) |
315 | str->AppendF(format: ":%d" , g.gcc_location->column_no); |
316 | } else { |
317 | str->AppendF(format: "%s" , g.module_name); |
318 | } |
319 | if (print_module_name && info.module) |
320 | str->AppendF(format: " in %s" , info.module); |
321 | } |
322 | |
323 | } // namespace __asan |
324 | |
325 | // ---------------------- Interface ---------------- {{{1 |
326 | using namespace __asan; |
327 | |
328 | // Apply __asan_register_globals to all globals found in the same loaded |
329 | // executable or shared library as `flag'. The flag tracks whether globals have |
330 | // already been registered or not for this image. |
331 | void __asan_register_image_globals(uptr *flag) { |
332 | if (*flag) |
333 | return; |
334 | AsanApplyToGlobals(op: __asan_register_globals, needle: flag); |
335 | *flag = 1; |
336 | } |
337 | |
338 | // This mirrors __asan_register_image_globals. |
339 | void __asan_unregister_image_globals(uptr *flag) { |
340 | if (!*flag) |
341 | return; |
342 | AsanApplyToGlobals(op: __asan_unregister_globals, needle: flag); |
343 | *flag = 0; |
344 | } |
345 | |
346 | void __asan_register_elf_globals(uptr *flag, void *start, void *stop) { |
347 | if (*flag || start == stop) |
348 | return; |
349 | CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); |
350 | __asan_global *globals_start = (__asan_global*)start; |
351 | __asan_global *globals_stop = (__asan_global*)stop; |
352 | __asan_register_globals(globals: globals_start, n: globals_stop - globals_start); |
353 | *flag = 1; |
354 | } |
355 | |
356 | void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) { |
357 | if (!*flag || start == stop) |
358 | return; |
359 | CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global)); |
360 | __asan_global *globals_start = (__asan_global*)start; |
361 | __asan_global *globals_stop = (__asan_global*)stop; |
362 | __asan_unregister_globals(globals: globals_start, n: globals_stop - globals_start); |
363 | *flag = 0; |
364 | } |
365 | |
366 | // Register an array of globals. |
367 | void __asan_register_globals(__asan_global *globals, uptr n) { |
368 | if (!flags()->report_globals) return; |
369 | GET_STACK_TRACE_MALLOC; |
370 | u32 stack_id = StackDepotPut(stack); |
371 | Lock lock(&mu_for_globals); |
372 | if (!global_registration_site_vector) { |
373 | global_registration_site_vector = |
374 | new (GetGlobalLowLevelAllocator()) GlobalRegistrationSiteVector; |
375 | global_registration_site_vector->reserve(new_size: 128); |
376 | } |
377 | GlobalRegistrationSite site = {.stack_id: stack_id, .g_first: &globals[0], .g_last: &globals[n - 1]}; |
378 | global_registration_site_vector->push_back(element: site); |
379 | if (flags()->report_globals >= 2) { |
380 | PRINT_CURRENT_STACK(); |
381 | Printf(format: "=== ID %d; %p %p\n" , stack_id, (void *)&globals[0], |
382 | (void *)&globals[n - 1]); |
383 | } |
384 | for (uptr i = 0; i < n; i++) { |
385 | if (SANITIZER_WINDOWS && globals[i].beg == 0) { |
386 | // The MSVC incremental linker may pad globals out to 256 bytes. As long |
387 | // as __asan_global is less than 256 bytes large and its size is a power |
388 | // of two, we can skip over the padding. |
389 | static_assert( |
390 | sizeof(__asan_global) < 256 && |
391 | (sizeof(__asan_global) & (sizeof(__asan_global) - 1)) == 0, |
392 | "sizeof(__asan_global) incompatible with incremental linker padding" ); |
393 | // If these are padding bytes, the rest of the global should be zero. |
394 | CHECK(globals[i].size == 0 && globals[i].size_with_redzone == 0 && |
395 | globals[i].name == nullptr && globals[i].module_name == nullptr && |
396 | globals[i].odr_indicator == 0); |
397 | continue; |
398 | } |
399 | RegisterGlobal(g: &globals[i]); |
400 | } |
401 | |
402 | // Poison the metadata. It should not be accessible to user code. |
403 | PoisonShadow(addr: reinterpret_cast<uptr>(globals), size: n * sizeof(__asan_global), |
404 | value: kAsanGlobalRedzoneMagic); |
405 | } |
406 | |
407 | // Unregister an array of globals. |
408 | // We must do this when a shared objects gets dlclosed. |
409 | void __asan_unregister_globals(__asan_global *globals, uptr n) { |
410 | if (!flags()->report_globals) return; |
411 | Lock lock(&mu_for_globals); |
412 | for (uptr i = 0; i < n; i++) { |
413 | if (SANITIZER_WINDOWS && globals[i].beg == 0) { |
414 | // Skip globals that look like padding from the MSVC incremental linker. |
415 | // See comment in __asan_register_globals. |
416 | continue; |
417 | } |
418 | UnregisterGlobal(g: &globals[i]); |
419 | } |
420 | |
421 | // Unpoison the metadata. |
422 | PoisonShadow(addr: reinterpret_cast<uptr>(globals), size: n * sizeof(__asan_global), value: 0); |
423 | } |
424 | |
425 | // This method runs immediately prior to dynamic initialization in each TU, |
426 | // when all dynamically initialized globals are unpoisoned. This method |
427 | // poisons all global variables not defined in this TU, so that a dynamic |
428 | // initializer can only touch global variables in the same TU. |
429 | void __asan_before_dynamic_init(const char *module_name) { |
430 | if (!flags()->check_initialization_order || |
431 | !CanPoisonMemory() || |
432 | !dynamic_init_globals) |
433 | return; |
434 | bool strict_init_order = flags()->strict_init_order; |
435 | CHECK(module_name); |
436 | CHECK(AsanInited()); |
437 | Lock lock(&mu_for_globals); |
438 | if (flags()->report_globals >= 3) |
439 | Printf(format: "DynInitPoison module: %s\n" , module_name); |
440 | for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { |
441 | DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; |
442 | const Global *g = &dyn_g.g; |
443 | if (dyn_g.initialized) |
444 | continue; |
445 | if (g->module_name != module_name) |
446 | PoisonShadowForGlobal(g, value: kAsanInitializationOrderMagic); |
447 | else if (!strict_init_order) |
448 | dyn_g.initialized = true; |
449 | } |
450 | } |
451 | |
452 | // This method runs immediately after dynamic initialization in each TU, when |
453 | // all dynamically initialized globals except for those defined in the current |
454 | // TU are poisoned. It simply unpoisons all dynamically initialized globals. |
455 | void __asan_after_dynamic_init() { |
456 | if (!flags()->check_initialization_order || |
457 | !CanPoisonMemory() || |
458 | !dynamic_init_globals) |
459 | return; |
460 | CHECK(AsanInited()); |
461 | Lock lock(&mu_for_globals); |
462 | // FIXME: Optionally report that we're unpoisoning globals from a module. |
463 | for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { |
464 | DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; |
465 | const Global *g = &dyn_g.g; |
466 | if (!dyn_g.initialized) { |
467 | // Unpoison the whole global. |
468 | PoisonShadowForGlobal(g, value: 0); |
469 | // Poison redzones back. |
470 | PoisonRedZones(g: *g); |
471 | } |
472 | } |
473 | } |
474 | |