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