1 //===-- asan_globals.cc ---------------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of AddressSanitizer, an address sanity checker.
11 //===----------------------------------------------------------------------===//
12 #include "asan_interceptors.h"
13 #include "asan_internal.h"
14 #include "asan_mapping.h"
15 #include "asan_report.h"
16 #include "asan_stack.h"
17 #include "asan_stats.h"
18 #include "asan_thread.h"
19 #include "sanitizer/asan_interface.h"
20 #include "sanitizer_common/sanitizer_mutex.h"
24 typedef __asan_global Global
;
26 struct ListOfGlobals
{
31 static BlockingMutex
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
;
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);
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
);
56 bool DescribeAddressIfGlobal(uptr addr
) {
57 if (!flags()->report_globals
) return false;
58 BlockingMutexLock
lock(&mu_for_globals
);
60 for (ListOfGlobals
*l
= list_of_all_globals
; l
; l
= l
->next
) {
61 const Global
&g
= *l
->g
;
62 if (flags()->report_globals
>= 2)
63 Report("Search Global: beg=%p size=%zu name=%s\n",
64 (void*)g
.beg
, g
.size
, (char*)g
.name
);
65 res
|= DescribeAddressRelativeToGlobal(addr
, g
);
70 // Register a global variable.
71 // This function may be called more than once for every global
72 // so we store the globals in a map.
73 static void RegisterGlobal(const Global
*g
) {
75 if (flags()->report_globals
>= 2)
76 Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n",
77 (void*)g
->beg
, g
->size
, g
->size_with_redzone
, g
->name
,
79 CHECK(flags()->report_globals
);
80 CHECK(AddrIsInMem(g
->beg
));
81 CHECK(AddrIsAlignedByGranularity(g
->beg
));
82 CHECK(AddrIsAlignedByGranularity(g
->size_with_redzone
));
85 (ListOfGlobals
*)allocator_for_globals
.Allocate(sizeof(ListOfGlobals
));
87 l
->next
= list_of_all_globals
;
88 list_of_all_globals
= l
;
89 if (g
->has_dynamic_init
) {
90 l
= (ListOfGlobals
*)allocator_for_globals
.Allocate(sizeof(ListOfGlobals
));
92 l
->next
= list_of_dynamic_init_globals
;
93 list_of_dynamic_init_globals
= l
;
97 static void UnregisterGlobal(const Global
*g
) {
99 CHECK(flags()->report_globals
);
100 CHECK(AddrIsInMem(g
->beg
));
101 CHECK(AddrIsAlignedByGranularity(g
->beg
));
102 CHECK(AddrIsAlignedByGranularity(g
->size_with_redzone
));
103 PoisonShadow(g
->beg
, g
->size_with_redzone
, 0);
104 // We unpoison the shadow memory for the global but we do not remove it from
105 // the list because that would require O(n^2) time with the current list
106 // implementation. It might not be worth doing anyway.
109 // Poison all shadow memory for a single global.
110 static void PoisonGlobalAndRedzones(const Global
*g
) {
112 CHECK(flags()->check_initialization_order
);
113 CHECK(AddrIsInMem(g
->beg
));
114 CHECK(AddrIsAlignedByGranularity(g
->beg
));
115 CHECK(AddrIsAlignedByGranularity(g
->size_with_redzone
));
116 if (flags()->report_globals
>= 3)
117 Printf("DynInitPoison : %s\n", g
->name
);
118 PoisonShadow(g
->beg
, g
->size_with_redzone
, kAsanInitializationOrderMagic
);
121 static void UnpoisonGlobal(const Global
*g
) {
123 CHECK(flags()->check_initialization_order
);
124 CHECK(AddrIsInMem(g
->beg
));
125 CHECK(AddrIsAlignedByGranularity(g
->beg
));
126 CHECK(AddrIsAlignedByGranularity(g
->size_with_redzone
));
127 if (flags()->report_globals
>= 3)
128 Printf("DynInitUnpoison: %s\n", g
->name
);
129 PoisonShadow(g
->beg
, g
->size_with_redzone
, 0);
133 } // namespace __asan
135 // ---------------------- Interface ---------------- {{{1
136 using namespace __asan
; // NOLINT
138 // Register an array of globals.
139 void __asan_register_globals(__asan_global
*globals
, uptr n
) {
140 if (!flags()->report_globals
) return;
141 BlockingMutexLock
lock(&mu_for_globals
);
142 for (uptr i
= 0; i
< n
; i
++) {
143 RegisterGlobal(&globals
[i
]);
147 // Unregister an array of globals.
148 // We must do this when a shared objects gets dlclosed.
149 void __asan_unregister_globals(__asan_global
*globals
, uptr n
) {
150 if (!flags()->report_globals
) return;
151 BlockingMutexLock
lock(&mu_for_globals
);
152 for (uptr i
= 0; i
< n
; i
++) {
153 UnregisterGlobal(&globals
[i
]);
157 // This method runs immediately prior to dynamic initialization in each TU,
158 // when all dynamically initialized globals are unpoisoned. This method
159 // poisons all global variables not defined in this TU, so that a dynamic
160 // initializer can only touch global variables in the same TU.
161 void __asan_before_dynamic_init(uptr first_addr
, uptr last_addr
) {
162 if (!flags()->check_initialization_order
) return;
163 CHECK(list_of_dynamic_init_globals
);
164 BlockingMutexLock
lock(&mu_for_globals
);
165 bool from_current_tu
= false;
166 // The list looks like:
167 // a => ... => b => last_addr => ... => first_addr => c => ...
168 // The globals of the current TU reside between last_addr and first_addr.
169 for (ListOfGlobals
*l
= list_of_dynamic_init_globals
; l
; l
= l
->next
) {
170 if (l
->g
->beg
== last_addr
)
171 from_current_tu
= true;
172 if (!from_current_tu
)
173 PoisonGlobalAndRedzones(l
->g
);
174 if (l
->g
->beg
== first_addr
)
175 from_current_tu
= false;
177 CHECK(!from_current_tu
);
180 // This method runs immediately after dynamic initialization in each TU, when
181 // all dynamically initialized globals except for those defined in the current
182 // TU are poisoned. It simply unpoisons all dynamically initialized globals.
183 void __asan_after_dynamic_init() {
184 if (!flags()->check_initialization_order
) return;
185 BlockingMutexLock
lock(&mu_for_globals
);
186 for (ListOfGlobals
*l
= list_of_dynamic_init_globals
; l
; l
= l
->next
)
187 UnpoisonGlobal(l
->g
);