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