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