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.
GetShadow()770b57cec5SDimitry Andric uptr GetShadow() {
780b57cec5SDimitry Andric return cfi_shadow_limits_storage.limits.start;
790b57cec5SDimitry Andric }
800b57cec5SDimitry Andric
GetShadowSize()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.
SetShadowSize(uptr size)860b57cec5SDimitry Andric void SetShadowSize(uptr size) {
870b57cec5SDimitry Andric cfi_shadow_limits_storage.limits.size = size;
880b57cec5SDimitry Andric }
890b57cec5SDimitry Andric
MemToShadowOffset(uptr x)900b57cec5SDimitry Andric uptr MemToShadowOffset(uptr x) {
910b57cec5SDimitry Andric return (x >> kShadowGranularity) << 1;
920b57cec5SDimitry Andric }
930b57cec5SDimitry Andric
MemToShadow(uptr x,uptr shadow_base)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;
ShadowValue(uptr addr,uint16_t v)1040b57cec5SDimitry Andric explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {}
1050b57cec5SDimitry Andric
1060b57cec5SDimitry Andric public:
is_invalid() const1070b57cec5SDimitry Andric bool is_invalid() const { return v == kInvalidShadow; }
1080b57cec5SDimitry Andric
is_unchecked() const1090b57cec5SDimitry Andric bool is_unchecked() const { return v == kUncheckedShadow; }
1100b57cec5SDimitry Andric
get_cfi_check() const1110b57cec5SDimitry 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.
load(uptr addr)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
Start()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
AddUnchecked(uptr begin,uptr end)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
Add(uptr begin,uptr end,uptr cfi_check)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
Install()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").
find_cfi_check_in_dso(dl_phdr_info * info)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
dl_iterate_phdr_cb(dl_phdr_info * info,size_t size,void * data)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.
UpdateShadow()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
InitShadow()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
EnterLoader()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
ExitLoader()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
CfiSlowPathCommon(u64 CallSiteTypeId,void * Ptr,void * DiagData)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
InitializeFlags()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
__cfi_slowpath(u64 CallSiteTypeId,void * Ptr)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
__cfi_slowpath_diag(u64 CallSiteTypeId,void * Ptr,void * DiagData)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.
INTERCEPTOR(void *,dlopen,const char * filename,int flag)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
INTERCEPTOR(int,dlclose,void * handle)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
EnsureInterceptorsInitialized()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
__cfi_init()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