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