xref: /freebsd/contrib/llvm-project/compiler-rt/lib/cfi/cfi.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //===-------- cfi.cpp -----------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements the runtime support for the cross-DSO CFI.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include <assert.h>
140b57cec5SDimitry Andric #include <elf.h>
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric #include "sanitizer_common/sanitizer_common.h"
170b57cec5SDimitry Andric #if SANITIZER_FREEBSD
180b57cec5SDimitry Andric #include <sys/link_elf.h>
190b57cec5SDimitry Andric #endif
200b57cec5SDimitry Andric #include <link.h>
210b57cec5SDimitry Andric #include <string.h>
220b57cec5SDimitry Andric #include <stdlib.h>
230b57cec5SDimitry Andric #include <sys/mman.h>
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric #if SANITIZER_LINUX
260b57cec5SDimitry Andric typedef ElfW(Phdr) Elf_Phdr;
270b57cec5SDimitry Andric typedef ElfW(Ehdr) Elf_Ehdr;
280b57cec5SDimitry Andric typedef ElfW(Addr) Elf_Addr;
290b57cec5SDimitry Andric typedef ElfW(Sym) Elf_Sym;
300b57cec5SDimitry Andric typedef ElfW(Dyn) Elf_Dyn;
310b57cec5SDimitry Andric #elif SANITIZER_FREEBSD
320b57cec5SDimitry Andric #if SANITIZER_WORDSIZE == 64
330b57cec5SDimitry Andric #define ElfW64_Dyn Elf_Dyn
340b57cec5SDimitry Andric #define ElfW64_Sym Elf_Sym
350b57cec5SDimitry Andric #else
360b57cec5SDimitry Andric #define ElfW32_Dyn Elf_Dyn
370b57cec5SDimitry Andric #define ElfW32_Sym Elf_Sym
380b57cec5SDimitry Andric #endif
390b57cec5SDimitry Andric #endif
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric #include "interception/interception.h"
420b57cec5SDimitry Andric #include "sanitizer_common/sanitizer_flag_parser.h"
430b57cec5SDimitry Andric #include "ubsan/ubsan_init.h"
440b57cec5SDimitry Andric #include "ubsan/ubsan_flags.h"
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric #ifdef CFI_ENABLE_DIAG
470b57cec5SDimitry Andric #include "ubsan/ubsan_handlers.h"
480b57cec5SDimitry Andric #endif
490b57cec5SDimitry Andric 
500b57cec5SDimitry Andric using namespace __sanitizer;
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric namespace __cfi {
530b57cec5SDimitry Andric 
54*5f757f3fSDimitry Andric #if SANITIZER_LOONGARCH64
55*5f757f3fSDimitry Andric #define kCfiShadowLimitsStorageSize 16384 // 16KiB on loongarch64 per page
56*5f757f3fSDimitry Andric #else
570b57cec5SDimitry Andric #define kCfiShadowLimitsStorageSize 4096 // 1 page
58*5f757f3fSDimitry Andric #endif
590b57cec5SDimitry Andric // Lets hope that the data segment is mapped with 4K pages.
600b57cec5SDimitry Andric // The pointer to the cfi shadow region is stored at the start of this page.
610b57cec5SDimitry Andric // The rest of the page is unused and re-mapped read-only.
620b57cec5SDimitry Andric static union {
630b57cec5SDimitry Andric   char space[kCfiShadowLimitsStorageSize];
640b57cec5SDimitry Andric   struct {
650b57cec5SDimitry Andric     uptr start;
660b57cec5SDimitry Andric     uptr size;
670b57cec5SDimitry Andric   } limits;
680b57cec5SDimitry Andric } cfi_shadow_limits_storage
690b57cec5SDimitry Andric     __attribute__((aligned(kCfiShadowLimitsStorageSize)));
700b57cec5SDimitry Andric static constexpr uptr kShadowGranularity = 12;
710b57cec5SDimitry Andric static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric static constexpr uint16_t kInvalidShadow = 0;
740b57cec5SDimitry Andric static constexpr uint16_t kUncheckedShadow = 0xFFFFU;
750b57cec5SDimitry Andric 
760b57cec5SDimitry Andric // Get the start address of the CFI shadow region.
770b57cec5SDimitry Andric uptr GetShadow() {
780b57cec5SDimitry Andric   return cfi_shadow_limits_storage.limits.start;
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric uptr GetShadowSize() {
820b57cec5SDimitry Andric   return cfi_shadow_limits_storage.limits.size;
830b57cec5SDimitry Andric }
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric // This will only work while the shadow is not allocated.
860b57cec5SDimitry Andric void SetShadowSize(uptr size) {
870b57cec5SDimitry Andric   cfi_shadow_limits_storage.limits.size = size;
880b57cec5SDimitry Andric }
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric uptr MemToShadowOffset(uptr x) {
910b57cec5SDimitry Andric   return (x >> kShadowGranularity) << 1;
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric uint16_t *MemToShadow(uptr x, uptr shadow_base) {
950b57cec5SDimitry Andric   return (uint16_t *)(shadow_base + MemToShadowOffset(x));
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric 
980b57cec5SDimitry Andric typedef int (*CFICheckFn)(u64, void *, void *);
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric // This class reads and decodes the shadow contents.
1010b57cec5SDimitry Andric class ShadowValue {
1020b57cec5SDimitry Andric   uptr addr;
1030b57cec5SDimitry Andric   uint16_t v;
1040b57cec5SDimitry Andric   explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {}
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric public:
1070b57cec5SDimitry Andric   bool is_invalid() const { return v == kInvalidShadow; }
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric   bool is_unchecked() const { return v == kUncheckedShadow; }
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric   CFICheckFn get_cfi_check() const {
1120b57cec5SDimitry Andric     assert(!is_invalid() && !is_unchecked());
1130b57cec5SDimitry Andric     uptr aligned_addr = addr & ~(kShadowAlign - 1);
1140b57cec5SDimitry Andric     uptr p = aligned_addr - (((uptr)v - 1) << kShadowGranularity);
1150b57cec5SDimitry Andric     return reinterpret_cast<CFICheckFn>(p);
1160b57cec5SDimitry Andric   }
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric   // Load a shadow value for the given application memory address.
1190b57cec5SDimitry Andric   static const ShadowValue load(uptr addr) {
1200b57cec5SDimitry Andric     uptr shadow_base = GetShadow();
1210b57cec5SDimitry Andric     uptr shadow_offset = MemToShadowOffset(addr);
1220b57cec5SDimitry Andric     if (shadow_offset > GetShadowSize())
1230b57cec5SDimitry Andric       return ShadowValue(addr, kInvalidShadow);
1240b57cec5SDimitry Andric     else
1250b57cec5SDimitry Andric       return ShadowValue(
1260b57cec5SDimitry Andric           addr, *reinterpret_cast<uint16_t *>(shadow_base + shadow_offset));
1270b57cec5SDimitry Andric   }
1280b57cec5SDimitry Andric };
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric class ShadowBuilder {
1310b57cec5SDimitry Andric   uptr shadow_;
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric public:
1340b57cec5SDimitry Andric   // Allocate a new empty shadow (for the entire address space) on the side.
1350b57cec5SDimitry Andric   void Start();
1360b57cec5SDimitry Andric   // Mark the given address range as unchecked.
1370b57cec5SDimitry Andric   // This is used for uninstrumented libraries like libc.
1380b57cec5SDimitry Andric   // Any CFI check with a target in that range will pass.
1390b57cec5SDimitry Andric   void AddUnchecked(uptr begin, uptr end);
1400b57cec5SDimitry Andric   // Mark the given address range as belonging to a library with the given
1410b57cec5SDimitry Andric   // cfi_check function.
1420b57cec5SDimitry Andric   void Add(uptr begin, uptr end, uptr cfi_check);
1430b57cec5SDimitry Andric   // Finish shadow construction. Atomically switch the current active shadow
1440b57cec5SDimitry Andric   // region with the newly constructed one and deallocate the former.
1450b57cec5SDimitry Andric   void Install();
1460b57cec5SDimitry Andric };
1470b57cec5SDimitry Andric 
1480b57cec5SDimitry Andric void ShadowBuilder::Start() {
1490b57cec5SDimitry Andric   shadow_ = (uptr)MmapNoReserveOrDie(GetShadowSize(), "CFI shadow");
1500b57cec5SDimitry Andric   VReport(1, "CFI: shadow at %zx .. %zx\n", shadow_, shadow_ + GetShadowSize());
1510b57cec5SDimitry Andric }
1520b57cec5SDimitry Andric 
1530b57cec5SDimitry Andric void ShadowBuilder::AddUnchecked(uptr begin, uptr end) {
1540b57cec5SDimitry Andric   uint16_t *shadow_begin = MemToShadow(begin, shadow_);
1550b57cec5SDimitry Andric   uint16_t *shadow_end = MemToShadow(end - 1, shadow_) + 1;
1560b57cec5SDimitry Andric   // memset takes a byte, so our unchecked shadow value requires both bytes to
1570b57cec5SDimitry Andric   // be the same. Make sure we're ok during compilation.
1580b57cec5SDimitry Andric   static_assert((kUncheckedShadow & 0xff) == ((kUncheckedShadow >> 8) & 0xff),
1590b57cec5SDimitry Andric                 "Both bytes of the 16-bit value must be the same!");
1600b57cec5SDimitry Andric   memset(shadow_begin, kUncheckedShadow & 0xff,
1610b57cec5SDimitry Andric          (shadow_end - shadow_begin) * sizeof(*shadow_begin));
1620b57cec5SDimitry Andric }
1630b57cec5SDimitry Andric 
1640b57cec5SDimitry Andric void ShadowBuilder::Add(uptr begin, uptr end, uptr cfi_check) {
1650b57cec5SDimitry Andric   assert((cfi_check & (kShadowAlign - 1)) == 0);
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric   // Don't fill anything below cfi_check. We can not represent those addresses
1680b57cec5SDimitry Andric   // in the shadow, and must make sure at codegen to place all valid call
1690b57cec5SDimitry Andric   // targets above cfi_check.
1700b57cec5SDimitry Andric   begin = Max(begin, cfi_check);
1710b57cec5SDimitry Andric   uint16_t *s = MemToShadow(begin, shadow_);
1720b57cec5SDimitry Andric   uint16_t *s_end = MemToShadow(end - 1, shadow_) + 1;
1730b57cec5SDimitry Andric   uint16_t sv = ((begin - cfi_check) >> kShadowGranularity) + 1;
1740b57cec5SDimitry Andric   for (; s < s_end; s++, sv++)
1750b57cec5SDimitry Andric     *s = sv;
1760b57cec5SDimitry Andric }
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric #if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
1790b57cec5SDimitry Andric void ShadowBuilder::Install() {
1800b57cec5SDimitry Andric   MprotectReadOnly(shadow_, GetShadowSize());
1810b57cec5SDimitry Andric   uptr main_shadow = GetShadow();
1820b57cec5SDimitry Andric   if (main_shadow) {
1830b57cec5SDimitry Andric     // Update.
1840b57cec5SDimitry Andric #if SANITIZER_LINUX
1850b57cec5SDimitry Andric     void *res = mremap((void *)shadow_, GetShadowSize(), GetShadowSize(),
1860b57cec5SDimitry Andric                        MREMAP_MAYMOVE | MREMAP_FIXED, (void *)main_shadow);
1870b57cec5SDimitry Andric     CHECK(res != MAP_FAILED);
1880b57cec5SDimitry Andric #elif SANITIZER_NETBSD
1890b57cec5SDimitry Andric     void *res = mremap((void *)shadow_, GetShadowSize(), (void *)main_shadow,
1900b57cec5SDimitry Andric                        GetShadowSize(), MAP_FIXED);
1910b57cec5SDimitry Andric     CHECK(res != MAP_FAILED);
1920b57cec5SDimitry Andric #else
1930b57cec5SDimitry Andric     void *res = MmapFixedOrDie(shadow_, GetShadowSize(), "cfi shadow");
1940b57cec5SDimitry Andric     CHECK(res != MAP_FAILED);
1950b57cec5SDimitry Andric     ::memcpy(&shadow_, &main_shadow, GetShadowSize());
1960b57cec5SDimitry Andric #endif
1970b57cec5SDimitry Andric   } else {
1980b57cec5SDimitry Andric     // Initial setup.
1990b57cec5SDimitry Andric     CHECK_EQ(kCfiShadowLimitsStorageSize, GetPageSizeCached());
2000b57cec5SDimitry Andric     CHECK_EQ(0, GetShadow());
2010b57cec5SDimitry Andric     cfi_shadow_limits_storage.limits.start = shadow_;
2020b57cec5SDimitry Andric     MprotectReadOnly((uptr)&cfi_shadow_limits_storage,
2030b57cec5SDimitry Andric                      sizeof(cfi_shadow_limits_storage));
2040b57cec5SDimitry Andric     CHECK_EQ(shadow_, GetShadow());
2050b57cec5SDimitry Andric   }
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric #else
2080b57cec5SDimitry Andric #error not implemented
2090b57cec5SDimitry Andric #endif
2100b57cec5SDimitry Andric 
2110b57cec5SDimitry Andric // This is a workaround for a glibc bug:
2120b57cec5SDimitry Andric // https://sourceware.org/bugzilla/show_bug.cgi?id=15199
2130b57cec5SDimitry Andric // Other platforms can, hopefully, just do
2140b57cec5SDimitry Andric //    dlopen(RTLD_NOLOAD | RTLD_LAZY)
2150b57cec5SDimitry Andric //    dlsym("__cfi_check").
2160b57cec5SDimitry Andric uptr find_cfi_check_in_dso(dl_phdr_info *info) {
2170b57cec5SDimitry Andric   const Elf_Dyn *dynamic = nullptr;
2180b57cec5SDimitry Andric   for (int i = 0; i < info->dlpi_phnum; ++i) {
2190b57cec5SDimitry Andric     if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
2200b57cec5SDimitry Andric       dynamic =
2210b57cec5SDimitry Andric           (const Elf_Dyn *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
2220b57cec5SDimitry Andric       break;
2230b57cec5SDimitry Andric     }
2240b57cec5SDimitry Andric   }
2250b57cec5SDimitry Andric   if (!dynamic) return 0;
2260b57cec5SDimitry Andric   uptr strtab = 0, symtab = 0, strsz = 0;
2270b57cec5SDimitry Andric   for (const Elf_Dyn *p = dynamic; p->d_tag != PT_NULL; ++p) {
2280b57cec5SDimitry Andric     if (p->d_tag == DT_SYMTAB)
2290b57cec5SDimitry Andric       symtab = p->d_un.d_ptr;
2300b57cec5SDimitry Andric     else if (p->d_tag == DT_STRTAB)
2310b57cec5SDimitry Andric       strtab = p->d_un.d_ptr;
2320b57cec5SDimitry Andric     else if (p->d_tag == DT_STRSZ)
2330b57cec5SDimitry Andric       strsz = p->d_un.d_ptr;
2340b57cec5SDimitry Andric   }
2350b57cec5SDimitry Andric 
2360b57cec5SDimitry Andric   if (symtab > strtab) {
2374824e7fdSDimitry Andric     VReport(1, "Can not handle: symtab > strtab (%zx > %zx)\n", symtab, strtab);
2380b57cec5SDimitry Andric     return 0;
2390b57cec5SDimitry Andric   }
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   // Verify that strtab and symtab are inside of the same LOAD segment.
2420b57cec5SDimitry Andric   // This excludes VDSO, which has (very high) bogus strtab and symtab pointers.
2430b57cec5SDimitry Andric   int phdr_idx;
2440b57cec5SDimitry Andric   for (phdr_idx = 0; phdr_idx < info->dlpi_phnum; phdr_idx++) {
2450b57cec5SDimitry Andric     const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx];
2460b57cec5SDimitry Andric     if (phdr->p_type == PT_LOAD) {
2470b57cec5SDimitry Andric       uptr beg = info->dlpi_addr + phdr->p_vaddr;
2480b57cec5SDimitry Andric       uptr end = beg + phdr->p_memsz;
2490b57cec5SDimitry Andric       if (strtab >= beg && strtab + strsz < end && symtab >= beg &&
2500b57cec5SDimitry Andric           symtab < end)
2510b57cec5SDimitry Andric         break;
2520b57cec5SDimitry Andric     }
2530b57cec5SDimitry Andric   }
2540b57cec5SDimitry Andric   if (phdr_idx == info->dlpi_phnum) {
2550b57cec5SDimitry Andric     // Nope, either different segments or just bogus pointers.
2560b57cec5SDimitry Andric     // Can not handle this.
2574824e7fdSDimitry Andric     VReport(1, "Can not handle: symtab %zx, strtab %zx\n", symtab, strtab);
2580b57cec5SDimitry Andric     return 0;
2590b57cec5SDimitry Andric   }
2600b57cec5SDimitry Andric 
2610b57cec5SDimitry Andric   for (const Elf_Sym *p = (const Elf_Sym *)symtab; (Elf_Addr)p < strtab;
2620b57cec5SDimitry Andric        ++p) {
2630b57cec5SDimitry Andric     // There is no reliable way to find the end of the symbol table. In
2640b57cec5SDimitry Andric     // lld-produces files, there are other sections between symtab and strtab.
2650b57cec5SDimitry Andric     // Stop looking when the symbol name is not inside strtab.
2660b57cec5SDimitry Andric     if (p->st_name >= strsz) break;
2670b57cec5SDimitry Andric     char *name = (char*)(strtab + p->st_name);
2680b57cec5SDimitry Andric     if (strcmp(name, "__cfi_check") == 0) {
2690b57cec5SDimitry Andric       assert(p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC) ||
2700b57cec5SDimitry Andric              p->st_info == ELF32_ST_INFO(STB_WEAK, STT_FUNC));
2710b57cec5SDimitry Andric       uptr addr = info->dlpi_addr + p->st_value;
2720b57cec5SDimitry Andric       return addr;
2730b57cec5SDimitry Andric     }
2740b57cec5SDimitry Andric   }
2750b57cec5SDimitry Andric   return 0;
2760b57cec5SDimitry Andric }
2770b57cec5SDimitry Andric 
2780b57cec5SDimitry Andric int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) {
2790b57cec5SDimitry Andric   uptr cfi_check = find_cfi_check_in_dso(info);
2800b57cec5SDimitry Andric   if (cfi_check)
2810b57cec5SDimitry Andric     VReport(1, "Module '%s' __cfi_check %zx\n", info->dlpi_name, cfi_check);
2820b57cec5SDimitry Andric 
2830b57cec5SDimitry Andric   ShadowBuilder *b = reinterpret_cast<ShadowBuilder *>(data);
2840b57cec5SDimitry Andric 
2850b57cec5SDimitry Andric   for (int i = 0; i < info->dlpi_phnum; i++) {
2860b57cec5SDimitry Andric     const Elf_Phdr *phdr = &info->dlpi_phdr[i];
2870b57cec5SDimitry Andric     if (phdr->p_type == PT_LOAD) {
2880b57cec5SDimitry Andric       // Jump tables are in the executable segment.
2890b57cec5SDimitry Andric       // VTables are in the non-executable one.
2900b57cec5SDimitry Andric       // Need to fill shadow for both.
2910b57cec5SDimitry Andric       // FIXME: reject writable if vtables are in the r/o segment. Depend on
2920b57cec5SDimitry Andric       // PT_RELRO?
2930b57cec5SDimitry Andric       uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
2940b57cec5SDimitry Andric       uptr cur_end = cur_beg + phdr->p_memsz;
2950b57cec5SDimitry Andric       if (cfi_check) {
2960b57cec5SDimitry Andric         VReport(1, "   %zx .. %zx\n", cur_beg, cur_end);
2970b57cec5SDimitry Andric         b->Add(cur_beg, cur_end, cfi_check);
2980b57cec5SDimitry Andric       } else {
2990b57cec5SDimitry Andric         b->AddUnchecked(cur_beg, cur_end);
3000b57cec5SDimitry Andric       }
3010b57cec5SDimitry Andric     }
3020b57cec5SDimitry Andric   }
3030b57cec5SDimitry Andric   return 0;
3040b57cec5SDimitry Andric }
3050b57cec5SDimitry Andric 
3060b57cec5SDimitry Andric // Init or update shadow for the current set of loaded libraries.
3070b57cec5SDimitry Andric void UpdateShadow() {
3080b57cec5SDimitry Andric   ShadowBuilder b;
3090b57cec5SDimitry Andric   b.Start();
3100b57cec5SDimitry Andric   dl_iterate_phdr(dl_iterate_phdr_cb, &b);
3110b57cec5SDimitry Andric   b.Install();
3120b57cec5SDimitry Andric }
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric void InitShadow() {
3150b57cec5SDimitry Andric   CHECK_EQ(0, GetShadow());
3160b57cec5SDimitry Andric   CHECK_EQ(0, GetShadowSize());
3170b57cec5SDimitry Andric 
3180b57cec5SDimitry Andric   uptr vma = GetMaxUserVirtualAddress();
3190b57cec5SDimitry Andric   // Shadow is 2 -> 2**kShadowGranularity.
3200b57cec5SDimitry Andric   SetShadowSize((vma >> (kShadowGranularity - 1)) + 1);
3210b57cec5SDimitry Andric   VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, GetShadowSize());
3220b57cec5SDimitry Andric 
3230b57cec5SDimitry Andric   UpdateShadow();
3240b57cec5SDimitry Andric }
3250b57cec5SDimitry Andric 
3260b57cec5SDimitry Andric THREADLOCAL int in_loader;
327349cc55cSDimitry Andric Mutex shadow_update_lock;
3280b57cec5SDimitry Andric 
32904eeddc0SDimitry Andric void EnterLoader() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
3300b57cec5SDimitry Andric   if (in_loader == 0) {
3310b57cec5SDimitry Andric     shadow_update_lock.Lock();
3320b57cec5SDimitry Andric   }
3330b57cec5SDimitry Andric   ++in_loader;
3340b57cec5SDimitry Andric }
3350b57cec5SDimitry Andric 
33604eeddc0SDimitry Andric void ExitLoader() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
3370b57cec5SDimitry Andric   CHECK(in_loader > 0);
3380b57cec5SDimitry Andric   --in_loader;
3390b57cec5SDimitry Andric   UpdateShadow();
3400b57cec5SDimitry Andric   if (in_loader == 0) {
3410b57cec5SDimitry Andric     shadow_update_lock.Unlock();
3420b57cec5SDimitry Andric   }
3430b57cec5SDimitry Andric }
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric ALWAYS_INLINE void CfiSlowPathCommon(u64 CallSiteTypeId, void *Ptr,
3460b57cec5SDimitry Andric                                      void *DiagData) {
3470b57cec5SDimitry Andric   uptr Addr = (uptr)Ptr;
3480b57cec5SDimitry Andric   VReport(3, "__cfi_slowpath: %llx, %p\n", CallSiteTypeId, Ptr);
3490b57cec5SDimitry Andric   ShadowValue sv = ShadowValue::load(Addr);
3500b57cec5SDimitry Andric   if (sv.is_invalid()) {
3510b57cec5SDimitry Andric     VReport(1, "CFI: invalid memory region for a check target: %p\n", Ptr);
3520b57cec5SDimitry Andric #ifdef CFI_ENABLE_DIAG
3530b57cec5SDimitry Andric     if (DiagData) {
3540b57cec5SDimitry Andric       __ubsan_handle_cfi_check_fail(
3550b57cec5SDimitry Andric           reinterpret_cast<__ubsan::CFICheckFailData *>(DiagData), Addr, false);
3560b57cec5SDimitry Andric       return;
3570b57cec5SDimitry Andric     }
3580b57cec5SDimitry Andric #endif
3590b57cec5SDimitry Andric     Trap();
3600b57cec5SDimitry Andric   }
3610b57cec5SDimitry Andric   if (sv.is_unchecked()) {
3620b57cec5SDimitry Andric     VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr);
3630b57cec5SDimitry Andric     return;
3640b57cec5SDimitry Andric   }
3650b57cec5SDimitry Andric   CFICheckFn cfi_check = sv.get_cfi_check();
366349cc55cSDimitry Andric   VReport(2, "__cfi_check at %p\n", (void *)cfi_check);
3670b57cec5SDimitry Andric   cfi_check(CallSiteTypeId, Ptr, DiagData);
3680b57cec5SDimitry Andric }
3690b57cec5SDimitry Andric 
3700b57cec5SDimitry Andric void InitializeFlags() {
3710b57cec5SDimitry Andric   SetCommonFlagsDefaults();
3720b57cec5SDimitry Andric #ifdef CFI_ENABLE_DIAG
3730b57cec5SDimitry Andric   __ubsan::Flags *uf = __ubsan::flags();
3740b57cec5SDimitry Andric   uf->SetDefaults();
3750b57cec5SDimitry Andric #endif
3760b57cec5SDimitry Andric 
3770b57cec5SDimitry Andric   FlagParser cfi_parser;
3780b57cec5SDimitry Andric   RegisterCommonFlags(&cfi_parser);
3790b57cec5SDimitry Andric   cfi_parser.ParseStringFromEnv("CFI_OPTIONS");
3800b57cec5SDimitry Andric 
3810b57cec5SDimitry Andric #ifdef CFI_ENABLE_DIAG
3820b57cec5SDimitry Andric   FlagParser ubsan_parser;
3830b57cec5SDimitry Andric   __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
3840b57cec5SDimitry Andric   RegisterCommonFlags(&ubsan_parser);
3850b57cec5SDimitry Andric 
386e8d8bef9SDimitry Andric   const char *ubsan_default_options = __ubsan_default_options();
3870b57cec5SDimitry Andric   ubsan_parser.ParseString(ubsan_default_options);
3880b57cec5SDimitry Andric   ubsan_parser.ParseStringFromEnv("UBSAN_OPTIONS");
3890b57cec5SDimitry Andric #endif
3900b57cec5SDimitry Andric 
3910b57cec5SDimitry Andric   InitializeCommonFlags();
3920b57cec5SDimitry Andric 
3930b57cec5SDimitry Andric   if (Verbosity())
3940b57cec5SDimitry Andric     ReportUnrecognizedFlags();
3950b57cec5SDimitry Andric 
3960b57cec5SDimitry Andric   if (common_flags()->help) {
3970b57cec5SDimitry Andric     cfi_parser.PrintFlagDescriptions();
3980b57cec5SDimitry Andric   }
3990b57cec5SDimitry Andric }
4000b57cec5SDimitry Andric 
4010b57cec5SDimitry Andric } // namespace __cfi
4020b57cec5SDimitry Andric 
4030b57cec5SDimitry Andric using namespace __cfi;
4040b57cec5SDimitry Andric 
4050b57cec5SDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
4060b57cec5SDimitry Andric __cfi_slowpath(u64 CallSiteTypeId, void *Ptr) {
4070b57cec5SDimitry Andric   CfiSlowPathCommon(CallSiteTypeId, Ptr, nullptr);
4080b57cec5SDimitry Andric }
4090b57cec5SDimitry Andric 
4100b57cec5SDimitry Andric #ifdef CFI_ENABLE_DIAG
4110b57cec5SDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
4120b57cec5SDimitry Andric __cfi_slowpath_diag(u64 CallSiteTypeId, void *Ptr, void *DiagData) {
4130b57cec5SDimitry Andric   CfiSlowPathCommon(CallSiteTypeId, Ptr, DiagData);
4140b57cec5SDimitry Andric }
4150b57cec5SDimitry Andric #endif
4160b57cec5SDimitry Andric 
4170b57cec5SDimitry Andric static void EnsureInterceptorsInitialized();
4180b57cec5SDimitry Andric 
4190b57cec5SDimitry Andric // Setup shadow for dlopen()ed libraries.
4200b57cec5SDimitry Andric // The actual shadow setup happens after dlopen() returns, which means that
4210b57cec5SDimitry Andric // a library can not be a target of any CFI checks while its constructors are
4220b57cec5SDimitry Andric // running. It's unclear how to fix this without some extra help from libc.
4230b57cec5SDimitry Andric // In glibc, mmap inside dlopen is not interceptable.
4240b57cec5SDimitry Andric // Maybe a seccomp-bpf filter?
4250b57cec5SDimitry Andric // We could insert a high-priority constructor into the library, but that would
4260b57cec5SDimitry Andric // not help with the uninstrumented libraries.
4270b57cec5SDimitry Andric INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
4280b57cec5SDimitry Andric   EnsureInterceptorsInitialized();
4290b57cec5SDimitry Andric   EnterLoader();
4300b57cec5SDimitry Andric   void *handle = REAL(dlopen)(filename, flag);
4310b57cec5SDimitry Andric   ExitLoader();
4320b57cec5SDimitry Andric   return handle;
4330b57cec5SDimitry Andric }
4340b57cec5SDimitry Andric 
4350b57cec5SDimitry Andric INTERCEPTOR(int, dlclose, void *handle) {
4360b57cec5SDimitry Andric   EnsureInterceptorsInitialized();
4370b57cec5SDimitry Andric   EnterLoader();
4380b57cec5SDimitry Andric   int res = REAL(dlclose)(handle);
4390b57cec5SDimitry Andric   ExitLoader();
4400b57cec5SDimitry Andric   return res;
4410b57cec5SDimitry Andric }
4420b57cec5SDimitry Andric 
443349cc55cSDimitry Andric static Mutex interceptor_init_lock;
4440b57cec5SDimitry Andric static bool interceptors_inited = false;
4450b57cec5SDimitry Andric 
4460b57cec5SDimitry Andric static void EnsureInterceptorsInitialized() {
447349cc55cSDimitry Andric   Lock lock(&interceptor_init_lock);
4480b57cec5SDimitry Andric   if (interceptors_inited)
4490b57cec5SDimitry Andric     return;
4500b57cec5SDimitry Andric 
4510b57cec5SDimitry Andric   INTERCEPT_FUNCTION(dlopen);
4520b57cec5SDimitry Andric   INTERCEPT_FUNCTION(dlclose);
4530b57cec5SDimitry Andric 
4540b57cec5SDimitry Andric   interceptors_inited = true;
4550b57cec5SDimitry Andric }
4560b57cec5SDimitry Andric 
4570b57cec5SDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
4580b57cec5SDimitry Andric #if !SANITIZER_CAN_USE_PREINIT_ARRAY
4590b57cec5SDimitry Andric // On ELF platforms, the constructor is invoked using .preinit_array (see below)
4600b57cec5SDimitry Andric __attribute__((constructor(0)))
4610b57cec5SDimitry Andric #endif
4620b57cec5SDimitry Andric void __cfi_init() {
4630b57cec5SDimitry Andric   SanitizerToolName = "CFI";
4640b57cec5SDimitry Andric   InitializeFlags();
4650b57cec5SDimitry Andric   InitShadow();
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric #ifdef CFI_ENABLE_DIAG
4680b57cec5SDimitry Andric   __ubsan::InitAsPlugin();
4690b57cec5SDimitry Andric #endif
4700b57cec5SDimitry Andric }
4710b57cec5SDimitry Andric 
4720b57cec5SDimitry Andric #if SANITIZER_CAN_USE_PREINIT_ARRAY
4730b57cec5SDimitry Andric // On ELF platforms, run cfi initialization before any other constructors.
4740b57cec5SDimitry Andric // On other platforms we use the constructor attribute to arrange to run our
4750b57cec5SDimitry Andric // initialization early.
4760b57cec5SDimitry Andric extern "C" {
4770b57cec5SDimitry Andric __attribute__((section(".preinit_array"),
4780b57cec5SDimitry Andric                used)) void (*__cfi_preinit)(void) = __cfi_init;
4790b57cec5SDimitry Andric }
4800b57cec5SDimitry Andric #endif
481