| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 1 | /* | 
| Dimitry Ivanov | bcc4da9 | 2017-02-15 15:31:13 -0800 | [diff] [blame] | 2 |  * Copyright (C) 2008 The Android Open Source Project | 
 | 3 |  * All rights reserved. | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 4 |  * | 
| Dimitry Ivanov | bcc4da9 | 2017-02-15 15:31:13 -0800 | [diff] [blame] | 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. | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 14 |  * | 
| Dimitry Ivanov | bcc4da9 | 2017-02-15 15:31:13 -0800 | [diff] [blame] | 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. | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 27 |  */ | 
 | 28 |  | 
 | 29 | #include "linker_cfi.h" | 
 | 30 |  | 
 | 31 | #include "linker_debug.h" | 
 | 32 | #include "linker_globals.h" | 
 | 33 | #include "private/bionic_page.h" | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 34 |  | 
 | 35 | #include <sys/mman.h> | 
| Elliott Hughes | 99d5465 | 2018-08-22 10:36:23 -0700 | [diff] [blame] | 36 | #include <sys/prctl.h> | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 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); | 
| Evgenii Stepanov | 636a2ec | 2017-01-20 13:47:04 -0800 | [diff] [blame] | 111 |   uint16_t sv_begin = ((begin + kShadowAlign - cfi_check) >> kCfiCheckGranularity) + kRegularShadowMin; | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 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); | 
| Evgenii Stepanov | 636a2ec | 2017-01-20 13:47:04 -0800 | [diff] [blame] | 118 |   uint16_t sv = sv_begin; | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 119 |   for (uint16_t& s : sw) { | 
| Evgenii Stepanov | 636a2ec | 2017-01-20 13:47:04 -0800 | [diff] [blame] | 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 |     } | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 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); | 
| Evgenii Stepanov | 68ecec1 | 2017-01-31 13:19:30 -0800 | [diff] [blame] | 208 |   mprotect(shadow_start, PAGE_SIZE, PROT_READ); | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 209 |   return true; | 
 | 210 | } | 
 | 211 |  | 
 | 212 | bool CFIShadowWriter::MaybeInit(soinfo* new_si, soinfo* solist) { | 
 | 213 |   CHECK(initial_link_done); | 
| Evgenii Stepanov | 636a2ec | 2017-01-20 13:47:04 -0800 | [diff] [blame] | 214 |   CHECK(shadow_start == nullptr); | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 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) { | 
| Evgenii Stepanov | 636a2ec | 2017-01-20 13:47:04 -0800 | [diff] [blame] | 274 |   CHECK(!initial_link_done); | 
| Evgenii Stepanov | 0a3637d | 2016-07-06 13:20:59 -0700 | [diff] [blame] | 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 | } |