blob: c34b1d3cedf202345e661195be2ad9b4bf45bd40 [file] [log] [blame]
Alexey Samsonove5f58952012-06-04 13:50:10 +00001//===-- asan_globals.cc ---------------------------------------------------===//
Kostya Serebryany1e172b42011-11-30 01:07:02 +00002//
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"
Kostya Serebryany1e172b42011-11-30 01:07:02 +000015#include "asan_internal.h"
Kostya Serebryany1e172b42011-11-30 01:07:02 +000016#include "asan_mapping.h"
Alexey Samsonov7e843492013-03-28 15:42:43 +000017#include "asan_poisoning.h"
Alexey Samsonove4bfca22012-08-09 09:27:24 +000018#include "asan_report.h"
Kostya Serebryany1e172b42011-11-30 01:07:02 +000019#include "asan_stack.h"
20#include "asan_stats.h"
Stephen Hines86277eb2015-03-23 12:06:32 -070021#include "asan_suppressions.h"
Kostya Serebryany1e172b42011-11-30 01:07:02 +000022#include "asan_thread.h"
Alexey Samsonov7e843492013-03-28 15:42:43 +000023#include "sanitizer_common/sanitizer_common.h"
Dmitry Vyukovf4f51f22013-01-14 07:51:39 +000024#include "sanitizer_common/sanitizer_mutex.h"
Alexey Samsonov7e843492013-03-28 15:42:43 +000025#include "sanitizer_common/sanitizer_placement_new.h"
Stephen Hines6a211c52014-07-21 00:49:56 -070026#include "sanitizer_common/sanitizer_stackdepot.h"
Kostya Serebryany1e172b42011-11-30 01:07:02 +000027
Kostya Serebryany1e172b42011-11-30 01:07:02 +000028namespace __asan {
29
30typedef __asan_global Global;
31
Kostya Serebryanyb89567c2011-12-02 21:02:20 +000032struct ListOfGlobals {
33 const Global *g;
34 ListOfGlobals *next;
35};
36
Dmitry Vyukovf4f51f22013-01-14 07:51:39 +000037static BlockingMutex mu_for_globals(LINKER_INITIALIZED);
Alexey Samsonov947fbd12012-08-27 14:04:54 +000038static LowLevelAllocator allocator_for_globals;
Kostya Serebryany3945c582012-08-21 14:10:25 +000039static ListOfGlobals *list_of_all_globals;
Kostya Serebryany1e172b42011-11-30 01:07:02 +000040
Alexey Samsonov7e843492013-03-28 15:42:43 +000041static const int kDynamicInitGlobalsInitialCapacity = 512;
Alexey Samsonovdfeef672013-04-19 08:35:16 +000042struct DynInitGlobal {
43 Global g;
44 bool initialized;
45};
Alexey Samsonova64d4352013-06-14 09:59:40 +000046typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals;
Alexey Samsonov7e843492013-03-28 15:42:43 +000047// Lazy-initialized and never deleted.
48static VectorOfGlobals *dynamic_init_globals;
49
Stephen Hines6a211c52014-07-21 00:49:56 -070050// We want to remember where a certain range of globals was registered.
51struct GlobalRegistrationSite {
52 u32 stack_id;
53 Global *g_first, *g_last;
54};
55typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector;
56static GlobalRegistrationSiteVector *global_registration_site_vector;
57
Timur Iskhodzhanovabfdbdf2013-03-28 18:52:40 +000058ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
Alexey Samsonov7e843492013-03-28 15:42:43 +000059 FastPoisonShadow(g->beg, g->size_with_redzone, value);
60}
61
Timur Iskhodzhanovabfdbdf2013-03-28 18:52:40 +000062ALWAYS_INLINE void PoisonRedZones(const Global &g) {
Kostya Serebryanya3b0e5e2013-01-23 11:14:21 +000063 uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY);
Alexey Samsonov7e843492013-03-28 15:42:43 +000064 FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size,
65 kAsanGlobalRedzoneMagic);
Kostya Serebryanya3b0e5e2013-01-23 11:14:21 +000066 if (g.size != aligned_size) {
Alexey Samsonov7e843492013-03-28 15:42:43 +000067 FastPoisonShadowPartialRightRedzone(
Kostya Serebryanya3b0e5e2013-01-23 11:14:21 +000068 g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY),
69 g.size % SHADOW_GRANULARITY,
70 SHADOW_GRANULARITY,
71 kAsanGlobalRedzoneMagic);
Kostya Serebryany1e172b42011-11-30 01:07:02 +000072 }
73}
74
Stephen Hines6d186232014-11-26 17:56:19 -080075const uptr kMinimalDistanceFromAnotherGlobal = 64;
76
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -070077static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
Stephen Hines6d186232014-11-26 17:56:19 -080078 if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
79 if (addr >= g.beg + g.size_with_redzone) return false;
80 return true;
Alexey Samsonov05e16a02013-03-26 13:06:12 +000081}
82
Stephen Hines6d186232014-11-26 17:56:19 -080083static void ReportGlobal(const Global &g, const char *prefix) {
84 Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
85 prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
86 g.module_name, g.has_dynamic_init);
87 if (g.location) {
88 Report(" location (%p): name=%s[%p], %d %d\n", g.location,
89 g.location->filename, g.location->filename, g.location->line_no,
90 g.location->column_no);
91 }
92}
93
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -070094static u32 FindRegistrationSite(const Global *g) {
95 mu_for_globals.CheckLocked();
Stephen Hines6a211c52014-07-21 00:49:56 -070096 CHECK(global_registration_site_vector);
97 for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
98 GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
99 if (g >= grs.g_first && g <= grs.g_last)
100 return grs.stack_id;
101 }
102 return 0;
103}
104
Pirama Arumuga Nainarcdce50b2015-07-01 12:26:56 -0700105int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
106 int max_globals) {
107 if (!flags()->report_globals) return 0;
108 BlockingMutexLock lock(&mu_for_globals);
109 int res = 0;
110 for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
111 const Global &g = *l->g;
112 if (flags()->report_globals >= 2)
113 ReportGlobal(g, "Search");
114 if (IsAddressNearGlobal(addr, g)) {
115 globals[res] = g;
116 if (reg_sites)
117 reg_sites[res] = FindRegistrationSite(&g);
118 res++;
119 if (res == max_globals) break;
120 }
121 }
122 return res;
123}
124
125bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
126 Global g = {};
127 if (GetGlobalsForAddress(addr, &g, nullptr, 1)) {
128 internal_strncpy(descr->name, g.name, descr->name_size);
129 descr->region_address = g.beg;
130 descr->region_size = g.size;
131 descr->region_kind = "global";
132 return true;
133 }
134 return false;
135}
136
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000137// Register a global variable.
138// This function may be called more than once for every global
139// so we store the globals in a map.
140static void RegisterGlobal(const Global *g) {
141 CHECK(asan_inited);
Kostya Serebryany3945c582012-08-21 14:10:25 +0000142 if (flags()->report_globals >= 2)
Alexey Samsonov05e16a02013-03-26 13:06:12 +0000143 ReportGlobal(*g, "Added");
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +0000144 CHECK(flags()->report_globals);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000145 CHECK(AddrIsInMem(g->beg));
Kostya Serebryanyb89567c2011-12-02 21:02:20 +0000146 CHECK(AddrIsAlignedByGranularity(g->beg));
Kostya Serebryanyc4910612011-12-15 21:55:34 +0000147 CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700148 if (flags()->detect_odr_violation) {
149 // Try detecting ODR (One Definition Rule) violation, i.e. the situation
150 // where two globals with the same name are defined in different modules.
151 if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) {
152 // This check may not be enough: if the first global is much larger
153 // the entire redzone of the second global may be within the first global.
154 for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
155 if (g->beg == l->g->beg &&
Stephen Hines86277eb2015-03-23 12:06:32 -0700156 (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
157 !IsODRViolationSuppressed(g->name))
Stephen Hines6a211c52014-07-21 00:49:56 -0700158 ReportODRViolation(g, FindRegistrationSite(g),
159 l->g, FindRegistrationSite(l->g));
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700160 }
161 }
162 }
Stephen Hines86277eb2015-03-23 12:06:32 -0700163 if (CanPoisonMemory())
Alexey Samsonov7e843492013-03-28 15:42:43 +0000164 PoisonRedZones(*g);
Peter Collingbourne53177242013-10-24 06:23:39 +0000165 ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals;
Kostya Serebryanyb89567c2011-12-02 21:02:20 +0000166 l->g = g;
Kostya Serebryany3945c582012-08-21 14:10:25 +0000167 l->next = list_of_all_globals;
168 list_of_all_globals = l;
169 if (g->has_dynamic_init) {
Alexey Samsonov7e843492013-03-28 15:42:43 +0000170 if (dynamic_init_globals == 0) {
Peter Collingbourne53177242013-10-24 06:23:39 +0000171 dynamic_init_globals = new(allocator_for_globals)
Alexey Samsonov7e843492013-03-28 15:42:43 +0000172 VectorOfGlobals(kDynamicInitGlobalsInitialCapacity);
173 }
Alexey Samsonovdfeef672013-04-19 08:35:16 +0000174 DynInitGlobal dyn_global = { *g, false };
175 dynamic_init_globals->push_back(dyn_global);
Kostya Serebryany3945c582012-08-21 14:10:25 +0000176 }
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000177}
178
Kostya Serebryanyc4910612011-12-15 21:55:34 +0000179static void UnregisterGlobal(const Global *g) {
180 CHECK(asan_inited);
Stephen Hines86277eb2015-03-23 12:06:32 -0700181 if (flags()->report_globals >= 2)
182 ReportGlobal(*g, "Removed");
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +0000183 CHECK(flags()->report_globals);
Kostya Serebryanyc4910612011-12-15 21:55:34 +0000184 CHECK(AddrIsInMem(g->beg));
185 CHECK(AddrIsAlignedByGranularity(g->beg));
186 CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
Stephen Hines86277eb2015-03-23 12:06:32 -0700187 if (CanPoisonMemory())
Alexey Samsonov7e843492013-03-28 15:42:43 +0000188 PoisonShadowForGlobal(g, 0);
Kostya Serebryanyc4910612011-12-15 21:55:34 +0000189 // We unpoison the shadow memory for the global but we do not remove it from
190 // the list because that would require O(n^2) time with the current list
191 // implementation. It might not be worth doing anyway.
192}
193
Alexey Samsonov46efcb02013-05-24 11:46:56 +0000194void StopInitOrderChecking() {
195 BlockingMutexLock lock(&mu_for_globals);
196 if (!flags()->check_initialization_order || !dynamic_init_globals)
197 return;
198 flags()->check_initialization_order = false;
199 for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
200 DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
201 const Global *g = &dyn_g.g;
202 // Unpoison the whole global.
203 PoisonShadowForGlobal(g, 0);
204 // Poison redzones back.
205 PoisonRedZones(*g);
206 }
207}
208
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000209} // namespace __asan
210
211// ---------------------- Interface ---------------- {{{1
212using namespace __asan; // NOLINT
213
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000214// Register an array of globals.
Kostya Serebryany9aead372012-05-31 14:11:07 +0000215void __asan_register_globals(__asan_global *globals, uptr n) {
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +0000216 if (!flags()->report_globals) return;
Stephen Hines86277eb2015-03-23 12:06:32 -0700217 GET_STACK_TRACE_MALLOC;
Stephen Hines6d186232014-11-26 17:56:19 -0800218 u32 stack_id = StackDepotPut(stack);
Dmitry Vyukovf4f51f22013-01-14 07:51:39 +0000219 BlockingMutexLock lock(&mu_for_globals);
Stephen Hines6a211c52014-07-21 00:49:56 -0700220 if (!global_registration_site_vector)
221 global_registration_site_vector =
222 new(allocator_for_globals) GlobalRegistrationSiteVector(128);
223 GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]};
224 global_registration_site_vector->push_back(site);
225 if (flags()->report_globals >= 2) {
226 PRINT_CURRENT_STACK();
227 Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]);
228 }
Kostya Serebryany9aead372012-05-31 14:11:07 +0000229 for (uptr i = 0; i < n; i++) {
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000230 RegisterGlobal(&globals[i]);
231 }
232}
Kostya Serebryanyc4910612011-12-15 21:55:34 +0000233
234// Unregister an array of globals.
Kostya Serebryany3945c582012-08-21 14:10:25 +0000235// We must do this when a shared objects gets dlclosed.
Kostya Serebryany9aead372012-05-31 14:11:07 +0000236void __asan_unregister_globals(__asan_global *globals, uptr n) {
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +0000237 if (!flags()->report_globals) return;
Dmitry Vyukovf4f51f22013-01-14 07:51:39 +0000238 BlockingMutexLock lock(&mu_for_globals);
Kostya Serebryany9aead372012-05-31 14:11:07 +0000239 for (uptr i = 0; i < n; i++) {
Kostya Serebryanyc4910612011-12-15 21:55:34 +0000240 UnregisterGlobal(&globals[i]);
241 }
242}
Kostya Serebryany3945c582012-08-21 14:10:25 +0000243
244// This method runs immediately prior to dynamic initialization in each TU,
245// when all dynamically initialized globals are unpoisoned. This method
246// poisons all global variables not defined in this TU, so that a dynamic
247// initializer can only touch global variables in the same TU.
Alexey Samsonov05e16a02013-03-26 13:06:12 +0000248void __asan_before_dynamic_init(const char *module_name) {
Alexey Samsonov7e843492013-03-28 15:42:43 +0000249 if (!flags()->check_initialization_order ||
Stephen Hines86277eb2015-03-23 12:06:32 -0700250 !CanPoisonMemory())
Alexey Samsonov7e843492013-03-28 15:42:43 +0000251 return;
Alexey Samsonovdfeef672013-04-19 08:35:16 +0000252 bool strict_init_order = flags()->strict_init_order;
Alexey Samsonov7e843492013-03-28 15:42:43 +0000253 CHECK(dynamic_init_globals);
Alexey Samsonov05e16a02013-03-26 13:06:12 +0000254 CHECK(module_name);
Alexey Samsonov7e843492013-03-28 15:42:43 +0000255 CHECK(asan_inited);
Dmitry Vyukovf4f51f22013-01-14 07:51:39 +0000256 BlockingMutexLock lock(&mu_for_globals);
Alexey Samsonov7e843492013-03-28 15:42:43 +0000257 if (flags()->report_globals >= 3)
258 Printf("DynInitPoison module: %s\n", module_name);
259 for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
Alexey Samsonovdfeef672013-04-19 08:35:16 +0000260 DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
261 const Global *g = &dyn_g.g;
262 if (dyn_g.initialized)
263 continue;
Alexey Samsonov7e843492013-03-28 15:42:43 +0000264 if (g->module_name != module_name)
265 PoisonShadowForGlobal(g, kAsanInitializationOrderMagic);
Alexey Samsonovdfeef672013-04-19 08:35:16 +0000266 else if (!strict_init_order)
267 dyn_g.initialized = true;
Kostya Serebryany3945c582012-08-21 14:10:25 +0000268 }
Kostya Serebryany3945c582012-08-21 14:10:25 +0000269}
270
271// This method runs immediately after dynamic initialization in each TU, when
272// all dynamically initialized globals except for those defined in the current
273// TU are poisoned. It simply unpoisons all dynamically initialized globals.
274void __asan_after_dynamic_init() {
Alexey Samsonov7e843492013-03-28 15:42:43 +0000275 if (!flags()->check_initialization_order ||
Stephen Hines86277eb2015-03-23 12:06:32 -0700276 !CanPoisonMemory())
Alexey Samsonov7e843492013-03-28 15:42:43 +0000277 return;
278 CHECK(asan_inited);
Dmitry Vyukovf4f51f22013-01-14 07:51:39 +0000279 BlockingMutexLock lock(&mu_for_globals);
Alexey Samsonov7e843492013-03-28 15:42:43 +0000280 // FIXME: Optionally report that we're unpoisoning globals from a module.
281 for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
Alexey Samsonovdfeef672013-04-19 08:35:16 +0000282 DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
283 const Global *g = &dyn_g.g;
284 if (!dyn_g.initialized) {
285 // Unpoison the whole global.
286 PoisonShadowForGlobal(g, 0);
287 // Poison redzones back.
288 PoisonRedZones(*g);
289 }
Alexey Samsonov7e843492013-03-28 15:42:43 +0000290 }
Kostya Serebryany3945c582012-08-21 14:10:25 +0000291}