Bug Summary

File:projects/compiler-rt/lib/cfi/cfi.cc
Warning:line 235, column 9
Access to field 'st_name' results in a dereference of a null pointer (loaded from variable 'p')

Annotated Source Code

Press '?' to see keyboard shortcuts

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