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_lock.h"
15 #include "asan_mapping.h"
16 #include "asan_report.h"
17 #include "asan_stack.h"
18 #include "asan_stats.h"
19 #include "asan_thread.h"
20 #include "sanitizer/asan_interface.h"
24 typedef __asan_global Global
;
26 struct ListOfGlobals
{
31 static AsanLock
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 static uptr
GetAlignedSize(uptr size
) {
57 return ((size
+ kGlobalAndStackRedzone
- 1) / kGlobalAndStackRedzone
)
58 * kGlobalAndStackRedzone
;
61 bool DescribeAddressIfGlobal(uptr addr
) {
62 if (!flags()->report_globals
) return false;
63 ScopedLock
lock(&mu_for_globals
);
65 for (ListOfGlobals
*l
= list_of_all_globals
; l
; l
= l
->next
) {
66 const Global
&g
= *l
->g
;
67 if (flags()->report_globals
>= 2)
68 Report("Search Global: beg=%p size=%zu name=%s\n",
69 (void*)g
.beg
, g
.size
, (char*)g
.name
);
70 res
|= DescribeAddressRelativeToGlobal(addr
, g
);
75 // Register a global variable.
76 // This function may be called more than once for every global
77 // so we store the globals in a map.
78 static void RegisterGlobal(const Global
*g
) {
80 if (flags()->report_globals
>= 2)
81 Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n",
82 (void*)g
->beg
, g
->size
, g
->size_with_redzone
, g
->name
,
84 CHECK(flags()->report_globals
);
85 CHECK(AddrIsInMem(g
->beg
));
86 CHECK(AddrIsAlignedByGranularity(g
->beg
));
87 CHECK(AddrIsAlignedByGranularity(g
->size_with_redzone
));
90 (ListOfGlobals
*)allocator_for_globals
.Allocate(sizeof(ListOfGlobals
));
92 l
->next
= list_of_all_globals
;
93 list_of_all_globals
= l
;
94 if (g
->has_dynamic_init
) {
95 l
= (ListOfGlobals
*)allocator_for_globals
.Allocate(sizeof(ListOfGlobals
));
97 l
->next
= list_of_dynamic_init_globals
;
98 list_of_dynamic_init_globals
= l
;
102 static void UnregisterGlobal(const Global
*g
) {
104 CHECK(flags()->report_globals
);
105 CHECK(AddrIsInMem(g
->beg
));
106 CHECK(AddrIsAlignedByGranularity(g
->beg
));
107 CHECK(AddrIsAlignedByGranularity(g
->size_with_redzone
));
108 PoisonShadow(g
->beg
, g
->size_with_redzone
, 0);
109 // We unpoison the shadow memory for the global but we do not remove it from
110 // the list because that would require O(n^2) time with the current list
111 // implementation. It might not be worth doing anyway.
114 // Poison all shadow memory for a single global.
115 static void PoisonGlobalAndRedzones(const Global
*g
) {
117 CHECK(flags()->check_initialization_order
);
118 CHECK(AddrIsInMem(g
->beg
));
119 CHECK(AddrIsAlignedByGranularity(g
->beg
));
120 CHECK(AddrIsAlignedByGranularity(g
->size_with_redzone
));
121 if (flags()->report_globals
>= 3)
122 Printf("DynInitPoison : %s\n", g
->name
);
123 PoisonShadow(g
->beg
, g
->size_with_redzone
, kAsanInitializationOrderMagic
);
126 static void UnpoisonGlobal(const Global
*g
) {
128 CHECK(flags()->check_initialization_order
);
129 CHECK(AddrIsInMem(g
->beg
));
130 CHECK(AddrIsAlignedByGranularity(g
->beg
));
131 CHECK(AddrIsAlignedByGranularity(g
->size_with_redzone
));
132 if (flags()->report_globals
>= 3)
133 Printf("DynInitUnpoison: %s\n", g
->name
);
134 PoisonShadow(g
->beg
, g
->size_with_redzone
, 0);
138 } // namespace __asan
140 // ---------------------- Interface ---------------- {{{1
141 using namespace __asan
; // NOLINT
143 // Register one global with a default redzone.
144 void __asan_register_global(uptr addr
, uptr size
,
146 if (!flags()->report_globals
) return;
147 ScopedLock
lock(&mu_for_globals
);
148 Global
*g
= (Global
*)allocator_for_globals
.Allocate(sizeof(Global
));
151 g
->size_with_redzone
= GetAlignedSize(size
) + kGlobalAndStackRedzone
;
156 // Register an array of globals.
157 void __asan_register_globals(__asan_global
*globals
, uptr n
) {
158 if (!flags()->report_globals
) return;
159 ScopedLock
lock(&mu_for_globals
);
160 for (uptr i
= 0; i
< n
; i
++) {
161 RegisterGlobal(&globals
[i
]);
165 // Unregister an array of globals.
166 // We must do this when a shared objects gets dlclosed.
167 void __asan_unregister_globals(__asan_global
*globals
, uptr n
) {
168 if (!flags()->report_globals
) return;
169 ScopedLock
lock(&mu_for_globals
);
170 for (uptr i
= 0; i
< n
; i
++) {
171 UnregisterGlobal(&globals
[i
]);
175 // This method runs immediately prior to dynamic initialization in each TU,
176 // when all dynamically initialized globals are unpoisoned. This method
177 // poisons all global variables not defined in this TU, so that a dynamic
178 // initializer can only touch global variables in the same TU.
179 void __asan_before_dynamic_init(uptr first_addr
, uptr last_addr
) {
180 if (!flags()->check_initialization_order
) return;
181 CHECK(list_of_dynamic_init_globals
);
182 ScopedLock
lock(&mu_for_globals
);
183 bool from_current_tu
= false;
184 // The list looks like:
185 // a => ... => b => last_addr => ... => first_addr => c => ...
186 // The globals of the current TU reside between last_addr and first_addr.
187 for (ListOfGlobals
*l
= list_of_dynamic_init_globals
; l
; l
= l
->next
) {
188 if (l
->g
->beg
== last_addr
)
189 from_current_tu
= true;
190 if (!from_current_tu
)
191 PoisonGlobalAndRedzones(l
->g
);
192 if (l
->g
->beg
== first_addr
)
193 from_current_tu
= false;
195 CHECK(!from_current_tu
);
198 // This method runs immediately after dynamic initialization in each TU, when
199 // all dynamically initialized globals except for those defined in the current
200 // TU are poisoned. It simply unpoisons all dynamically initialized globals.
201 void __asan_after_dynamic_init() {
202 if (!flags()->check_initialization_order
) return;
203 ScopedLock
lock(&mu_for_globals
);
204 for (ListOfGlobals
*l
= list_of_dynamic_init_globals
; l
; l
= l
->next
)
205 UnpoisonGlobal(l
->g
);