Home | History | Annotate | Download | only in linker
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include "linker_cfi.h"
     30 
     31 #include "linker_debug.h"
     32 #include "linker_globals.h"
     33 #include "private/bionic_page.h"
     34 #include "private/bionic_prctl.h"
     35 
     36 #include <sys/mman.h>
     37 #include <sys/types.h>
     38 #include <cstdint>
     39 
     40 // Update shadow without making it writable by preparing the data on the side and mremap-ing it in
     41 // place.
     42 class ShadowWrite {
     43   char* shadow_start;
     44   char* shadow_end;
     45   char* aligned_start;
     46   char* aligned_end;
     47   char* tmp_start;
     48 
     49  public:
     50   ShadowWrite(uint16_t* s, uint16_t* e) {
     51     shadow_start = reinterpret_cast<char*>(s);
     52     shadow_end = reinterpret_cast<char*>(e);
     53     aligned_start = reinterpret_cast<char*>(PAGE_START(reinterpret_cast<uintptr_t>(shadow_start)));
     54     aligned_end = reinterpret_cast<char*>(PAGE_END(reinterpret_cast<uintptr_t>(shadow_end)));
     55     tmp_start =
     56         reinterpret_cast<char*>(mmap(nullptr, aligned_end - aligned_start, PROT_READ | PROT_WRITE,
     57                                      MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
     58     CHECK(tmp_start != MAP_FAILED);
     59     memcpy(tmp_start, aligned_start, shadow_start - aligned_start);
     60     memcpy(tmp_start + (shadow_end - aligned_start), shadow_end, aligned_end - shadow_end);
     61   }
     62 
     63   uint16_t* begin() {
     64     return reinterpret_cast<uint16_t*>(tmp_start + (shadow_start - aligned_start));
     65   }
     66 
     67   uint16_t* end() {
     68     return reinterpret_cast<uint16_t*>(tmp_start + (shadow_end - aligned_start));
     69   }
     70 
     71   ~ShadowWrite() {
     72     size_t size = aligned_end - aligned_start;
     73     mprotect(tmp_start, size, PROT_READ);
     74     void* res = mremap(tmp_start, size, size, MREMAP_MAYMOVE | MREMAP_FIXED,
     75                        reinterpret_cast<void*>(aligned_start));
     76     CHECK(res != MAP_FAILED);
     77   }
     78 };
     79 
     80 void CFIShadowWriter::FixupVmaName() {
     81   prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, *shadow_start, kShadowSize, "cfi shadow");
     82 }
     83 
     84 void CFIShadowWriter::AddConstant(uintptr_t begin, uintptr_t end, uint16_t v) {
     85   uint16_t* shadow_begin = MemToShadow(begin);
     86   uint16_t* shadow_end = MemToShadow(end - 1) + 1;
     87 
     88   ShadowWrite sw(shadow_begin, shadow_end);
     89   std::fill(sw.begin(), sw.end(), v);
     90 }
     91 
     92 void CFIShadowWriter::AddUnchecked(uintptr_t begin, uintptr_t end) {
     93   AddConstant(begin, end, kUncheckedShadow);
     94 }
     95 
     96 void CFIShadowWriter::AddInvalid(uintptr_t begin, uintptr_t end) {
     97   AddConstant(begin, end, kInvalidShadow);
     98 }
     99 
    100 void CFIShadowWriter::Add(uintptr_t begin, uintptr_t end, uintptr_t cfi_check) {
    101   CHECK((cfi_check & (kCfiCheckAlign - 1)) == 0);
    102 
    103   // Don't fill anything below cfi_check. We can not represent those addresses
    104   // in the shadow, and must make sure at codegen to place all valid call
    105   // targets above cfi_check.
    106   begin = std::max(begin, cfi_check) & ~(kShadowAlign - 1);
    107   uint16_t* shadow_begin = MemToShadow(begin);
    108   uint16_t* shadow_end = MemToShadow(end - 1) + 1;
    109 
    110   ShadowWrite sw(shadow_begin, shadow_end);
    111   uint16_t sv_begin = ((begin + kShadowAlign - cfi_check) >> kCfiCheckGranularity) + kRegularShadowMin;
    112 
    113   // With each step of the loop below, __cfi_check address computation base is increased by
    114   // 2**ShadowGranularity.
    115   // To compensate for that, each next shadow value must be increased by 2**ShadowGranularity /
    116   // 2**CfiCheckGranularity.
    117   uint16_t sv_step = 1 << (kShadowGranularity - kCfiCheckGranularity);
    118   uint16_t sv = sv_begin;
    119   for (uint16_t& s : sw) {
    120     if (sv < sv_begin) {
    121       // If shadow value wraps around, also fall back to unchecked. This means the binary is too
    122       // large. FIXME: consider using a (slow) resolution function instead.
    123       s = kUncheckedShadow;
    124       continue;
    125     }
    126     // If there is something there already, fall back to unchecked. This may happen in rare cases
    127     // with MAP_FIXED libraries. FIXME: consider using a (slow) resolution function instead.
    128     s = (s == kInvalidShadow) ? sv : kUncheckedShadow;
    129     sv += sv_step;
    130   }
    131 }
    132 
    133 static soinfo* find_libdl(soinfo* solist) {
    134   for (soinfo* si = solist; si != nullptr; si = si->next) {
    135     const char* soname = si->get_soname();
    136     if (soname && strcmp(soname, "libdl.so") == 0) {
    137       return si;
    138     }
    139   }
    140   return nullptr;
    141 }
    142 
    143 static uintptr_t soinfo_find_symbol(soinfo* si, const char* s) {
    144   SymbolName name(s);
    145   const ElfW(Sym) * sym;
    146   if (si->find_symbol_by_name(name, nullptr, &sym) && sym) {
    147     return si->resolve_symbol_address(sym);
    148   }
    149   return 0;
    150 }
    151 
    152 uintptr_t soinfo_find_cfi_check(soinfo* si) {
    153   return soinfo_find_symbol(si, "__cfi_check");
    154 }
    155 
    156 uintptr_t CFIShadowWriter::MapShadow() {
    157   void* p =
    158       mmap(nullptr, kShadowSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
    159   CHECK(p != MAP_FAILED);
    160   return reinterpret_cast<uintptr_t>(p);
    161 }
    162 
    163 bool CFIShadowWriter::AddLibrary(soinfo* si) {
    164   CHECK(shadow_start != nullptr);
    165   if (si->base == 0 || si->size == 0) {
    166     return true;
    167   }
    168   uintptr_t cfi_check = soinfo_find_cfi_check(si);
    169   if (cfi_check == 0) {
    170     INFO("[ CFI add 0x%zx + 0x%zx %s ]", static_cast<uintptr_t>(si->base),
    171          static_cast<uintptr_t>(si->size), si->get_soname());
    172     AddUnchecked(si->base, si->base + si->size);
    173     return true;
    174   }
    175 
    176   INFO("[ CFI add 0x%zx + 0x%zx %s: 0x%zx ]", static_cast<uintptr_t>(si->base),
    177        static_cast<uintptr_t>(si->size), si->get_soname(), cfi_check);
    178 #ifdef __arm__
    179   // Require Thumb encoding.
    180   if ((cfi_check & 1UL) != 1UL) {
    181     DL_ERR("__cfi_check in not a Thumb function in the library \"%s\"", si->get_soname());
    182     return false;
    183   }
    184   cfi_check &= ~1UL;
    185 #endif
    186   if ((cfi_check & (kCfiCheckAlign - 1)) != 0) {
    187     DL_ERR("unaligned __cfi_check in the library \"%s\"", si->get_soname());
    188     return false;
    189   }
    190   Add(si->base, si->base + si->size, cfi_check);
    191   return true;
    192 }
    193 
    194 // Pass the shadow mapping address to libdl.so. In return, we get an pointer to the location
    195 // libdl.so uses to store the address.
    196 bool CFIShadowWriter::NotifyLibDl(soinfo* solist, uintptr_t p) {
    197   soinfo* libdl = find_libdl(solist);
    198   if (libdl == nullptr) {
    199     DL_ERR("CFI could not find libdl");
    200     return false;
    201   }
    202 
    203   uintptr_t cfi_init = soinfo_find_symbol(libdl, "__cfi_init");
    204   CHECK(cfi_init != 0);
    205   shadow_start = reinterpret_cast<uintptr_t* (*)(uintptr_t)>(cfi_init)(p);
    206   CHECK(shadow_start != nullptr);
    207   CHECK(*shadow_start == p);
    208   mprotect(shadow_start, PAGE_SIZE, PROT_READ);
    209   return true;
    210 }
    211 
    212 bool CFIShadowWriter::MaybeInit(soinfo* new_si, soinfo* solist) {
    213   CHECK(initial_link_done);
    214   CHECK(shadow_start == nullptr);
    215   // Check if CFI shadow must be initialized at this time.
    216   bool found = false;
    217   if (new_si == nullptr) {
    218     // This is the case when we've just completed the initial link. There may have been earlier
    219     // calls to MaybeInit that were skipped. Look though the entire solist.
    220     for (soinfo* si = solist; si != nullptr; si = si->next) {
    221       if (soinfo_find_cfi_check(si)) {
    222         found = true;
    223         break;
    224       }
    225     }
    226   } else {
    227     // See if the new library uses CFI.
    228     found = soinfo_find_cfi_check(new_si);
    229   }
    230 
    231   // Nothing found.
    232   if (!found) {
    233     return true;
    234   }
    235 
    236   // Init shadow and add all currently loaded libraries (not just the new ones).
    237   if (!NotifyLibDl(solist, MapShadow()))
    238     return false;
    239   for (soinfo* si = solist; si != nullptr; si = si->next) {
    240     if (!AddLibrary(si))
    241       return false;
    242   }
    243   FixupVmaName();
    244   return true;
    245 }
    246 
    247 bool CFIShadowWriter::AfterLoad(soinfo* si, soinfo* solist) {
    248   if (!initial_link_done) {
    249     // Too early.
    250     return true;
    251   }
    252 
    253   if (shadow_start == nullptr) {
    254     return MaybeInit(si, solist);
    255   }
    256 
    257   // Add the new library to the CFI shadow.
    258   if (!AddLibrary(si))
    259     return false;
    260   FixupVmaName();
    261   return true;
    262 }
    263 
    264 void CFIShadowWriter::BeforeUnload(soinfo* si) {
    265   if (shadow_start == nullptr) return;
    266   if (si->base == 0 || si->size == 0) return;
    267   INFO("[ CFI remove 0x%zx + 0x%zx: %s ]", static_cast<uintptr_t>(si->base),
    268        static_cast<uintptr_t>(si->size), si->get_soname());
    269   AddInvalid(si->base, si->base + si->size);
    270   FixupVmaName();
    271 }
    272 
    273 bool CFIShadowWriter::InitialLinkDone(soinfo* solist) {
    274   CHECK(!initial_link_done);
    275   initial_link_done = true;
    276   return MaybeInit(nullptr, solist);
    277 }
    278 
    279 // Find __cfi_check in the caller and let it handle the problem. Since caller_pc is likely not a
    280 // valid CFI target, we can not use CFI shadow for lookup. This does not need to be fast, do the
    281 // regular symbol lookup.
    282 void CFIShadowWriter::CfiFail(uint64_t CallSiteTypeId, void* Ptr, void* DiagData, void* CallerPc) {
    283   soinfo* si = find_containing_library(CallerPc);
    284   if (!si) {
    285     __builtin_trap();
    286   }
    287 
    288   uintptr_t cfi_check = soinfo_find_cfi_check(si);
    289   if (!cfi_check) {
    290     __builtin_trap();
    291   }
    292 
    293   reinterpret_cast<CFICheckFn>(cfi_check)(CallSiteTypeId, Ptr, DiagData);
    294 }
    295