Bug Summary

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