Bug Summary

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')

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-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-8/lib/clang/8.0.0 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I /build/llvm-toolchain-snapshot-8~svn345461/build-llvm/projects/compiler-rt/lib/cfi -I /build/llvm-toolchain-snapshot-8~svn345461/projects/compiler-rt/lib/cfi -I /build/llvm-toolchain-snapshot-8~svn345461/build-llvm/include -I /build/llvm-toolchain-snapshot-8~svn345461/include -I /build/llvm-toolchain-snapshot-8~svn345461/projects/compiler-rt/lib/cfi/.. -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0/32 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/i386-pc-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/backward -internal-isystem /usr/include/clang/8.0.0/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.0.0/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-comment -Wno-unused-parameter -Wno-variadic-macros -Wno-non-virtual-dtor -std=c++11 -fdeprecated-macro -fdebug-compilation-dir /build/llvm-toolchain-snapshot-8~svn345461/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-10-27-211344-32123-1 -x c++ /build/llvm-toolchain-snapshot-8~svn345461/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
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
27typedef ElfW(Phdr)Elf32_Phdr Elf_Phdr;
28typedef ElfW(Ehdr)Elf32_Ehdr Elf_Ehdr;
29typedef ElfW(Addr)Elf32_Addr Elf_Addr;
30typedef ElfW(Sym)Elf32_Sym Elf_Sym;
31typedef ElfW(Dyn)Elf32_Dyn Elf_Dyn;
32#elif SANITIZER_FREEBSD0
33#if SANITIZER_WORDSIZE32 == 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
51using namespace __sanitizer;
52
53namespace __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.
59static union {
60 char space[kCfiShadowLimitsStorageSize4096];
61 struct {
62 uptr start;
63 uptr size;
64 } limits;
65} cfi_shadow_limits_storage
66 __attribute__((aligned(kCfiShadowLimitsStorageSize4096)));
67static constexpr uptr kShadowGranularity = 12;
68static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096
69
70static constexpr uint16_t kInvalidShadow = 0;
71static constexpr uint16_t kUncheckedShadow = 0xFFFFU;
72
73// Get the start address of the CFI shadow region.
74uptr GetShadow() {
75 return cfi_shadow_limits_storage.limits.start;
76}
77
78uptr GetShadowSize() {
79 return cfi_shadow_limits_storage.limits.size;
80}
81
82// This will only work while the shadow is not allocated.
83void SetShadowSize(uptr size) {
84 cfi_shadow_limits_storage.limits.size = size;
85}
86
87uptr MemToShadowOffset(uptr x) {
88 return (x >> kShadowGranularity) << 1;
89}
90
91uint16_t *MemToShadow(uptr x, uptr shadow_base) {
92 return (uint16_t *)(shadow_base + MemToShadowOffset(x));
93}
94
95typedef int (*CFICheckFn)(u64, void *, void *);
96
97// This class reads and decodes the shadow contents.
98class ShadowValue {
99 uptr addr;
100 uint16_t v;
101 explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {}
102
103public:
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~svn345461/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
127class ShadowBuilder {
128 uptr shadow_;
129
130public:
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
145void 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
150void 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
161void 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~svn345461/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
176void 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~svn345461/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~svn345461/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~svn345461/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~svn345461/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~svn345461/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~svn345461/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").
213uptr 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) {
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
216 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
217 dynamic =
218 (const Elf_Dyn *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
219 break;
10
Execution continues on line 222
220 }
221 }
222 if (!dynamic) return 0;
11
Assuming 'dynamic' is non-null
12
Taking false branch
223 uptr strtab = 0, symtab = 0, strsz = 0;
13
'symtab' initialized to 0
224 for (const Elf_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 233
225 if (p->d_tag == DT_SYMTAB6)
16
Assuming the condition is false
17
Taking false branch
226 symtab = p->d_un.d_ptr;
227 else if (p->d_tag == DT_STRTAB5)
18
Assuming the condition is true
19
Taking true branch
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) {
22
Taking false branch
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++) {
23
Loop condition is true. Entering loop body
242 const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx];
243 if (phdr->p_type == PT_LOAD1) {
24
Assuming the condition is true
25
Taking true branch
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 &&
26
Assuming 'strtab' is >= 'beg'
27
Assuming the condition is true
28
Assuming 'symtab' is >= 'beg'
30
Taking true branch
247 symtab < end)
29
Assuming 'symtab' is < 'end'
248 break;
31
Execution continues on line 251
249 }
250 }
251 if (phdr_idx == info->dlpi_phnum) {
32
Taking false branch
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;
33
'p' initialized to a null pointer value
34
Assuming 'p' is < 'strtab'
35
Loop condition is true. Entering loop body
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;
36
Access to field 'st_name' results in a dereference of a null pointer (loaded from variable 'p')
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~svn345461/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~svn345461/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
275int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) {
276 uptr cfi_check = find_cfi_check_in_dso(info);
1
Calling 'find_cfi_check_in_dso'
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.
304void UpdateShadow() {
305 ShadowBuilder b;
306 b.Start();
307 dl_iterate_phdr(dl_iterate_phdr_cb, &b);
308 b.Install();
309}
310
311void 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~svn345461/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~svn345461/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
323THREADLOCAL__thread int in_loader;
324BlockingMutex shadow_update_lock(LINKER_INITIALIZED);
325
326void EnterLoader() {
327 if (in_loader == 0) {
328 shadow_update_lock.Lock();
329 }
330 ++in_loader;
331}
332
333void 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~svn345461/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
342ALWAYS_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
367void 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
400using namespace __cfi;
401
402extern "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
408extern "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
414static 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.
424INTERCEPTOR(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
432INTERCEPTOR(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
440static BlockingMutex interceptor_init_lock(LINKER_INITIALIZED);
441static bool interceptors_inited = false;
442
443static 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
454extern "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
459void __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.
473extern "C" {
474__attribute__((section(".preinit_array"),
475 used)) void (*__cfi_preinit)(void) = __cfi_init;
476}
477#endif