Home | History | Annotate | Download | only in cfi
      1 //===-------- cfi.cc ------------------------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file implements the runtime support for the cross-DSO CFI.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 // FIXME: Intercept dlopen/dlclose.
     15 // FIXME: Support diagnostic mode.
     16 // FIXME: Harden:
     17 //  * mprotect shadow, use mremap for updates
     18 //  * something else equally important
     19 
     20 #include <assert.h>
     21 #include <elf.h>
     22 #include <link.h>
     23 #include <string.h>
     24 
     25 typedef ElfW(Phdr) Elf_Phdr;
     26 typedef ElfW(Ehdr) Elf_Ehdr;
     27 
     28 #include "interception/interception.h"
     29 #include "sanitizer_common/sanitizer_common.h"
     30 #include "sanitizer_common/sanitizer_flag_parser.h"
     31 #include "ubsan/ubsan_init.h"
     32 #include "ubsan/ubsan_flags.h"
     33 
     34 static uptr __cfi_shadow;
     35 static constexpr uptr kShadowGranularity = 12;
     36 static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096
     37 
     38 static constexpr uint16_t kInvalidShadow = 0;
     39 static constexpr uint16_t kUncheckedShadow = 0xFFFFU;
     40 
     41 static uint16_t *mem_to_shadow(uptr x) {
     42   return (uint16_t *)(__cfi_shadow + ((x >> kShadowGranularity) << 1));
     43 }
     44 
     45 typedef int (*CFICheckFn)(uptr, void *);
     46 
     47 class ShadowValue {
     48   uptr addr;
     49   uint16_t v;
     50   explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {}
     51 
     52 public:
     53   bool is_invalid() const { return v == kInvalidShadow; }
     54 
     55   bool is_unchecked() const { return v == kUncheckedShadow; }
     56 
     57   CFICheckFn get_cfi_check() const {
     58     assert(!is_invalid() && !is_unchecked());
     59     uptr aligned_addr = addr & ~(kShadowAlign - 1);
     60     uptr p = aligned_addr - (((uptr)v - 1) << kShadowGranularity);
     61     return reinterpret_cast<CFICheckFn>(p);
     62   }
     63 
     64   // Load a shadow valud for the given application memory address.
     65   static const ShadowValue load(uptr addr) {
     66     return ShadowValue(addr, *mem_to_shadow(addr));
     67   }
     68 };
     69 
     70 static void fill_shadow_constant(uptr begin, uptr end, uint16_t v) {
     71   assert(v == kInvalidShadow || v == kUncheckedShadow);
     72   uint16_t *shadow_begin = mem_to_shadow(begin);
     73   uint16_t *shadow_end = mem_to_shadow(end - 1) + 1;
     74   memset(shadow_begin, v, (shadow_end - shadow_begin) * sizeof(*shadow_begin));
     75 }
     76 
     77 static void fill_shadow(uptr begin, uptr end, uptr cfi_check) {
     78   assert((cfi_check & (kShadowAlign - 1)) == 0);
     79 
     80   // Don't fill anything below cfi_check. We can not represent those addresses
     81   // in the shadow, and must make sure at codegen to place all valid call
     82   // targets above cfi_check.
     83   uptr p = Max(begin, cfi_check);
     84   uint16_t *s = mem_to_shadow(p);
     85   uint16_t *s_end = mem_to_shadow(end - 1) + 1;
     86   uint16_t sv = ((p - cfi_check) >> kShadowGranularity) + 1;
     87   for (; s < s_end; s++, sv++)
     88     *s = sv;
     89 
     90   // Sanity checks.
     91   uptr q = p & ~(kShadowAlign - 1);
     92   for (; q < end; q += kShadowAlign) {
     93     assert((uptr)ShadowValue::load(q).get_cfi_check() == cfi_check);
     94     assert((uptr)ShadowValue::load(q + kShadowAlign / 2).get_cfi_check() ==
     95            cfi_check);
     96     assert((uptr)ShadowValue::load(q + kShadowAlign - 1).get_cfi_check() ==
     97            cfi_check);
     98   }
     99 }
    100 
    101 // This is a workaround for a glibc bug:
    102 // https://sourceware.org/bugzilla/show_bug.cgi?id=15199
    103 // Other platforms can, hopefully, just do
    104 //    dlopen(RTLD_NOLOAD | RTLD_LAZY)
    105 //    dlsym("__cfi_check").
    106 static uptr find_cfi_check_in_dso(dl_phdr_info *info) {
    107   const ElfW(Dyn) *dynamic = nullptr;
    108   for (int i = 0; i < info->dlpi_phnum; ++i) {
    109     if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
    110       dynamic =
    111           (const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
    112       break;
    113     }
    114   }
    115   if (!dynamic) return 0;
    116   uptr strtab = 0, symtab = 0;
    117   for (const ElfW(Dyn) *p = dynamic; p->d_tag != PT_NULL; ++p) {
    118     if (p->d_tag == DT_SYMTAB)
    119       symtab = p->d_un.d_ptr;
    120     else if (p->d_tag == DT_STRTAB)
    121       strtab = p->d_un.d_ptr;
    122   }
    123 
    124   if (symtab > strtab) {
    125     VReport(1, "Can not handle: symtab > strtab (%p > %zx)\n", symtab, strtab);
    126     return 0;
    127   }
    128 
    129   // Verify that strtab and symtab are inside of the same LOAD segment.
    130   // This excludes VDSO, which has (very high) bogus strtab and symtab pointers.
    131   int phdr_idx;
    132   for (phdr_idx = 0; phdr_idx < info->dlpi_phnum; phdr_idx++) {
    133     const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx];
    134     if (phdr->p_type == PT_LOAD) {
    135       uptr beg = info->dlpi_addr + phdr->p_vaddr;
    136       uptr end = beg + phdr->p_memsz;
    137       if (strtab >= beg && strtab < end && symtab >= beg && symtab < end)
    138         break;
    139     }
    140   }
    141   if (phdr_idx == info->dlpi_phnum) {
    142     // Nope, either different segments or just bogus pointers.
    143     // Can not handle this.
    144     VReport(1, "Can not handle: symtab %p, strtab %zx\n", symtab, strtab);
    145     return 0;
    146   }
    147 
    148   for (const ElfW(Sym) *p = (const ElfW(Sym) *)symtab; (ElfW(Addr))p < strtab;
    149        ++p) {
    150     char *name = (char*)(strtab + p->st_name);
    151     if (strcmp(name, "__cfi_check") == 0) {
    152       assert(p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC));
    153       uptr addr = info->dlpi_addr + p->st_value;
    154       return addr;
    155     }
    156   }
    157   return 0;
    158 }
    159 
    160 static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) {
    161   uptr cfi_check = find_cfi_check_in_dso(info);
    162   if (cfi_check)
    163     VReport(1, "Module '%s' __cfi_check %zx\n", info->dlpi_name, cfi_check);
    164 
    165   for (int i = 0; i < info->dlpi_phnum; i++) {
    166     const Elf_Phdr *phdr = &info->dlpi_phdr[i];
    167     if (phdr->p_type == PT_LOAD) {
    168       // Jump tables are in the executable segment.
    169       // VTables are in the non-executable one.
    170       // Need to fill shadow for both.
    171       // FIXME: reject writable if vtables are in the r/o segment. Depend on
    172       // PT_RELRO?
    173       uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
    174       uptr cur_end = cur_beg + phdr->p_memsz;
    175       if (cfi_check) {
    176         VReport(1, "   %zx .. %zx\n", cur_beg, cur_end);
    177         fill_shadow(cur_beg, cur_end, cfi_check ? cfi_check : (uptr)(-1));
    178       } else {
    179         fill_shadow_constant(cur_beg, cur_end, kInvalidShadow);
    180       }
    181     }
    182   }
    183   return 0;
    184 }
    185 
    186 // Fill shadow for the initial libraries.
    187 static void init_shadow() {
    188   dl_iterate_phdr(dl_iterate_phdr_cb, nullptr);
    189 }
    190 
    191 extern "C" SANITIZER_INTERFACE_ATTRIBUTE
    192 void __cfi_slowpath(uptr CallSiteTypeId, void *Ptr) {
    193   uptr Addr = (uptr)Ptr;
    194   VReport(3, "__cfi_slowpath: %zx, %p\n", CallSiteTypeId, Ptr);
    195   ShadowValue sv = ShadowValue::load(Addr);
    196   if (sv.is_invalid()) {
    197     VReport(2, "CFI: invalid memory region for a function pointer (shadow==0): %p\n", Ptr);
    198     Die();
    199   }
    200   if (sv.is_unchecked()) {
    201     VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr);
    202     return;
    203   }
    204   CFICheckFn cfi_check = sv.get_cfi_check();
    205   VReport(2, "__cfi_check at %p\n", cfi_check);
    206   cfi_check(CallSiteTypeId, Ptr);
    207 }
    208 
    209 static void InitializeFlags() {
    210   SetCommonFlagsDefaults();
    211 #ifdef CFI_ENABLE_DIAG
    212   __ubsan::Flags *uf = __ubsan::flags();
    213   uf->SetDefaults();
    214 #endif
    215 
    216   FlagParser cfi_parser;
    217   RegisterCommonFlags(&cfi_parser);
    218   cfi_parser.ParseString(GetEnv("CFI_OPTIONS"));
    219 
    220 #ifdef CFI_ENABLE_DIAG
    221   FlagParser ubsan_parser;
    222   __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
    223   RegisterCommonFlags(&ubsan_parser);
    224 
    225   const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
    226   ubsan_parser.ParseString(ubsan_default_options);
    227   ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
    228 #endif
    229 
    230   SetVerbosity(common_flags()->verbosity);
    231 
    232   if (Verbosity()) ReportUnrecognizedFlags();
    233 
    234   if (common_flags()->help) {
    235     cfi_parser.PrintFlagDescriptions();
    236   }
    237 }
    238 
    239 extern "C" SANITIZER_INTERFACE_ATTRIBUTE
    240 #if !SANITIZER_CAN_USE_PREINIT_ARRAY
    241 // On ELF platforms, the constructor is invoked using .preinit_array (see below)
    242 __attribute__((constructor(0)))
    243 #endif
    244 void __cfi_init() {
    245   SanitizerToolName = "CFI";
    246   InitializeFlags();
    247 
    248   uptr vma = GetMaxVirtualAddress();
    249   // Shadow is 2 -> 2**kShadowGranularity.
    250   uptr shadow_size = (vma >> (kShadowGranularity - 1)) + 1;
    251   VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, shadow_size);
    252   void *shadow = MmapNoReserveOrDie(shadow_size, "CFI shadow");
    253   VReport(1, "CFI: shadow at %zx .. %zx\n", shadow,
    254           reinterpret_cast<uptr>(shadow) + shadow_size);
    255   __cfi_shadow = (uptr)shadow;
    256   init_shadow();
    257 
    258 #ifdef CFI_ENABLE_DIAG
    259   __ubsan::InitAsPlugin();
    260 #endif
    261 }
    262 
    263 #if SANITIZER_CAN_USE_PREINIT_ARRAY
    264 // On ELF platforms, run cfi initialization before any other constructors.
    265 // On other platforms we use the constructor attribute to arrange to run our
    266 // initialization early.
    267 extern "C" {
    268 __attribute__((section(".preinit_array"),
    269                used)) void (*__cfi_preinit)(void) = __cfi_init;
    270 }
    271 #endif
    272