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