File: | projects/compiler-rt/lib/cfi/cfi.cc |
Warning: | line 263, column 9 Access to field 'st_name' results in a dereference of a null pointer (loaded from variable 'p') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===-------- cfi.cc ------------------------------------------------------===// | |||
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 implements the runtime support for the cross-DSO CFI. | |||
11 | // | |||
12 | //===----------------------------------------------------------------------===// | |||
13 | ||||
14 | #include <assert.h> | |||
15 | #include <elf.h> | |||
16 | ||||
17 | #include "sanitizer_common/sanitizer_common.h" | |||
18 | #if SANITIZER_FREEBSD0 | |||
19 | #include <sys/link_elf.h> | |||
20 | #endif | |||
21 | #include <link.h> | |||
22 | #include <string.h> | |||
23 | #include <stdlib.h> | |||
24 | #include <sys/mman.h> | |||
25 | ||||
26 | #if SANITIZER_LINUX1 | |||
27 | typedef ElfW(Phdr)Elf64_Phdr Elf_Phdr; | |||
28 | typedef ElfW(Ehdr)Elf64_Ehdr Elf_Ehdr; | |||
29 | typedef ElfW(Addr)Elf64_Addr Elf_Addr; | |||
30 | typedef ElfW(Sym)Elf64_Sym Elf_Sym; | |||
31 | typedef ElfW(Dyn)Elf64_Dyn Elf_Dyn; | |||
32 | #elif SANITIZER_FREEBSD0 | |||
33 | #if SANITIZER_WORDSIZE64 == 64 | |||
34 | #define ElfW64_Dyn Elf_Dyn | |||
35 | #define ElfW64_Sym Elf_Sym | |||
36 | #else | |||
37 | #define ElfW32_Dyn Elf_Dyn | |||
38 | #define ElfW32_Sym Elf_Sym | |||
39 | #endif | |||
40 | #endif | |||
41 | ||||
42 | #include "interception/interception.h" | |||
43 | #include "sanitizer_common/sanitizer_flag_parser.h" | |||
44 | #include "ubsan/ubsan_init.h" | |||
45 | #include "ubsan/ubsan_flags.h" | |||
46 | ||||
47 | #ifdef CFI_ENABLE_DIAG | |||
48 | #include "ubsan/ubsan_handlers.h" | |||
49 | #endif | |||
50 | ||||
51 | using namespace __sanitizer; | |||
52 | ||||
53 | namespace __cfi { | |||
54 | ||||
55 | #define kCfiShadowLimitsStorageSize4096 4096 // 1 page | |||
56 | // Lets hope that the data segment is mapped with 4K pages. | |||
57 | // The pointer to the cfi shadow region is stored at the start of this page. | |||
58 | // The rest of the page is unused and re-mapped read-only. | |||
59 | static union { | |||
60 | char space[kCfiShadowLimitsStorageSize4096]; | |||
61 | struct { | |||
62 | uptr start; | |||
63 | uptr size; | |||
64 | } limits; | |||
65 | } cfi_shadow_limits_storage | |||
66 | __attribute__((aligned(kCfiShadowLimitsStorageSize4096))); | |||
67 | static constexpr uptr kShadowGranularity = 12; | |||
68 | static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096 | |||
69 | ||||
70 | static constexpr uint16_t kInvalidShadow = 0; | |||
71 | static constexpr uint16_t kUncheckedShadow = 0xFFFFU; | |||
72 | ||||
73 | // Get the start address of the CFI shadow region. | |||
74 | uptr GetShadow() { | |||
75 | return cfi_shadow_limits_storage.limits.start; | |||
76 | } | |||
77 | ||||
78 | uptr GetShadowSize() { | |||
79 | return cfi_shadow_limits_storage.limits.size; | |||
80 | } | |||
81 | ||||
82 | // This will only work while the shadow is not allocated. | |||
83 | void SetShadowSize(uptr size) { | |||
84 | cfi_shadow_limits_storage.limits.size = size; | |||
85 | } | |||
86 | ||||
87 | uptr MemToShadowOffset(uptr x) { | |||
88 | return (x >> kShadowGranularity) << 1; | |||
89 | } | |||
90 | ||||
91 | uint16_t *MemToShadow(uptr x, uptr shadow_base) { | |||
92 | return (uint16_t *)(shadow_base + MemToShadowOffset(x)); | |||
93 | } | |||
94 | ||||
95 | typedef int (*CFICheckFn)(u64, void *, void *); | |||
96 | ||||
97 | // This class reads and decodes the shadow contents. | |||
98 | class ShadowValue { | |||
99 | uptr addr; | |||
100 | uint16_t v; | |||
101 | explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {} | |||
102 | ||||
103 | public: | |||
104 | bool is_invalid() const { return v == kInvalidShadow; } | |||
105 | ||||
106 | bool is_unchecked() const { return v == kUncheckedShadow; } | |||
107 | ||||
108 | CFICheckFn get_cfi_check() const { | |||
109 | assert(!is_invalid() && !is_unchecked())((!is_invalid() && !is_unchecked()) ? static_cast< void> (0) : __assert_fail ("!is_invalid() && !is_unchecked()" , "/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 109, __PRETTY_FUNCTION__)); | |||
110 | uptr aligned_addr = addr & ~(kShadowAlign - 1); | |||
111 | uptr p = aligned_addr - (((uptr)v - 1) << kShadowGranularity); | |||
112 | return reinterpret_cast<CFICheckFn>(p); | |||
113 | } | |||
114 | ||||
115 | // Load a shadow value for the given application memory address. | |||
116 | static const ShadowValue load(uptr addr) { | |||
117 | uptr shadow_base = GetShadow(); | |||
118 | uptr shadow_offset = MemToShadowOffset(addr); | |||
119 | if (shadow_offset > GetShadowSize()) | |||
120 | return ShadowValue(addr, kInvalidShadow); | |||
121 | else | |||
122 | return ShadowValue( | |||
123 | addr, *reinterpret_cast<uint16_t *>(shadow_base + shadow_offset)); | |||
124 | } | |||
125 | }; | |||
126 | ||||
127 | class ShadowBuilder { | |||
128 | uptr shadow_; | |||
129 | ||||
130 | public: | |||
131 | // Allocate a new empty shadow (for the entire address space) on the side. | |||
132 | void Start(); | |||
133 | // Mark the given address range as unchecked. | |||
134 | // This is used for uninstrumented libraries like libc. | |||
135 | // Any CFI check with a target in that range will pass. | |||
136 | void AddUnchecked(uptr begin, uptr end); | |||
137 | // Mark the given address range as belonging to a library with the given | |||
138 | // cfi_check function. | |||
139 | void Add(uptr begin, uptr end, uptr cfi_check); | |||
140 | // Finish shadow construction. Atomically switch the current active shadow | |||
141 | // region with the newly constructed one and deallocate the former. | |||
142 | void Install(); | |||
143 | }; | |||
144 | ||||
145 | void ShadowBuilder::Start() { | |||
146 | shadow_ = (uptr)MmapNoReserveOrDie(GetShadowSize(), "CFI shadow"); | |||
147 | VReport(1, "CFI: shadow at %zx .. %zx\n", shadow_, shadow_ + GetShadowSize())do { if ((uptr)Verbosity() >= (1)) Report("CFI: shadow at %zx .. %zx\n" , shadow_, shadow_ + GetShadowSize()); } while (0); | |||
148 | } | |||
149 | ||||
150 | void ShadowBuilder::AddUnchecked(uptr begin, uptr end) { | |||
151 | uint16_t *shadow_begin = MemToShadow(begin, shadow_); | |||
152 | uint16_t *shadow_end = MemToShadow(end - 1, shadow_) + 1; | |||
153 | // memset takes a byte, so our unchecked shadow value requires both bytes to | |||
154 | // be the same. Make sure we're ok during compilation. | |||
155 | static_assert((kUncheckedShadow & 0xff) == ((kUncheckedShadow >> 8) & 0xff), | |||
156 | "Both bytes of the 16-bit value must be the same!"); | |||
157 | memset(shadow_begin, kUncheckedShadow & 0xff, | |||
158 | (shadow_end - shadow_begin) * sizeof(*shadow_begin)); | |||
159 | } | |||
160 | ||||
161 | void ShadowBuilder::Add(uptr begin, uptr end, uptr cfi_check) { | |||
162 | assert((cfi_check & (kShadowAlign - 1)) == 0)(((cfi_check & (kShadowAlign - 1)) == 0) ? static_cast< void> (0) : __assert_fail ("(cfi_check & (kShadowAlign - 1)) == 0" , "/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 162, __PRETTY_FUNCTION__)); | |||
163 | ||||
164 | // Don't fill anything below cfi_check. We can not represent those addresses | |||
165 | // in the shadow, and must make sure at codegen to place all valid call | |||
166 | // targets above cfi_check. | |||
167 | begin = Max(begin, cfi_check); | |||
168 | uint16_t *s = MemToShadow(begin, shadow_); | |||
169 | uint16_t *s_end = MemToShadow(end - 1, shadow_) + 1; | |||
170 | uint16_t sv = ((begin - cfi_check) >> kShadowGranularity) + 1; | |||
171 | for (; s < s_end; s++, sv++) | |||
172 | *s = sv; | |||
173 | } | |||
174 | ||||
175 | #if SANITIZER_LINUX1 || SANITIZER_FREEBSD0 || SANITIZER_NETBSD0 | |||
176 | void ShadowBuilder::Install() { | |||
177 | MprotectReadOnly(shadow_, GetShadowSize()); | |||
178 | uptr main_shadow = GetShadow(); | |||
179 | if (main_shadow) { | |||
180 | // Update. | |||
181 | #if SANITIZER_LINUX1 | |||
182 | void *res = mremap((void *)shadow_, GetShadowSize(), GetShadowSize(), | |||
183 | MREMAP_MAYMOVE1 | MREMAP_FIXED2, (void *)main_shadow); | |||
184 | CHECK(res != MAP_FAILED)do { __sanitizer::u64 v1 = (__sanitizer::u64)((res != ((void * ) -1))); __sanitizer::u64 v2 = (__sanitizer::u64)(0); if (__builtin_expect (!!(!(v1 != v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 184, "(" "(res != ((void *) -1))" ") " "!=" " (" "0" ")", v1 , v2); } while (false); | |||
185 | #elif SANITIZER_NETBSD0 | |||
186 | void *res = mremap((void *)shadow_, GetShadowSize(), (void *)main_shadow, | |||
187 | GetShadowSize(), MAP_FIXED0x10); | |||
188 | CHECK(res != MAP_FAILED)do { __sanitizer::u64 v1 = (__sanitizer::u64)((res != ((void * ) -1))); __sanitizer::u64 v2 = (__sanitizer::u64)(0); if (__builtin_expect (!!(!(v1 != v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 188, "(" "(res != ((void *) -1))" ") " "!=" " (" "0" ")", v1 , v2); } while (false); | |||
189 | #else | |||
190 | void *res = MmapFixedOrDie(shadow_, GetShadowSize()); | |||
191 | CHECK(res != MAP_FAILED)do { __sanitizer::u64 v1 = (__sanitizer::u64)((res != ((void * ) -1))); __sanitizer::u64 v2 = (__sanitizer::u64)(0); if (__builtin_expect (!!(!(v1 != v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 191, "(" "(res != ((void *) -1))" ") " "!=" " (" "0" ")", v1 , v2); } while (false); | |||
192 | ::memcpy(&shadow_, &main_shadow, GetShadowSize()); | |||
193 | #endif | |||
194 | } else { | |||
195 | // Initial setup. | |||
196 | CHECK_EQ(kCfiShadowLimitsStorageSize, GetPageSizeCached())do { __sanitizer::u64 v1 = (__sanitizer::u64)((4096)); __sanitizer ::u64 v2 = (__sanitizer::u64)((GetPageSizeCached())); if (__builtin_expect (!!(!(v1 == v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 196, "(" "(4096)" ") " "==" " (" "(GetPageSizeCached())" ")" , v1, v2); } while (false); | |||
197 | CHECK_EQ(0, GetShadow())do { __sanitizer::u64 v1 = (__sanitizer::u64)((0)); __sanitizer ::u64 v2 = (__sanitizer::u64)((GetShadow())); if (__builtin_expect (!!(!(v1 == v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 197, "(" "(0)" ") " "==" " (" "(GetShadow())" ")", v1, v2); } while (false); | |||
198 | cfi_shadow_limits_storage.limits.start = shadow_; | |||
199 | MprotectReadOnly((uptr)&cfi_shadow_limits_storage, | |||
200 | sizeof(cfi_shadow_limits_storage)); | |||
201 | CHECK_EQ(shadow_, GetShadow())do { __sanitizer::u64 v1 = (__sanitizer::u64)((shadow_)); __sanitizer ::u64 v2 = (__sanitizer::u64)((GetShadow())); if (__builtin_expect (!!(!(v1 == v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 201, "(" "(shadow_)" ") " "==" " (" "(GetShadow())" ")", v1 , v2); } while (false); | |||
202 | } | |||
203 | } | |||
204 | #else | |||
205 | #error not implemented | |||
206 | #endif | |||
207 | ||||
208 | // This is a workaround for a glibc bug: | |||
209 | // https://sourceware.org/bugzilla/show_bug.cgi?id=15199 | |||
210 | // Other platforms can, hopefully, just do | |||
211 | // dlopen(RTLD_NOLOAD | RTLD_LAZY) | |||
212 | // dlsym("__cfi_check"). | |||
213 | uptr find_cfi_check_in_dso(dl_phdr_info *info) { | |||
214 | const Elf_Dyn *dynamic = nullptr; | |||
215 | for (int i = 0; i < info->dlpi_phnum; ++i) { | |||
216 | if (info->dlpi_phdr[i].p_type == PT_DYNAMIC2) { | |||
217 | dynamic = | |||
218 | (const Elf_Dyn *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); | |||
219 | break; | |||
220 | } | |||
221 | } | |||
222 | if (!dynamic) return 0; | |||
223 | uptr strtab = 0, symtab = 0, strsz = 0; | |||
224 | for (const Elf_Dyn *p = dynamic; p->d_tag != PT_NULL0; ++p) { | |||
225 | if (p->d_tag == DT_SYMTAB6) | |||
226 | symtab = p->d_un.d_ptr; | |||
227 | else if (p->d_tag == DT_STRTAB5) | |||
228 | strtab = p->d_un.d_ptr; | |||
229 | else if (p->d_tag == DT_STRSZ10) | |||
230 | strsz = p->d_un.d_ptr; | |||
231 | } | |||
232 | ||||
233 | if (symtab > strtab) { | |||
234 | VReport(1, "Can not handle: symtab > strtab (%p > %zx)\n", symtab, strtab)do { if ((uptr)Verbosity() >= (1)) Report("Can not handle: symtab > strtab (%p > %zx)\n" , symtab, strtab); } while (0); | |||
235 | return 0; | |||
236 | } | |||
237 | ||||
238 | // Verify that strtab and symtab are inside of the same LOAD segment. | |||
239 | // This excludes VDSO, which has (very high) bogus strtab and symtab pointers. | |||
240 | int phdr_idx; | |||
241 | for (phdr_idx = 0; phdr_idx < info->dlpi_phnum; phdr_idx++) { | |||
242 | const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx]; | |||
243 | if (phdr->p_type == PT_LOAD1) { | |||
244 | uptr beg = info->dlpi_addr + phdr->p_vaddr; | |||
245 | uptr end = beg + phdr->p_memsz; | |||
246 | if (strtab >= beg && strtab + strsz < end && symtab >= beg && | |||
247 | symtab < end) | |||
248 | break; | |||
249 | } | |||
250 | } | |||
251 | if (phdr_idx == info->dlpi_phnum) { | |||
252 | // Nope, either different segments or just bogus pointers. | |||
253 | // Can not handle this. | |||
254 | VReport(1, "Can not handle: symtab %p, strtab %zx\n", symtab, strtab)do { if ((uptr)Verbosity() >= (1)) Report("Can not handle: symtab %p, strtab %zx\n" , symtab, strtab); } while (0); | |||
255 | return 0; | |||
256 | } | |||
257 | ||||
258 | for (const Elf_Sym *p = (const Elf_Sym *)symtab; (Elf_Addr)p < strtab; | |||
259 | ++p) { | |||
260 | // There is no reliable way to find the end of the symbol table. In | |||
261 | // lld-produces files, there are other sections between symtab and strtab. | |||
262 | // Stop looking when the symbol name is not inside strtab. | |||
263 | if (p->st_name >= strsz) break; | |||
| ||||
264 | char *name = (char*)(strtab + p->st_name); | |||
265 | if (strcmp(name, "__cfi_check") == 0) { | |||
266 | assert(p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC) ||((p->st_info == (((1) << 4) + ((2) & 0xf)) || p-> st_info == (((2) << 4) + ((2) & 0xf))) ? static_cast <void> (0) : __assert_fail ("p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC) || p->st_info == ELF32_ST_INFO(STB_WEAK, STT_FUNC)" , "/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 267, __PRETTY_FUNCTION__)) | |||
267 | p->st_info == ELF32_ST_INFO(STB_WEAK, STT_FUNC))((p->st_info == (((1) << 4) + ((2) & 0xf)) || p-> st_info == (((2) << 4) + ((2) & 0xf))) ? static_cast <void> (0) : __assert_fail ("p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC) || p->st_info == ELF32_ST_INFO(STB_WEAK, STT_FUNC)" , "/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 267, __PRETTY_FUNCTION__)); | |||
268 | uptr addr = info->dlpi_addr + p->st_value; | |||
269 | return addr; | |||
270 | } | |||
271 | } | |||
272 | return 0; | |||
273 | } | |||
274 | ||||
275 | int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) { | |||
276 | uptr cfi_check = find_cfi_check_in_dso(info); | |||
| ||||
277 | if (cfi_check) | |||
278 | VReport(1, "Module '%s' __cfi_check %zx\n", info->dlpi_name, cfi_check)do { if ((uptr)Verbosity() >= (1)) Report("Module '%s' __cfi_check %zx\n" , info->dlpi_name, cfi_check); } while (0); | |||
279 | ||||
280 | ShadowBuilder *b = reinterpret_cast<ShadowBuilder *>(data); | |||
281 | ||||
282 | for (int i = 0; i < info->dlpi_phnum; i++) { | |||
283 | const Elf_Phdr *phdr = &info->dlpi_phdr[i]; | |||
284 | if (phdr->p_type == PT_LOAD1) { | |||
285 | // Jump tables are in the executable segment. | |||
286 | // VTables are in the non-executable one. | |||
287 | // Need to fill shadow for both. | |||
288 | // FIXME: reject writable if vtables are in the r/o segment. Depend on | |||
289 | // PT_RELRO? | |||
290 | uptr cur_beg = info->dlpi_addr + phdr->p_vaddr; | |||
291 | uptr cur_end = cur_beg + phdr->p_memsz; | |||
292 | if (cfi_check) { | |||
293 | VReport(1, " %zx .. %zx\n", cur_beg, cur_end)do { if ((uptr)Verbosity() >= (1)) Report(" %zx .. %zx\n" , cur_beg, cur_end); } while (0); | |||
294 | b->Add(cur_beg, cur_end, cfi_check); | |||
295 | } else { | |||
296 | b->AddUnchecked(cur_beg, cur_end); | |||
297 | } | |||
298 | } | |||
299 | } | |||
300 | return 0; | |||
301 | } | |||
302 | ||||
303 | // Init or update shadow for the current set of loaded libraries. | |||
304 | void UpdateShadow() { | |||
305 | ShadowBuilder b; | |||
306 | b.Start(); | |||
307 | dl_iterate_phdr(dl_iterate_phdr_cb, &b); | |||
308 | b.Install(); | |||
309 | } | |||
310 | ||||
311 | void InitShadow() { | |||
312 | CHECK_EQ(0, GetShadow())do { __sanitizer::u64 v1 = (__sanitizer::u64)((0)); __sanitizer ::u64 v2 = (__sanitizer::u64)((GetShadow())); if (__builtin_expect (!!(!(v1 == v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 312, "(" "(0)" ") " "==" " (" "(GetShadow())" ")", v1, v2); } while (false); | |||
313 | CHECK_EQ(0, GetShadowSize())do { __sanitizer::u64 v1 = (__sanitizer::u64)((0)); __sanitizer ::u64 v2 = (__sanitizer::u64)((GetShadowSize())); if (__builtin_expect (!!(!(v1 == v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 313, "(" "(0)" ") " "==" " (" "(GetShadowSize())" ")", v1, v2 ); } while (false); | |||
314 | ||||
315 | uptr vma = GetMaxUserVirtualAddress(); | |||
316 | // Shadow is 2 -> 2**kShadowGranularity. | |||
317 | SetShadowSize((vma >> (kShadowGranularity - 1)) + 1); | |||
318 | VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, GetShadowSize())do { if ((uptr)Verbosity() >= (1)) Report("CFI: VMA size %zx, shadow size %zx\n" , vma, GetShadowSize()); } while (0); | |||
319 | ||||
320 | UpdateShadow(); | |||
321 | } | |||
322 | ||||
323 | THREADLOCAL__thread int in_loader; | |||
324 | BlockingMutex shadow_update_lock(LINKER_INITIALIZED); | |||
325 | ||||
326 | void EnterLoader() { | |||
327 | if (in_loader == 0) { | |||
328 | shadow_update_lock.Lock(); | |||
329 | } | |||
330 | ++in_loader; | |||
331 | } | |||
332 | ||||
333 | void ExitLoader() { | |||
334 | CHECK(in_loader > 0)do { __sanitizer::u64 v1 = (__sanitizer::u64)((in_loader > 0)); __sanitizer::u64 v2 = (__sanitizer::u64)(0); if (__builtin_expect (!!(!(v1 != v2)), 0)) __sanitizer::CheckFailed("/build/llvm-toolchain-snapshot-8~svn350071/projects/compiler-rt/lib/cfi/cfi.cc" , 334, "(" "(in_loader > 0)" ") " "!=" " (" "0" ")", v1, v2 ); } while (false); | |||
335 | --in_loader; | |||
336 | UpdateShadow(); | |||
337 | if (in_loader == 0) { | |||
338 | shadow_update_lock.Unlock(); | |||
339 | } | |||
340 | } | |||
341 | ||||
342 | ALWAYS_INLINEinline __attribute__((always_inline)) void CfiSlowPathCommon(u64 CallSiteTypeId, void *Ptr, | |||
343 | void *DiagData) { | |||
344 | uptr Addr = (uptr)Ptr; | |||
345 | VReport(3, "__cfi_slowpath: %llx, %p\n", CallSiteTypeId, Ptr)do { if ((uptr)Verbosity() >= (3)) Report("__cfi_slowpath: %llx, %p\n" , CallSiteTypeId, Ptr); } while (0); | |||
346 | ShadowValue sv = ShadowValue::load(Addr); | |||
347 | if (sv.is_invalid()) { | |||
348 | VReport(1, "CFI: invalid memory region for a check target: %p\n", Ptr)do { if ((uptr)Verbosity() >= (1)) Report("CFI: invalid memory region for a check target: %p\n" , Ptr); } while (0); | |||
349 | #ifdef CFI_ENABLE_DIAG | |||
350 | if (DiagData) { | |||
351 | __ubsan_handle_cfi_check_fail( | |||
352 | reinterpret_cast<__ubsan::CFICheckFailData *>(DiagData), Addr, false); | |||
353 | return; | |||
354 | } | |||
355 | #endif | |||
356 | Trap(); | |||
357 | } | |||
358 | if (sv.is_unchecked()) { | |||
359 | VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr)do { if ((uptr)Verbosity() >= (2)) Report("CFI: unchecked call (shadow=FFFF): %p\n" , Ptr); } while (0); | |||
360 | return; | |||
361 | } | |||
362 | CFICheckFn cfi_check = sv.get_cfi_check(); | |||
363 | VReport(2, "__cfi_check at %p\n", cfi_check)do { if ((uptr)Verbosity() >= (2)) Report("__cfi_check at %p\n" , cfi_check); } while (0); | |||
364 | cfi_check(CallSiteTypeId, Ptr, DiagData); | |||
365 | } | |||
366 | ||||
367 | void InitializeFlags() { | |||
368 | SetCommonFlagsDefaults(); | |||
369 | #ifdef CFI_ENABLE_DIAG | |||
370 | __ubsan::Flags *uf = __ubsan::flags(); | |||
371 | uf->SetDefaults(); | |||
372 | #endif | |||
373 | ||||
374 | FlagParser cfi_parser; | |||
375 | RegisterCommonFlags(&cfi_parser); | |||
376 | cfi_parser.ParseString(GetEnv("CFI_OPTIONS")); | |||
377 | ||||
378 | #ifdef CFI_ENABLE_DIAG | |||
379 | FlagParser ubsan_parser; | |||
380 | __ubsan::RegisterUbsanFlags(&ubsan_parser, uf); | |||
381 | RegisterCommonFlags(&ubsan_parser); | |||
382 | ||||
383 | const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); | |||
384 | ubsan_parser.ParseString(ubsan_default_options); | |||
385 | ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); | |||
386 | #endif | |||
387 | ||||
388 | InitializeCommonFlags(); | |||
389 | ||||
390 | if (Verbosity()) | |||
391 | ReportUnrecognizedFlags(); | |||
392 | ||||
393 | if (common_flags()->help) { | |||
394 | cfi_parser.PrintFlagDescriptions(); | |||
395 | } | |||
396 | } | |||
397 | ||||
398 | } // namespace __cfi | |||
399 | ||||
400 | using namespace __cfi; | |||
401 | ||||
402 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE__attribute__((visibility("default"))) void | |||
403 | __cfi_slowpath(u64 CallSiteTypeId, void *Ptr) { | |||
404 | CfiSlowPathCommon(CallSiteTypeId, Ptr, nullptr); | |||
405 | } | |||
406 | ||||
407 | #ifdef CFI_ENABLE_DIAG | |||
408 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE__attribute__((visibility("default"))) void | |||
409 | __cfi_slowpath_diag(u64 CallSiteTypeId, void *Ptr, void *DiagData) { | |||
410 | CfiSlowPathCommon(CallSiteTypeId, Ptr, DiagData); | |||
411 | } | |||
412 | #endif | |||
413 | ||||
414 | static void EnsureInterceptorsInitialized(); | |||
415 | ||||
416 | // Setup shadow for dlopen()ed libraries. | |||
417 | // The actual shadow setup happens after dlopen() returns, which means that | |||
418 | // a library can not be a target of any CFI checks while its constructors are | |||
419 | // running. It's unclear how to fix this without some extra help from libc. | |||
420 | // In glibc, mmap inside dlopen is not interceptable. | |||
421 | // Maybe a seccomp-bpf filter? | |||
422 | // We could insert a high-priority constructor into the library, but that would | |||
423 | // not help with the uninstrumented libraries. | |||
424 | INTERCEPTOR(void*, dlopen, const char *filename, int flag)typedef void* (*dlopen_type)(const char *filename, int flag); namespace __interception { dlopen_type real_dlopen; } extern "C" void* dlopen(const char *filename, int flag) __attribute__ ((weak, alias("__interceptor_" "dlopen"), visibility("default" ))); extern "C" __attribute__((visibility("default"))) void* __interceptor_dlopen (const char *filename, int flag) { | |||
425 | EnsureInterceptorsInitialized(); | |||
426 | EnterLoader(); | |||
427 | void *handle = REAL(dlopen)__interception::real_dlopen(filename, flag); | |||
428 | ExitLoader(); | |||
429 | return handle; | |||
430 | } | |||
431 | ||||
432 | INTERCEPTOR(int, dlclose, void *handle)typedef int (*dlclose_type)(void *handle); namespace __interception { dlclose_type real_dlclose; } extern "C" int dlclose(void * handle) __attribute__((weak, alias("__interceptor_" "dlclose" ), visibility("default"))); extern "C" __attribute__((visibility ("default"))) int __interceptor_dlclose(void *handle) { | |||
433 | EnsureInterceptorsInitialized(); | |||
434 | EnterLoader(); | |||
435 | int res = REAL(dlclose)__interception::real_dlclose(handle); | |||
436 | ExitLoader(); | |||
437 | return res; | |||
438 | } | |||
439 | ||||
440 | static BlockingMutex interceptor_init_lock(LINKER_INITIALIZED); | |||
441 | static bool interceptors_inited = false; | |||
442 | ||||
443 | static void EnsureInterceptorsInitialized() { | |||
444 | BlockingMutexLock lock(&interceptor_init_lock); | |||
445 | if (interceptors_inited) | |||
446 | return; | |||
447 | ||||
448 | INTERCEPT_FUNCTION(dlopen)::__interception::GetRealFunctionAddress( "dlopen", (::__interception ::uptr *)&__interception::real_dlopen, (::__interception:: uptr) & (dlopen), (::__interception::uptr) & __interceptor_dlopen ); | |||
449 | INTERCEPT_FUNCTION(dlclose)::__interception::GetRealFunctionAddress( "dlclose", (::__interception ::uptr *)&__interception::real_dlclose, (::__interception ::uptr) & (dlclose), (::__interception::uptr) & __interceptor_dlclose ); | |||
450 | ||||
451 | interceptors_inited = true; | |||
452 | } | |||
453 | ||||
454 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE__attribute__((visibility("default"))) | |||
455 | #if !SANITIZER_CAN_USE_PREINIT_ARRAY1 | |||
456 | // On ELF platforms, the constructor is invoked using .preinit_array (see below) | |||
457 | __attribute__((constructor(0))) | |||
458 | #endif | |||
459 | void __cfi_init() { | |||
460 | SanitizerToolName = "CFI"; | |||
461 | InitializeFlags(); | |||
462 | InitShadow(); | |||
463 | ||||
464 | #ifdef CFI_ENABLE_DIAG | |||
465 | __ubsan::InitAsPlugin(); | |||
466 | #endif | |||
467 | } | |||
468 | ||||
469 | #if SANITIZER_CAN_USE_PREINIT_ARRAY1 | |||
470 | // On ELF platforms, run cfi initialization before any other constructors. | |||
471 | // On other platforms we use the constructor attribute to arrange to run our | |||
472 | // initialization early. | |||
473 | extern "C" { | |||
474 | __attribute__((section(".preinit_array"), | |||
475 | used)) void (*__cfi_preinit)(void) = __cfi_init; | |||
476 | } | |||
477 | #endif |