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 #include <assert.h>
     15 #include <elf.h>
     16 #include <link.h>
     17 #include <string.h>
     18 #include <sys/mman.h>
     19 
     20 typedef ElfW(Phdr) Elf_Phdr;
     21 typedef ElfW(Ehdr) Elf_Ehdr;
     22 
     23 #include "interception/interception.h"
     24 #include "sanitizer_common/sanitizer_common.h"
     25 #include "sanitizer_common/sanitizer_flag_parser.h"
     26 #include "ubsan/ubsan_init.h"
     27 #include "ubsan/ubsan_flags.h"
     28 
     29 #ifdef CFI_ENABLE_DIAG
     30 #include "ubsan/ubsan_handlers.h"
     31 #endif
     32 
     33 namespace __cfi {
     34 
     35 #define kCfiShadowLimitsStorageSize 4096 // 1 page
     36 // Lets hope that the data segment is mapped with 4K pages.
     37 // The pointer to the cfi shadow region is stored at the start of this page.
     38 // The rest of the page is unused and re-mapped read-only.
     39 static union {
     40   char space[kCfiShadowLimitsStorageSize];
     41   struct {
     42     uptr start;
     43     uptr size;
     44   } limits;
     45 } cfi_shadow_limits_storage
     46     __attribute__((aligned(kCfiShadowLimitsStorageSize)));
     47 static constexpr uptr kShadowGranularity = 12;
     48 static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096
     49 
     50 static constexpr uint16_t kInvalidShadow = 0;
     51 static constexpr uint16_t kUncheckedShadow = 0xFFFFU;
     52 
     53 // Get the start address of the CFI shadow region.
     54 uptr GetShadow() {
     55   return cfi_shadow_limits_storage.limits.start;
     56 }
     57 
     58 uptr GetShadowSize() {
     59   return cfi_shadow_limits_storage.limits.size;
     60 }
     61 
     62 // This will only work while the shadow is not allocated.
     63 void SetShadowSize(uptr size) {
     64   cfi_shadow_limits_storage.limits.size = size;
     65 }
     66 
     67 uptr MemToShadowOffset(uptr x) {
     68   return (x >> kShadowGranularity) << 1;
     69 }
     70 
     71 uint16_t *MemToShadow(uptr x, uptr shadow_base) {
     72   return (uint16_t *)(shadow_base + MemToShadowOffset(x));
     73 }
     74 
     75 typedef int (*CFICheckFn)(u64, void *, void *);
     76 
     77 // This class reads and decodes the shadow contents.
     78 class ShadowValue {
     79   uptr addr;
     80   uint16_t v;
     81   explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {}
     82 
     83 public:
     84   bool is_invalid() const { return v == kInvalidShadow; }
     85 
     86   bool is_unchecked() const { return v == kUncheckedShadow; }
     87 
     88   CFICheckFn get_cfi_check() const {
     89     assert(!is_invalid() && !is_unchecked());
     90     uptr aligned_addr = addr & ~(kShadowAlign - 1);
     91     uptr p = aligned_addr - (((uptr)v - 1) << kShadowGranularity);
     92     return reinterpret_cast<CFICheckFn>(p);
     93   }
     94 
     95   // Load a shadow value for the given application memory address.
     96   static const ShadowValue load(uptr addr) {
     97     uptr shadow_base = GetShadow();
     98     uptr shadow_offset = MemToShadowOffset(addr);
     99     if (shadow_offset > GetShadowSize())
    100       return ShadowValue(addr, kInvalidShadow);
    101     else
    102       return ShadowValue(
    103           addr, *reinterpret_cast<uint16_t *>(shadow_base + shadow_offset));
    104   }
    105 };
    106 
    107 class ShadowBuilder {
    108   uptr shadow_;
    109 
    110 public:
    111   // Allocate a new empty shadow (for the entire address space) on the side.
    112   void Start();
    113   // Mark the given address range as unchecked.
    114   // This is used for uninstrumented libraries like libc.
    115   // Any CFI check with a target in that range will pass.
    116   void AddUnchecked(uptr begin, uptr end);
    117   // Mark the given address range as belonging to a library with the given
    118   // cfi_check function.
    119   void Add(uptr begin, uptr end, uptr cfi_check);
    120   // Finish shadow construction. Atomically switch the current active shadow
    121   // region with the newly constructed one and deallocate the former.
    122   void Install();
    123 };
    124 
    125 void ShadowBuilder::Start() {
    126   shadow_ = (uptr)MmapNoReserveOrDie(GetShadowSize(), "CFI shadow");
    127   VReport(1, "CFI: shadow at %zx .. %zx\n", shadow_, shadow_ + GetShadowSize());
    128 }
    129 
    130 void ShadowBuilder::AddUnchecked(uptr begin, uptr end) {
    131   uint16_t *shadow_begin = MemToShadow(begin, shadow_);
    132   uint16_t *shadow_end = MemToShadow(end - 1, shadow_) + 1;
    133   memset(shadow_begin, kUncheckedShadow,
    134          (shadow_end - shadow_begin) * sizeof(*shadow_begin));
    135 }
    136 
    137 void ShadowBuilder::Add(uptr begin, uptr end, uptr cfi_check) {
    138   assert((cfi_check & (kShadowAlign - 1)) == 0);
    139 
    140   // Don't fill anything below cfi_check. We can not represent those addresses
    141   // in the shadow, and must make sure at codegen to place all valid call
    142   // targets above cfi_check.
    143   begin = Max(begin, cfi_check);
    144   uint16_t *s = MemToShadow(begin, shadow_);
    145   uint16_t *s_end = MemToShadow(end - 1, shadow_) + 1;
    146   uint16_t sv = ((begin - cfi_check) >> kShadowGranularity) + 1;
    147   for (; s < s_end; s++, sv++)
    148     *s = sv;
    149 }
    150 
    151 #if SANITIZER_LINUX
    152 void ShadowBuilder::Install() {
    153   MprotectReadOnly(shadow_, GetShadowSize());
    154   uptr main_shadow = GetShadow();
    155   if (main_shadow) {
    156     // Update.
    157     void *res = mremap((void *)shadow_, GetShadowSize(), GetShadowSize(),
    158                        MREMAP_MAYMOVE | MREMAP_FIXED, (void *)main_shadow);
    159     CHECK(res != MAP_FAILED);
    160   } else {
    161     // Initial setup.
    162     CHECK_EQ(kCfiShadowLimitsStorageSize, GetPageSizeCached());
    163     CHECK_EQ(0, GetShadow());
    164     cfi_shadow_limits_storage.limits.start = shadow_;
    165     MprotectReadOnly((uptr)&cfi_shadow_limits_storage,
    166                      sizeof(cfi_shadow_limits_storage));
    167     CHECK_EQ(shadow_, GetShadow());
    168   }
    169 }
    170 #else
    171 #error not implemented
    172 #endif
    173 
    174 // This is a workaround for a glibc bug:
    175 // https://sourceware.org/bugzilla/show_bug.cgi?id=15199
    176 // Other platforms can, hopefully, just do
    177 //    dlopen(RTLD_NOLOAD | RTLD_LAZY)
    178 //    dlsym("__cfi_check").
    179 uptr find_cfi_check_in_dso(dl_phdr_info *info) {
    180   const ElfW(Dyn) *dynamic = nullptr;
    181   for (int i = 0; i < info->dlpi_phnum; ++i) {
    182     if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
    183       dynamic =
    184           (const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
    185       break;
    186     }
    187   }
    188   if (!dynamic) return 0;
    189   uptr strtab = 0, symtab = 0;
    190   for (const ElfW(Dyn) *p = dynamic; p->d_tag != PT_NULL; ++p) {
    191     if (p->d_tag == DT_SYMTAB)
    192       symtab = p->d_un.d_ptr;
    193     else if (p->d_tag == DT_STRTAB)
    194       strtab = p->d_un.d_ptr;
    195   }
    196 
    197   if (symtab > strtab) {
    198     VReport(1, "Can not handle: symtab > strtab (%p > %zx)\n", symtab, strtab);
    199     return 0;
    200   }
    201 
    202   // Verify that strtab and symtab are inside of the same LOAD segment.
    203   // This excludes VDSO, which has (very high) bogus strtab and symtab pointers.
    204   int phdr_idx;
    205   for (phdr_idx = 0; phdr_idx < info->dlpi_phnum; phdr_idx++) {
    206     const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx];
    207     if (phdr->p_type == PT_LOAD) {
    208       uptr beg = info->dlpi_addr + phdr->p_vaddr;
    209       uptr end = beg + phdr->p_memsz;
    210       if (strtab >= beg && strtab < end && symtab >= beg && symtab < end)
    211         break;
    212     }
    213   }
    214   if (phdr_idx == info->dlpi_phnum) {
    215     // Nope, either different segments or just bogus pointers.
    216     // Can not handle this.
    217     VReport(1, "Can not handle: symtab %p, strtab %zx\n", symtab, strtab);
    218     return 0;
    219   }
    220 
    221   for (const ElfW(Sym) *p = (const ElfW(Sym) *)symtab; (ElfW(Addr))p < strtab;
    222        ++p) {
    223     char *name = (char*)(strtab + p->st_name);
    224     if (strcmp(name, "__cfi_check") == 0) {
    225       assert(p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC));
    226       uptr addr = info->dlpi_addr + p->st_value;
    227       return addr;
    228     }
    229   }
    230   return 0;
    231 }
    232 
    233 int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) {
    234   uptr cfi_check = find_cfi_check_in_dso(info);
    235   if (cfi_check)
    236     VReport(1, "Module '%s' __cfi_check %zx\n", info->dlpi_name, cfi_check);
    237 
    238   ShadowBuilder *b = reinterpret_cast<ShadowBuilder *>(data);
    239 
    240   for (int i = 0; i < info->dlpi_phnum; i++) {
    241     const Elf_Phdr *phdr = &info->dlpi_phdr[i];
    242     if (phdr->p_type == PT_LOAD) {
    243       // Jump tables are in the executable segment.
    244       // VTables are in the non-executable one.
    245       // Need to fill shadow for both.
    246       // FIXME: reject writable if vtables are in the r/o segment. Depend on
    247       // PT_RELRO?
    248       uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
    249       uptr cur_end = cur_beg + phdr->p_memsz;
    250       if (cfi_check) {
    251         VReport(1, "   %zx .. %zx\n", cur_beg, cur_end);
    252         b->Add(cur_beg, cur_end, cfi_check);
    253       } else {
    254         b->AddUnchecked(cur_beg, cur_end);
    255       }
    256     }
    257   }
    258   return 0;
    259 }
    260 
    261 // Init or update shadow for the current set of loaded libraries.
    262 void UpdateShadow() {
    263   ShadowBuilder b;
    264   b.Start();
    265   dl_iterate_phdr(dl_iterate_phdr_cb, &b);
    266   b.Install();
    267 }
    268 
    269 void InitShadow() {
    270   CHECK_EQ(0, GetShadow());
    271   CHECK_EQ(0, GetShadowSize());
    272 
    273   uptr vma = GetMaxVirtualAddress();
    274   // Shadow is 2 -> 2**kShadowGranularity.
    275   SetShadowSize((vma >> (kShadowGranularity - 1)) + 1);
    276   VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, GetShadowSize());
    277 
    278   UpdateShadow();
    279 }
    280 
    281 THREADLOCAL int in_loader;
    282 BlockingMutex shadow_update_lock(LINKER_INITIALIZED);
    283 
    284 void EnterLoader() {
    285   if (in_loader == 0) {
    286     shadow_update_lock.Lock();
    287   }
    288   ++in_loader;
    289 }
    290 
    291 void ExitLoader() {
    292   CHECK(in_loader > 0);
    293   --in_loader;
    294   UpdateShadow();
    295   if (in_loader == 0) {
    296     shadow_update_lock.Unlock();
    297   }
    298 }
    299 
    300 ALWAYS_INLINE void CfiSlowPathCommon(u64 CallSiteTypeId, void *Ptr,
    301                                      void *DiagData) {
    302   uptr Addr = (uptr)Ptr;
    303   VReport(3, "__cfi_slowpath: %llx, %p\n", CallSiteTypeId, Ptr);
    304   ShadowValue sv = ShadowValue::load(Addr);
    305   if (sv.is_invalid()) {
    306     VReport(1, "CFI: invalid memory region for a check target: %p\n", Ptr);
    307 #ifdef CFI_ENABLE_DIAG
    308     if (DiagData) {
    309       __ubsan_handle_cfi_check_fail(
    310           reinterpret_cast<__ubsan::CFICheckFailData *>(DiagData), Addr, false);
    311       return;
    312     }
    313 #endif
    314     Trap();
    315   }
    316   if (sv.is_unchecked()) {
    317     VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr);
    318     return;
    319   }
    320   CFICheckFn cfi_check = sv.get_cfi_check();
    321   VReport(2, "__cfi_check at %p\n", cfi_check);
    322   cfi_check(CallSiteTypeId, Ptr, DiagData);
    323 }
    324 
    325 void InitializeFlags() {
    326   SetCommonFlagsDefaults();
    327 #ifdef CFI_ENABLE_DIAG
    328   __ubsan::Flags *uf = __ubsan::flags();
    329   uf->SetDefaults();
    330 #endif
    331 
    332   FlagParser cfi_parser;
    333   RegisterCommonFlags(&cfi_parser);
    334   cfi_parser.ParseString(GetEnv("CFI_OPTIONS"));
    335 
    336 #ifdef CFI_ENABLE_DIAG
    337   FlagParser ubsan_parser;
    338   __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
    339   RegisterCommonFlags(&ubsan_parser);
    340 
    341   const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
    342   ubsan_parser.ParseString(ubsan_default_options);
    343   ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
    344 #endif
    345 
    346   InitializeCommonFlags();
    347 
    348   if (Verbosity())
    349     ReportUnrecognizedFlags();
    350 
    351   if (common_flags()->help) {
    352     cfi_parser.PrintFlagDescriptions();
    353   }
    354 }
    355 
    356 } // namespace __cfi
    357 
    358 using namespace __cfi;
    359 
    360 extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
    361 __cfi_slowpath(u64 CallSiteTypeId, void *Ptr) {
    362   CfiSlowPathCommon(CallSiteTypeId, Ptr, nullptr);
    363 }
    364 
    365 #ifdef CFI_ENABLE_DIAG
    366 extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
    367 __cfi_slowpath_diag(u64 CallSiteTypeId, void *Ptr, void *DiagData) {
    368   CfiSlowPathCommon(CallSiteTypeId, Ptr, DiagData);
    369 }
    370 #endif
    371 
    372 // Setup shadow for dlopen()ed libraries.
    373 // The actual shadow setup happens after dlopen() returns, which means that
    374 // a library can not be a target of any CFI checks while its constructors are
    375 // running. It's unclear how to fix this without some extra help from libc.
    376 // In glibc, mmap inside dlopen is not interceptable.
    377 // Maybe a seccomp-bpf filter?
    378 // We could insert a high-priority constructor into the library, but that would
    379 // not help with the uninstrumented libraries.
    380 INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
    381   EnterLoader();
    382   void *handle = REAL(dlopen)(filename, flag);
    383   ExitLoader();
    384   return handle;
    385 }
    386 
    387 INTERCEPTOR(int, dlclose, void *handle) {
    388   EnterLoader();
    389   int res = REAL(dlclose)(handle);
    390   ExitLoader();
    391   return res;
    392 }
    393 
    394 extern "C" SANITIZER_INTERFACE_ATTRIBUTE
    395 #if !SANITIZER_CAN_USE_PREINIT_ARRAY
    396 // On ELF platforms, the constructor is invoked using .preinit_array (see below)
    397 __attribute__((constructor(0)))
    398 #endif
    399 void __cfi_init() {
    400   SanitizerToolName = "CFI";
    401   InitializeFlags();
    402   InitShadow();
    403 
    404   INTERCEPT_FUNCTION(dlopen);
    405   INTERCEPT_FUNCTION(dlclose);
    406 
    407 #ifdef CFI_ENABLE_DIAG
    408   __ubsan::InitAsPlugin();
    409 #endif
    410 }
    411 
    412 #if SANITIZER_CAN_USE_PREINIT_ARRAY
    413 // On ELF platforms, run cfi initialization before any other constructors.
    414 // On other platforms we use the constructor attribute to arrange to run our
    415 // initialization early.
    416 extern "C" {
    417 __attribute__((section(".preinit_array"),
    418                used)) void (*__cfi_preinit)(void) = __cfi_init;
    419 }
    420 #endif
    421