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