linker_cfi.cpp revision 68ecec1965dcec08c7df3f0224d69604c683cd6c
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "linker_cfi.h" 18 19#include "linker_debug.h" 20#include "linker_globals.h" 21#include "private/bionic_page.h" 22#include "private/bionic_prctl.h" 23 24#include <sys/mman.h> 25#include <sys/types.h> 26#include <cstdint> 27 28// Update shadow without making it writable by preparing the data on the side and mremap-ing it in 29// place. 30class ShadowWrite { 31 char* shadow_start; 32 char* shadow_end; 33 char* aligned_start; 34 char* aligned_end; 35 char* tmp_start; 36 37 public: 38 ShadowWrite(uint16_t* s, uint16_t* e) { 39 shadow_start = reinterpret_cast<char*>(s); 40 shadow_end = reinterpret_cast<char*>(e); 41 aligned_start = reinterpret_cast<char*>(PAGE_START(reinterpret_cast<uintptr_t>(shadow_start))); 42 aligned_end = reinterpret_cast<char*>(PAGE_END(reinterpret_cast<uintptr_t>(shadow_end))); 43 tmp_start = 44 reinterpret_cast<char*>(mmap(nullptr, aligned_end - aligned_start, PROT_READ | PROT_WRITE, 45 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); 46 CHECK(tmp_start != MAP_FAILED); 47 memcpy(tmp_start, aligned_start, shadow_start - aligned_start); 48 memcpy(tmp_start + (shadow_end - aligned_start), shadow_end, aligned_end - shadow_end); 49 } 50 51 uint16_t* begin() { 52 return reinterpret_cast<uint16_t*>(tmp_start + (shadow_start - aligned_start)); 53 } 54 55 uint16_t* end() { 56 return reinterpret_cast<uint16_t*>(tmp_start + (shadow_end - aligned_start)); 57 } 58 59 ~ShadowWrite() { 60 size_t size = aligned_end - aligned_start; 61 mprotect(tmp_start, size, PROT_READ); 62 void* res = mremap(tmp_start, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, 63 reinterpret_cast<void*>(aligned_start)); 64 CHECK(res != MAP_FAILED); 65 } 66}; 67 68void CFIShadowWriter::FixupVmaName() { 69 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, *shadow_start, kShadowSize, "cfi shadow"); 70} 71 72void CFIShadowWriter::AddConstant(uintptr_t begin, uintptr_t end, uint16_t v) { 73 uint16_t* shadow_begin = MemToShadow(begin); 74 uint16_t* shadow_end = MemToShadow(end - 1) + 1; 75 76 ShadowWrite sw(shadow_begin, shadow_end); 77 std::fill(sw.begin(), sw.end(), v); 78} 79 80void CFIShadowWriter::AddUnchecked(uintptr_t begin, uintptr_t end) { 81 AddConstant(begin, end, kUncheckedShadow); 82} 83 84void CFIShadowWriter::AddInvalid(uintptr_t begin, uintptr_t end) { 85 AddConstant(begin, end, kInvalidShadow); 86} 87 88void CFIShadowWriter::Add(uintptr_t begin, uintptr_t end, uintptr_t cfi_check) { 89 CHECK((cfi_check & (kCfiCheckAlign - 1)) == 0); 90 91 // Don't fill anything below cfi_check. We can not represent those addresses 92 // in the shadow, and must make sure at codegen to place all valid call 93 // targets above cfi_check. 94 begin = std::max(begin, cfi_check) & ~(kShadowAlign - 1); 95 uint16_t* shadow_begin = MemToShadow(begin); 96 uint16_t* shadow_end = MemToShadow(end - 1) + 1; 97 98 ShadowWrite sw(shadow_begin, shadow_end); 99 uint16_t sv_begin = ((begin + kShadowAlign - cfi_check) >> kCfiCheckGranularity) + kRegularShadowMin; 100 101 // With each step of the loop below, __cfi_check address computation base is increased by 102 // 2**ShadowGranularity. 103 // To compensate for that, each next shadow value must be increased by 2**ShadowGranularity / 104 // 2**CfiCheckGranularity. 105 uint16_t sv_step = 1 << (kShadowGranularity - kCfiCheckGranularity); 106 uint16_t sv = sv_begin; 107 for (uint16_t& s : sw) { 108 if (sv < sv_begin) { 109 // If shadow value wraps around, also fall back to unchecked. This means the binary is too 110 // large. FIXME: consider using a (slow) resolution function instead. 111 s = kUncheckedShadow; 112 continue; 113 } 114 // If there is something there already, fall back to unchecked. This may happen in rare cases 115 // with MAP_FIXED libraries. FIXME: consider using a (slow) resolution function instead. 116 s = (s == kInvalidShadow) ? sv : kUncheckedShadow; 117 sv += sv_step; 118 } 119} 120 121static soinfo* find_libdl(soinfo* solist) { 122 for (soinfo* si = solist; si != nullptr; si = si->next) { 123 const char* soname = si->get_soname(); 124 if (soname && strcmp(soname, "libdl.so") == 0) { 125 return si; 126 } 127 } 128 return nullptr; 129} 130 131static uintptr_t soinfo_find_symbol(soinfo* si, const char* s) { 132 SymbolName name(s); 133 const ElfW(Sym) * sym; 134 if (si->find_symbol_by_name(name, nullptr, &sym) && sym) { 135 return si->resolve_symbol_address(sym); 136 } 137 return 0; 138} 139 140uintptr_t soinfo_find_cfi_check(soinfo* si) { 141 return soinfo_find_symbol(si, "__cfi_check"); 142} 143 144uintptr_t CFIShadowWriter::MapShadow() { 145 void* p = 146 mmap(nullptr, kShadowSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); 147 CHECK(p != MAP_FAILED); 148 return reinterpret_cast<uintptr_t>(p); 149} 150 151bool CFIShadowWriter::AddLibrary(soinfo* si) { 152 CHECK(shadow_start != nullptr); 153 if (si->base == 0 || si->size == 0) { 154 return true; 155 } 156 uintptr_t cfi_check = soinfo_find_cfi_check(si); 157 if (cfi_check == 0) { 158 INFO("[ CFI add 0x%zx + 0x%zx %s ]", static_cast<uintptr_t>(si->base), 159 static_cast<uintptr_t>(si->size), si->get_soname()); 160 AddUnchecked(si->base, si->base + si->size); 161 return true; 162 } 163 164 INFO("[ CFI add 0x%zx + 0x%zx %s: 0x%zx ]", static_cast<uintptr_t>(si->base), 165 static_cast<uintptr_t>(si->size), si->get_soname(), cfi_check); 166#ifdef __arm__ 167 // Require Thumb encoding. 168 if ((cfi_check & 1UL) != 1UL) { 169 DL_ERR("__cfi_check in not a Thumb function in the library \"%s\"", si->get_soname()); 170 return false; 171 } 172 cfi_check &= ~1UL; 173#endif 174 if ((cfi_check & (kCfiCheckAlign - 1)) != 0) { 175 DL_ERR("unaligned __cfi_check in the library \"%s\"", si->get_soname()); 176 return false; 177 } 178 Add(si->base, si->base + si->size, cfi_check); 179 return true; 180} 181 182// Pass the shadow mapping address to libdl.so. In return, we get an pointer to the location 183// libdl.so uses to store the address. 184bool CFIShadowWriter::NotifyLibDl(soinfo* solist, uintptr_t p) { 185 soinfo* libdl = find_libdl(solist); 186 if (libdl == nullptr) { 187 DL_ERR("CFI could not find libdl"); 188 return false; 189 } 190 191 uintptr_t cfi_init = soinfo_find_symbol(libdl, "__cfi_init"); 192 CHECK(cfi_init != 0); 193 shadow_start = reinterpret_cast<uintptr_t* (*)(uintptr_t)>(cfi_init)(p); 194 CHECK(shadow_start != nullptr); 195 CHECK(*shadow_start == p); 196 mprotect(shadow_start, PAGE_SIZE, PROT_READ); 197 return true; 198} 199 200bool CFIShadowWriter::MaybeInit(soinfo* new_si, soinfo* solist) { 201 CHECK(initial_link_done); 202 CHECK(shadow_start == nullptr); 203 // Check if CFI shadow must be initialized at this time. 204 bool found = false; 205 if (new_si == nullptr) { 206 // This is the case when we've just completed the initial link. There may have been earlier 207 // calls to MaybeInit that were skipped. Look though the entire solist. 208 for (soinfo* si = solist; si != nullptr; si = si->next) { 209 if (soinfo_find_cfi_check(si)) { 210 found = true; 211 break; 212 } 213 } 214 } else { 215 // See if the new library uses CFI. 216 found = soinfo_find_cfi_check(new_si); 217 } 218 219 // Nothing found. 220 if (!found) { 221 return true; 222 } 223 224 // Init shadow and add all currently loaded libraries (not just the new ones). 225 if (!NotifyLibDl(solist, MapShadow())) 226 return false; 227 for (soinfo* si = solist; si != nullptr; si = si->next) { 228 if (!AddLibrary(si)) 229 return false; 230 } 231 FixupVmaName(); 232 return true; 233} 234 235bool CFIShadowWriter::AfterLoad(soinfo* si, soinfo* solist) { 236 if (!initial_link_done) { 237 // Too early. 238 return true; 239 } 240 241 if (shadow_start == nullptr) { 242 return MaybeInit(si, solist); 243 } 244 245 // Add the new library to the CFI shadow. 246 if (!AddLibrary(si)) 247 return false; 248 FixupVmaName(); 249 return true; 250} 251 252void CFIShadowWriter::BeforeUnload(soinfo* si) { 253 if (shadow_start == nullptr) return; 254 if (si->base == 0 || si->size == 0) return; 255 INFO("[ CFI remove 0x%zx + 0x%zx: %s ]", static_cast<uintptr_t>(si->base), 256 static_cast<uintptr_t>(si->size), si->get_soname()); 257 AddInvalid(si->base, si->base + si->size); 258 FixupVmaName(); 259} 260 261bool CFIShadowWriter::InitialLinkDone(soinfo* solist) { 262 CHECK(!initial_link_done); 263 initial_link_done = true; 264 return MaybeInit(nullptr, solist); 265} 266 267// Find __cfi_check in the caller and let it handle the problem. Since caller_pc is likely not a 268// valid CFI target, we can not use CFI shadow for lookup. This does not need to be fast, do the 269// regular symbol lookup. 270void CFIShadowWriter::CfiFail(uint64_t CallSiteTypeId, void* Ptr, void* DiagData, void* CallerPc) { 271 soinfo* si = find_containing_library(CallerPc); 272 if (!si) { 273 __builtin_trap(); 274 } 275 276 uintptr_t cfi_check = soinfo_find_cfi_check(si); 277 if (!cfi_check) { 278 __builtin_trap(); 279 } 280 281 reinterpret_cast<CFICheckFn>(cfi_check)(CallSiteTypeId, Ptr, DiagData); 282} 283