1 //===-- asan_descriptions.cpp -----------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file is a part of AddressSanitizer, an address sanity checker. 10 // 11 // ASan functions for getting information about an address and/or printing it. 12 //===----------------------------------------------------------------------===// 13 14 #include "asan_descriptions.h" 15 #include "asan_mapping.h" 16 #include "asan_report.h" 17 #include "asan_stack.h" 18 #include "sanitizer_common/sanitizer_stackdepot.h" 19 20 namespace __asan { 21 22 AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) { 23 if (!t) { 24 internal_snprintf(name, sizeof(name), "T-1"); 25 return; 26 } 27 int len = internal_snprintf(name, sizeof(name), "T%llu", t->unique_id); 28 CHECK(((unsigned int)len) < sizeof(name)); 29 if (internal_strlen(t->name)) 30 internal_snprintf(&name[len], sizeof(name) - len, " (%s)", t->name); 31 } 32 33 AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) 34 : AsanThreadIdAndName( 35 tid == kInvalidTid ? nullptr : GetThreadContextByTidLocked(tid)) { 36 asanThreadRegistry().CheckLocked(); 37 } 38 39 void DescribeThread(AsanThreadContext *context) { 40 CHECK(context); 41 asanThreadRegistry().CheckLocked(); 42 // No need to announce the main thread. 43 if (context->tid == kMainTid || context->announced) { 44 return; 45 } 46 context->announced = true; 47 48 InternalScopedString str; 49 str.AppendF("Thread %s", AsanThreadIdAndName(context).c_str()); 50 51 AsanThreadContext *parent_context = 52 context->parent_tid == kInvalidTid 53 ? nullptr 54 : GetThreadContextByTidLocked(context->parent_tid); 55 56 // `context->parent_tid` may point to reused slot. Check `unique_id` which 57 // is always smaller for the parent, always greater for a new user. 58 if (!parent_context || context->unique_id <= parent_context->unique_id) { 59 str.Append(" created by unknown thread\n"); 60 Printf("%s", str.data()); 61 return; 62 } 63 str.AppendF(" created by %s here:\n", 64 AsanThreadIdAndName(context->parent_tid).c_str()); 65 Printf("%s", str.data()); 66 StackDepotGet(context->stack_id).Print(); 67 // Recursively described parent thread if needed. 68 if (flags()->print_full_thread_history) 69 DescribeThread(parent_context); 70 } 71 72 // Shadow descriptions 73 static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) { 74 CHECK(!AddrIsInMem(addr)); 75 if (AddrIsInShadowGap(addr)) { 76 *shadow_kind = kShadowKindGap; 77 } else if (AddrIsInHighShadow(addr)) { 78 *shadow_kind = kShadowKindHigh; 79 } else if (AddrIsInLowShadow(addr)) { 80 *shadow_kind = kShadowKindLow; 81 } else { 82 return false; 83 } 84 return true; 85 } 86 87 bool DescribeAddressIfShadow(uptr addr) { 88 ShadowAddressDescription descr; 89 if (!GetShadowAddressInformation(addr, &descr)) return false; 90 descr.Print(); 91 return true; 92 } 93 94 bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) { 95 if (AddrIsInMem(addr)) return false; 96 ShadowKind shadow_kind; 97 if (!GetShadowKind(addr, &shadow_kind)) return false; 98 if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr; 99 descr->addr = addr; 100 descr->kind = shadow_kind; 101 return true; 102 } 103 104 // Heap descriptions 105 static void GetAccessToHeapChunkInformation(ChunkAccess *descr, 106 AsanChunkView chunk, uptr addr, 107 uptr access_size) { 108 descr->bad_addr = addr; 109 if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) { 110 descr->access_type = kAccessTypeLeft; 111 } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) { 112 descr->access_type = kAccessTypeRight; 113 if (descr->offset < 0) { 114 descr->bad_addr -= descr->offset; 115 descr->offset = 0; 116 } 117 } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) { 118 descr->access_type = kAccessTypeInside; 119 } else { 120 descr->access_type = kAccessTypeUnknown; 121 } 122 descr->chunk_begin = chunk.Beg(); 123 descr->chunk_size = chunk.UsedSize(); 124 descr->user_requested_alignment = chunk.UserRequestedAlignment(); 125 descr->alloc_type = chunk.GetAllocType(); 126 } 127 128 static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) { 129 Decorator d; 130 InternalScopedString str; 131 str.Append(d.Location()); 132 switch (descr.access_type) { 133 case kAccessTypeLeft: 134 str.AppendF("%p is located %zd bytes before", (void *)descr.bad_addr, 135 descr.offset); 136 break; 137 case kAccessTypeRight: 138 str.AppendF("%p is located %zd bytes after", (void *)descr.bad_addr, 139 descr.offset); 140 break; 141 case kAccessTypeInside: 142 str.AppendF("%p is located %zd bytes inside of", (void *)descr.bad_addr, 143 descr.offset); 144 break; 145 case kAccessTypeUnknown: 146 str.AppendF( 147 "%p is located somewhere around (this is AddressSanitizer bug!)", 148 (void *)descr.bad_addr); 149 } 150 str.AppendF(" %zu-byte region [%p,%p)\n", descr.chunk_size, 151 (void *)descr.chunk_begin, 152 (void *)(descr.chunk_begin + descr.chunk_size)); 153 str.Append(d.Default()); 154 Printf("%s", str.data()); 155 } 156 157 bool GetHeapAddressInformation(uptr addr, uptr access_size, 158 HeapAddressDescription *descr) { 159 AsanChunkView chunk = FindHeapChunkByAddress(addr); 160 if (!chunk.IsValid()) { 161 return false; 162 } 163 descr->addr = addr; 164 GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr, 165 access_size); 166 CHECK_NE(chunk.AllocTid(), kInvalidTid); 167 descr->alloc_tid = chunk.AllocTid(); 168 descr->alloc_stack_id = chunk.GetAllocStackId(); 169 descr->free_tid = chunk.FreeTid(); 170 if (descr->free_tid != kInvalidTid) 171 descr->free_stack_id = chunk.GetFreeStackId(); 172 return true; 173 } 174 175 static StackTrace GetStackTraceFromId(u32 id) { 176 CHECK(id); 177 StackTrace res = StackDepotGet(id); 178 CHECK(res.trace); 179 return res; 180 } 181 182 bool DescribeAddressIfHeap(uptr addr, uptr access_size) { 183 HeapAddressDescription descr; 184 if (!GetHeapAddressInformation(addr, access_size, &descr)) { 185 Printf( 186 "AddressSanitizer can not describe address in more detail " 187 "(wild memory access suspected).\n"); 188 return false; 189 } 190 descr.Print(); 191 return true; 192 } 193 194 // Stack descriptions 195 bool GetStackAddressInformation(uptr addr, uptr access_size, 196 StackAddressDescription *descr) { 197 AsanThread *t = FindThreadByStackAddress(addr); 198 if (!t) return false; 199 200 descr->addr = addr; 201 descr->tid = t->tid(); 202 // Try to fetch precise stack frame for this access. 203 AsanThread::StackFrameAccess access; 204 if (!t->GetStackFrameAccessByAddr(addr, &access)) { 205 descr->frame_descr = nullptr; 206 return true; 207 } 208 209 descr->offset = access.offset; 210 descr->access_size = access_size; 211 descr->frame_pc = access.frame_pc; 212 descr->frame_descr = access.frame_descr; 213 214 #if SANITIZER_PPC64V1 215 // On PowerPC64 ELFv1, the address of a function actually points to a 216 // three-doubleword data structure with the first field containing 217 // the address of the function's code. 218 descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc); 219 #endif 220 descr->frame_pc += 16; 221 222 return true; 223 } 224 225 static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, 226 uptr access_size, uptr prev_var_end, 227 uptr next_var_beg) { 228 uptr var_end = var.beg + var.size; 229 uptr addr_end = addr + access_size; 230 const char *pos_descr = nullptr; 231 // If the variable [var.beg, var_end) is the nearest variable to the 232 // current memory access, indicate it in the log. 233 if (addr >= var.beg) { 234 if (addr_end <= var_end) 235 pos_descr = "is inside"; // May happen if this is a use-after-return. 236 else if (addr < var_end) 237 pos_descr = "partially overflows"; 238 else if (addr_end <= next_var_beg && 239 next_var_beg - addr_end >= addr - var_end) 240 pos_descr = "overflows"; 241 } else { 242 if (addr_end > var.beg) 243 pos_descr = "partially underflows"; 244 else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end) 245 pos_descr = "underflows"; 246 } 247 InternalScopedString str; 248 str.AppendF(" [%zd, %zd)", var.beg, var_end); 249 // Render variable name. 250 str.Append(" '"); 251 for (uptr i = 0; i < var.name_len; ++i) { 252 str.AppendF("%c", var.name_pos[i]); 253 } 254 str.Append("'"); 255 if (var.line > 0) { 256 str.AppendF(" (line %zd)", var.line); 257 } 258 if (pos_descr) { 259 Decorator d; 260 // FIXME: we may want to also print the size of the access here, 261 // but in case of accesses generated by memset it may be confusing. 262 str.AppendF("%s <== Memory access at offset %zd %s this variable%s\n", 263 d.Location(), addr, pos_descr, d.Default()); 264 } else { 265 str.Append("\n"); 266 } 267 Printf("%s", str.data()); 268 } 269 270 bool DescribeAddressIfStack(uptr addr, uptr access_size) { 271 StackAddressDescription descr; 272 if (!GetStackAddressInformation(addr, access_size, &descr)) return false; 273 descr.Print(); 274 return true; 275 } 276 277 // Global descriptions 278 static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, 279 const __asan_global &g) { 280 InternalScopedString str; 281 Decorator d; 282 str.Append(d.Location()); 283 if (addr < g.beg) { 284 str.AppendF("%p is located %zd bytes before", (void *)addr, g.beg - addr); 285 } else if (addr + access_size > g.beg + g.size) { 286 if (addr < g.beg + g.size) addr = g.beg + g.size; 287 str.AppendF("%p is located %zd bytes after", (void *)addr, 288 addr - (g.beg + g.size)); 289 } else { 290 // Can it happen? 291 str.AppendF("%p is located %zd bytes inside of", (void *)addr, 292 addr - g.beg); 293 } 294 str.AppendF(" global variable '%s' defined in '", 295 MaybeDemangleGlobalName(g.name)); 296 PrintGlobalLocation(&str, g, /*print_module_name=*/false); 297 str.AppendF("' (%p) of size %zu\n", (void *)g.beg, g.size); 298 str.Append(d.Default()); 299 PrintGlobalNameIfASCII(&str, g); 300 Printf("%s", str.data()); 301 } 302 303 bool GetGlobalAddressInformation(uptr addr, uptr access_size, 304 GlobalAddressDescription *descr) { 305 descr->addr = addr; 306 int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites, 307 ARRAY_SIZE(descr->globals)); 308 descr->size = globals_num; 309 descr->access_size = access_size; 310 return globals_num != 0; 311 } 312 313 bool DescribeAddressIfGlobal(uptr addr, uptr access_size, 314 const char *bug_type) { 315 GlobalAddressDescription descr; 316 if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false; 317 318 descr.Print(bug_type); 319 return true; 320 } 321 322 void ShadowAddressDescription::Print() const { 323 Printf("Address %p is located in the %s area.\n", (void *)addr, 324 ShadowNames[kind]); 325 } 326 327 void GlobalAddressDescription::Print(const char *bug_type) const { 328 for (int i = 0; i < size; i++) { 329 DescribeAddressRelativeToGlobal(addr, access_size, globals[i]); 330 if (bug_type && 331 0 == internal_strcmp(bug_type, "initialization-order-fiasco") && 332 reg_sites[i]) { 333 Printf(" registered at:\n"); 334 StackDepotGet(reg_sites[i]).Print(); 335 } 336 } 337 } 338 339 bool GlobalAddressDescription::PointsInsideTheSameVariable( 340 const GlobalAddressDescription &other) const { 341 if (size == 0 || other.size == 0) return false; 342 343 for (uptr i = 0; i < size; i++) { 344 const __asan_global &a = globals[i]; 345 for (uptr j = 0; j < other.size; j++) { 346 const __asan_global &b = other.globals[j]; 347 if (a.beg == b.beg && 348 a.beg <= addr && 349 b.beg <= other.addr && 350 (addr + access_size) < (a.beg + a.size) && 351 (other.addr + other.access_size) < (b.beg + b.size)) 352 return true; 353 } 354 } 355 356 return false; 357 } 358 359 void StackAddressDescription::Print() const { 360 Decorator d; 361 Printf("%s", d.Location()); 362 Printf("Address %p is located in stack of thread %s", (void *)addr, 363 AsanThreadIdAndName(tid).c_str()); 364 365 if (!frame_descr) { 366 Printf("%s\n", d.Default()); 367 return; 368 } 369 Printf(" at offset %zu in frame%s\n", offset, d.Default()); 370 371 // Now we print the frame where the alloca has happened. 372 // We print this frame as a stack trace with one element. 373 // The symbolizer may print more than one frame if inlining was involved. 374 // The frame numbers may be different than those in the stack trace printed 375 // previously. That's unfortunate, but I have no better solution, 376 // especially given that the alloca may be from entirely different place 377 // (e.g. use-after-scope, or different thread's stack). 378 Printf("%s", d.Default()); 379 StackTrace alloca_stack(&frame_pc, 1); 380 alloca_stack.Print(); 381 382 InternalMmapVector<StackVarDescr> vars; 383 vars.reserve(16); 384 if (!ParseFrameDescription(frame_descr, &vars)) { 385 Printf( 386 "AddressSanitizer can't parse the stack frame " 387 "descriptor: |%s|\n", 388 frame_descr); 389 // 'addr' is a stack address, so return true even if we can't parse frame 390 return; 391 } 392 uptr n_objects = vars.size(); 393 // Report the number of stack objects. 394 Printf(" This frame has %zu object(s):\n", n_objects); 395 396 // Report all objects in this frame. 397 for (uptr i = 0; i < n_objects; i++) { 398 uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; 399 uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); 400 PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end, 401 next_var_beg); 402 } 403 Printf( 404 "HINT: this may be a false positive if your program uses " 405 "some custom stack unwind mechanism, swapcontext or vfork\n"); 406 if (SANITIZER_WINDOWS) 407 Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n"); 408 else 409 Printf(" (longjmp and C++ exceptions *are* supported)\n"); 410 411 DescribeThread(GetThreadContextByTidLocked(tid)); 412 } 413 414 void HeapAddressDescription::Print() const { 415 PrintHeapChunkAccess(addr, chunk_access); 416 417 asanThreadRegistry().CheckLocked(); 418 AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid); 419 StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id); 420 421 Decorator d; 422 AsanThreadContext *free_thread = nullptr; 423 if (free_tid != kInvalidTid) { 424 free_thread = GetThreadContextByTidLocked(free_tid); 425 Printf("%sfreed by thread %s here:%s\n", d.Allocation(), 426 AsanThreadIdAndName(free_thread).c_str(), d.Default()); 427 StackTrace free_stack = GetStackTraceFromId(free_stack_id); 428 free_stack.Print(); 429 Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(), 430 AsanThreadIdAndName(alloc_thread).c_str(), d.Default()); 431 } else { 432 Printf("%sallocated by thread %s here:%s\n", d.Allocation(), 433 AsanThreadIdAndName(alloc_thread).c_str(), d.Default()); 434 } 435 alloc_stack.Print(); 436 DescribeThread(GetCurrentThread()); 437 if (free_thread) DescribeThread(free_thread); 438 DescribeThread(alloc_thread); 439 } 440 441 AddressDescription::AddressDescription(uptr addr, uptr access_size, 442 bool shouldLockThreadRegistry) { 443 if (GetShadowAddressInformation(addr, &data.shadow)) { 444 data.kind = kAddressKindShadow; 445 return; 446 } 447 if (GetHeapAddressInformation(addr, access_size, &data.heap)) { 448 data.kind = kAddressKindHeap; 449 return; 450 } 451 452 bool isStackMemory = false; 453 if (shouldLockThreadRegistry) { 454 ThreadRegistryLock l(&asanThreadRegistry()); 455 isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack); 456 } else { 457 isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack); 458 } 459 if (isStackMemory) { 460 data.kind = kAddressKindStack; 461 return; 462 } 463 464 if (GetGlobalAddressInformation(addr, access_size, &data.global)) { 465 data.kind = kAddressKindGlobal; 466 return; 467 } 468 data.kind = kAddressKindWild; 469 data.wild.addr = addr; 470 data.wild.access_size = access_size; 471 } 472 473 void WildAddressDescription::Print() const { 474 Printf("Address %p is a wild pointer inside of access range of size %p.\n", 475 (void *)addr, (void *)access_size); 476 } 477 478 void PrintAddressDescription(uptr addr, uptr access_size, 479 const char *bug_type) { 480 ShadowAddressDescription shadow_descr; 481 if (GetShadowAddressInformation(addr, &shadow_descr)) { 482 shadow_descr.Print(); 483 return; 484 } 485 486 GlobalAddressDescription global_descr; 487 if (GetGlobalAddressInformation(addr, access_size, &global_descr)) { 488 global_descr.Print(bug_type); 489 return; 490 } 491 492 StackAddressDescription stack_descr; 493 if (GetStackAddressInformation(addr, access_size, &stack_descr)) { 494 stack_descr.Print(); 495 return; 496 } 497 498 HeapAddressDescription heap_descr; 499 if (GetHeapAddressInformation(addr, access_size, &heap_descr)) { 500 heap_descr.Print(); 501 return; 502 } 503 504 // We exhausted our possibilities. Bail out. 505 Printf( 506 "AddressSanitizer can not describe address in more detail " 507 "(wild memory access suspected).\n"); 508 } 509 } // namespace __asan 510