]>
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" | |
f35db108 | 14 | #include "asan_mapping.h" |
ef1b3fda | 15 | #include "asan_poisoning.h" |
f35db108 WM |
16 | #include "asan_report.h" |
17 | #include "asan_stack.h" | |
18 | #include "asan_stats.h" | |
19 | #include "asan_thread.h" | |
ef1b3fda | 20 | #include "sanitizer_common/sanitizer_common.h" |
2660d12d | 21 | #include "sanitizer_common/sanitizer_mutex.h" |
ef1b3fda | 22 | #include "sanitizer_common/sanitizer_placement_new.h" |
f35db108 WM |
23 | |
24 | namespace __asan { | |
25 | ||
26 | typedef __asan_global Global; | |
27 | ||
28 | struct ListOfGlobals { | |
29 | const Global *g; | |
30 | ListOfGlobals *next; | |
31 | }; | |
32 | ||
2660d12d | 33 | static BlockingMutex mu_for_globals(LINKER_INITIALIZED); |
f35db108 WM |
34 | static LowLevelAllocator allocator_for_globals; |
35 | static ListOfGlobals *list_of_all_globals; | |
f35db108 | 36 | |
ef1b3fda KS |
37 | static const int kDynamicInitGlobalsInitialCapacity = 512; |
38 | struct DynInitGlobal { | |
39 | Global g; | |
40 | bool initialized; | |
41 | }; | |
42 | typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals; | |
43 | // Lazy-initialized and never deleted. | |
44 | static VectorOfGlobals *dynamic_init_globals; | |
45 | ||
46 | ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) { | |
47 | FastPoisonShadow(g->beg, g->size_with_redzone, value); | |
48 | } | |
49 | ||
50 | ALWAYS_INLINE void PoisonRedZones(const Global &g) { | |
b4ab7d34 | 51 | uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY); |
ef1b3fda KS |
52 | FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size, |
53 | kAsanGlobalRedzoneMagic); | |
b4ab7d34 | 54 | if (g.size != aligned_size) { |
ef1b3fda | 55 | FastPoisonShadowPartialRightRedzone( |
b4ab7d34 KS |
56 | g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY), |
57 | g.size % SHADOW_GRANULARITY, | |
58 | SHADOW_GRANULARITY, | |
59 | kAsanGlobalRedzoneMagic); | |
f35db108 WM |
60 | } |
61 | } | |
62 | ||
ef1b3fda KS |
63 | static void ReportGlobal(const Global &g, const char *prefix) { |
64 | Report("%s Global: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n", | |
65 | prefix, (void*)g.beg, g.size, g.size_with_redzone, g.name, | |
66 | g.module_name, g.has_dynamic_init); | |
67 | } | |
68 | ||
b4ab7d34 | 69 | bool DescribeAddressIfGlobal(uptr addr, uptr size) { |
f35db108 | 70 | if (!flags()->report_globals) return false; |
2660d12d | 71 | BlockingMutexLock lock(&mu_for_globals); |
f35db108 WM |
72 | bool res = false; |
73 | for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { | |
74 | const Global &g = *l->g; | |
75 | if (flags()->report_globals >= 2) | |
ef1b3fda | 76 | ReportGlobal(g, "Search"); |
b4ab7d34 | 77 | res |= DescribeAddressRelativeToGlobal(addr, size, g); |
f35db108 WM |
78 | } |
79 | return res; | |
80 | } | |
81 | ||
82 | // Register a global variable. | |
83 | // This function may be called more than once for every global | |
84 | // so we store the globals in a map. | |
85 | static void RegisterGlobal(const Global *g) { | |
86 | CHECK(asan_inited); | |
87 | if (flags()->report_globals >= 2) | |
ef1b3fda | 88 | ReportGlobal(*g, "Added"); |
f35db108 WM |
89 | CHECK(flags()->report_globals); |
90 | CHECK(AddrIsInMem(g->beg)); | |
91 | CHECK(AddrIsAlignedByGranularity(g->beg)); | |
92 | CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); | |
ef1b3fda KS |
93 | if (flags()->poison_heap) |
94 | PoisonRedZones(*g); | |
df77f0e4 | 95 | ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; |
f35db108 WM |
96 | l->g = g; |
97 | l->next = list_of_all_globals; | |
98 | list_of_all_globals = l; | |
99 | if (g->has_dynamic_init) { | |
ef1b3fda | 100 | if (dynamic_init_globals == 0) { |
df77f0e4 | 101 | dynamic_init_globals = new(allocator_for_globals) |
ef1b3fda KS |
102 | VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); |
103 | } | |
104 | DynInitGlobal dyn_global = { *g, false }; | |
105 | dynamic_init_globals->push_back(dyn_global); | |
f35db108 WM |
106 | } |
107 | } | |
108 | ||
109 | static void UnregisterGlobal(const Global *g) { | |
110 | CHECK(asan_inited); | |
111 | CHECK(flags()->report_globals); | |
112 | CHECK(AddrIsInMem(g->beg)); | |
113 | CHECK(AddrIsAlignedByGranularity(g->beg)); | |
114 | CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); | |
ef1b3fda KS |
115 | if (flags()->poison_heap) |
116 | PoisonShadowForGlobal(g, 0); | |
f35db108 WM |
117 | // We unpoison the shadow memory for the global but we do not remove it from |
118 | // the list because that would require O(n^2) time with the current list | |
119 | // implementation. It might not be worth doing anyway. | |
120 | } | |
121 | ||
ef1b3fda KS |
122 | void StopInitOrderChecking() { |
123 | BlockingMutexLock lock(&mu_for_globals); | |
124 | if (!flags()->check_initialization_order || !dynamic_init_globals) | |
125 | return; | |
126 | flags()->check_initialization_order = false; | |
127 | for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { | |
128 | DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; | |
129 | const Global *g = &dyn_g.g; | |
130 | // Unpoison the whole global. | |
131 | PoisonShadowForGlobal(g, 0); | |
132 | // Poison redzones back. | |
133 | PoisonRedZones(*g); | |
134 | } | |
f35db108 WM |
135 | } |
136 | ||
137 | } // namespace __asan | |
138 | ||
139 | // ---------------------- Interface ---------------- {{{1 | |
140 | using namespace __asan; // NOLINT | |
141 | ||
f35db108 WM |
142 | // Register an array of globals. |
143 | void __asan_register_globals(__asan_global *globals, uptr n) { | |
144 | if (!flags()->report_globals) return; | |
2660d12d | 145 | BlockingMutexLock lock(&mu_for_globals); |
f35db108 WM |
146 | for (uptr i = 0; i < n; i++) { |
147 | RegisterGlobal(&globals[i]); | |
148 | } | |
149 | } | |
150 | ||
151 | // Unregister an array of globals. | |
152 | // We must do this when a shared objects gets dlclosed. | |
153 | void __asan_unregister_globals(__asan_global *globals, uptr n) { | |
154 | if (!flags()->report_globals) return; | |
2660d12d | 155 | BlockingMutexLock lock(&mu_for_globals); |
f35db108 WM |
156 | for (uptr i = 0; i < n; i++) { |
157 | UnregisterGlobal(&globals[i]); | |
158 | } | |
159 | } | |
160 | ||
161 | // This method runs immediately prior to dynamic initialization in each TU, | |
162 | // when all dynamically initialized globals are unpoisoned. This method | |
163 | // poisons all global variables not defined in this TU, so that a dynamic | |
164 | // initializer can only touch global variables in the same TU. | |
ef1b3fda KS |
165 | void __asan_before_dynamic_init(const char *module_name) { |
166 | if (!flags()->check_initialization_order || | |
167 | !flags()->poison_heap) | |
168 | return; | |
169 | bool strict_init_order = flags()->strict_init_order; | |
170 | CHECK(dynamic_init_globals); | |
171 | CHECK(module_name); | |
172 | CHECK(asan_inited); | |
2660d12d | 173 | BlockingMutexLock lock(&mu_for_globals); |
ef1b3fda KS |
174 | if (flags()->report_globals >= 3) |
175 | Printf("DynInitPoison module: %s\n", module_name); | |
176 | for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { | |
177 | DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; | |
178 | const Global *g = &dyn_g.g; | |
179 | if (dyn_g.initialized) | |
180 | continue; | |
181 | if (g->module_name != module_name) | |
182 | PoisonShadowForGlobal(g, kAsanInitializationOrderMagic); | |
183 | else if (!strict_init_order) | |
184 | dyn_g.initialized = true; | |
f35db108 | 185 | } |
f35db108 WM |
186 | } |
187 | ||
188 | // This method runs immediately after dynamic initialization in each TU, when | |
189 | // all dynamically initialized globals except for those defined in the current | |
190 | // TU are poisoned. It simply unpoisons all dynamically initialized globals. | |
191 | void __asan_after_dynamic_init() { | |
ef1b3fda KS |
192 | if (!flags()->check_initialization_order || |
193 | !flags()->poison_heap) | |
194 | return; | |
195 | CHECK(asan_inited); | |
2660d12d | 196 | BlockingMutexLock lock(&mu_for_globals); |
ef1b3fda KS |
197 | // FIXME: Optionally report that we're unpoisoning globals from a module. |
198 | for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) { | |
199 | DynInitGlobal &dyn_g = (*dynamic_init_globals)[i]; | |
200 | const Global *g = &dyn_g.g; | |
201 | if (!dyn_g.initialized) { | |
202 | // Unpoison the whole global. | |
203 | PoisonShadowForGlobal(g, 0); | |
204 | // Poison redzones back. | |
205 | PoisonRedZones(*g); | |
206 | } | |
207 | } | |
f35db108 | 208 | } |