1//===-- asan_descriptions.cc ------------------------------------*- C++ -*-===//
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 is a part of AddressSanitizer, an address sanity checker.
11//
12// ASan functions for getting information about an address and/or printing it.
13//===----------------------------------------------------------------------===//
14
15#include "asan_descriptions.h"
16#include "asan_mapping.h"
17#include "asan_report.h"
18#include "asan_stack.h"
19#include "sanitizer_common/sanitizer_stackdepot.h"
20
21namespace __asan {
22
23AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) {
24  Init(t->tid, t->name);
25}
26
27AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) {
28  if (tid == kInvalidTid) {
29    Init(tid, "");
30  } else {
31    asanThreadRegistry().CheckLocked();
32    AsanThreadContext *t = GetThreadContextByTidLocked(tid);
33    Init(tid, t->name);
34  }
35}
36
37void AsanThreadIdAndName::Init(u32 tid, const char *tname) {
38  int len = internal_snprintf(name, sizeof(name), "T%d", tid);
39  CHECK(((unsigned int)len) < sizeof(name));
40  if (tname[0] != '\0')
41    internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname);
42}
43
44void DescribeThread(AsanThreadContext *context) {
45  CHECK(context);
46  asanThreadRegistry().CheckLocked();
47  // No need to announce the main thread.
48  if (context->tid == 0 || context->announced) {
49    return;
50  }
51  context->announced = true;
52  InternalScopedString str(1024);
53  str.append("Thread %s", AsanThreadIdAndName(context).c_str());
54  if (context->parent_tid == kInvalidTid) {
55    str.append(" created by unknown thread\n");
56    Printf("%s", str.data());
57    return;
58  }
59  str.append(" created by %s here:\n",
60             AsanThreadIdAndName(context->parent_tid).c_str());
61  Printf("%s", str.data());
62  StackDepotGet(context->stack_id).Print();
63  // Recursively described parent thread if needed.
64  if (flags()->print_full_thread_history) {
65    AsanThreadContext *parent_context =
66        GetThreadContextByTidLocked(context->parent_tid);
67    DescribeThread(parent_context);
68  }
69}
70
71// Shadow descriptions
72static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) {
73  CHECK(!AddrIsInMem(addr));
74  if (AddrIsInShadowGap(addr)) {
75    *shadow_kind = kShadowKindGap;
76  } else if (AddrIsInHighShadow(addr)) {
77    *shadow_kind = kShadowKindHigh;
78  } else if (AddrIsInLowShadow(addr)) {
79    *shadow_kind = kShadowKindLow;
80  } else {
81    CHECK(0 && "Address is not in memory and not in shadow?");
82    return false;
83  }
84  return true;
85}
86
87bool DescribeAddressIfShadow(uptr addr) {
88  ShadowAddressDescription descr;
89  if (!GetShadowAddressInformation(addr, &descr)) return false;
90  descr.Print();
91  return true;
92}
93
94bool 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
105static 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
128static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {
129  Decorator d;
130  InternalScopedString str(4096);
131  str.append("%s", d.Location());
132  switch (descr.access_type) {
133    case kAccessTypeLeft:
134      str.append("%p is located %zd bytes to the left of",
135                 (void *)descr.bad_addr, descr.offset);
136      break;
137    case kAccessTypeRight:
138      str.append("%p is located %zd bytes to the right of",
139                 (void *)descr.bad_addr, descr.offset);
140      break;
141    case kAccessTypeInside:
142      str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr,
143                 descr.offset);
144      break;
145    case kAccessTypeUnknown:
146      str.append(
147          "%p is located somewhere around (this is AddressSanitizer bug!)",
148          (void *)descr.bad_addr);
149  }
150  str.append(" %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("%s", d.Default());
154  Printf("%s", str.data());
155}
156
157bool 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
175static StackTrace GetStackTraceFromId(u32 id) {
176  CHECK(id);
177  StackTrace res = StackDepotGet(id);
178  CHECK(res.trace);
179  return res;
180}
181
182bool 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
195bool 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
225static 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(1024);
248  str.append("    [%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.append("%c", var.name_pos[i]);
253  }
254  str.append("'");
255  if (var.line > 0) {
256    str.append(" (line %d)", 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.append("%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
270bool 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
278static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
279                                            const __asan_global &g) {
280  InternalScopedString str(4096);
281  Decorator d;
282  str.append("%s", d.Location());
283  if (addr < g.beg) {
284    str.append("%p is located %zd bytes to the left", (void *)addr,
285               g.beg - addr);
286  } else if (addr + access_size > g.beg + g.size) {
287    if (addr < g.beg + g.size) addr = g.beg + g.size;
288    str.append("%p is located %zd bytes to the right", (void *)addr,
289               addr - (g.beg + g.size));
290  } else {
291    // Can it happen?
292    str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
293  }
294  str.append(" of global variable '%s' defined in '",
295             MaybeDemangleGlobalName(g.name));
296  PrintGlobalLocation(&str, g);
297  str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
298  str.append("%s", d.Default());
299  PrintGlobalNameIfASCII(&str, g);
300  Printf("%s", str.data());
301}
302
303bool 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
313bool 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
322void ShadowAddressDescription::Print() const {
323  Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]);
324}
325
326void GlobalAddressDescription::Print(const char *bug_type) const {
327  for (int i = 0; i < size; i++) {
328    DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);
329    if (bug_type &&
330        0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
331        reg_sites[i]) {
332      Printf("  registered at:\n");
333      StackDepotGet(reg_sites[i]).Print();
334    }
335  }
336}
337
338bool GlobalAddressDescription::PointsInsideTheSameVariable(
339    const GlobalAddressDescription &other) const {
340  if (size == 0 || other.size == 0) return false;
341
342  for (uptr i = 0; i < size; i++) {
343    const __asan_global &a = globals[i];
344    for (uptr j = 0; j < other.size; j++) {
345      const __asan_global &b = other.globals[j];
346      if (a.beg == b.beg &&
347          a.beg <= addr &&
348          b.beg <= other.addr &&
349          (addr + access_size) < (a.beg + a.size) &&
350          (other.addr + other.access_size) < (b.beg + b.size))
351        return true;
352    }
353  }
354
355  return false;
356}
357
358void StackAddressDescription::Print() const {
359  Decorator d;
360  Printf("%s", d.Location());
361  Printf("Address %p is located in stack of thread %s", addr,
362         AsanThreadIdAndName(tid).c_str());
363
364  if (!frame_descr) {
365    Printf("%s\n", d.Default());
366    return;
367  }
368  Printf(" at offset %zu in frame%s\n", offset, d.Default());
369
370  // Now we print the frame where the alloca has happened.
371  // We print this frame as a stack trace with one element.
372  // The symbolizer may print more than one frame if inlining was involved.
373  // The frame numbers may be different than those in the stack trace printed
374  // previously. That's unfortunate, but I have no better solution,
375  // especially given that the alloca may be from entirely different place
376  // (e.g. use-after-scope, or different thread's stack).
377  Printf("%s", d.Default());
378  StackTrace alloca_stack(&frame_pc, 1);
379  alloca_stack.Print();
380
381  InternalMmapVector<StackVarDescr> vars;
382  vars.reserve(16);
383  if (!ParseFrameDescription(frame_descr, &vars)) {
384    Printf(
385        "AddressSanitizer can't parse the stack frame "
386        "descriptor: |%s|\n",
387        frame_descr);
388    // 'addr' is a stack address, so return true even if we can't parse frame
389    return;
390  }
391  uptr n_objects = vars.size();
392  // Report the number of stack objects.
393  Printf("  This frame has %zu object(s):\n", n_objects);
394
395  // Report all objects in this frame.
396  for (uptr i = 0; i < n_objects; i++) {
397    uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
398    uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
399    PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,
400                                  next_var_beg);
401  }
402  Printf(
403      "HINT: this may be a false positive if your program uses "
404      "some custom stack unwind mechanism, swapcontext or vfork\n");
405  if (SANITIZER_WINDOWS)
406    Printf("      (longjmp, SEH and C++ exceptions *are* supported)\n");
407  else
408    Printf("      (longjmp and C++ exceptions *are* supported)\n");
409
410  DescribeThread(GetThreadContextByTidLocked(tid));
411}
412
413void HeapAddressDescription::Print() const {
414  PrintHeapChunkAccess(addr, chunk_access);
415
416  asanThreadRegistry().CheckLocked();
417  AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
418  StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);
419
420  Decorator d;
421  AsanThreadContext *free_thread = nullptr;
422  if (free_tid != kInvalidTid) {
423    free_thread = GetThreadContextByTidLocked(free_tid);
424    Printf("%sfreed by thread %s here:%s\n", d.Allocation(),
425           AsanThreadIdAndName(free_thread).c_str(), d.Default());
426    StackTrace free_stack = GetStackTraceFromId(free_stack_id);
427    free_stack.Print();
428    Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(),
429           AsanThreadIdAndName(alloc_thread).c_str(), d.Default());
430  } else {
431    Printf("%sallocated by thread %s here:%s\n", d.Allocation(),
432           AsanThreadIdAndName(alloc_thread).c_str(), d.Default());
433  }
434  alloc_stack.Print();
435  DescribeThread(GetCurrentThread());
436  if (free_thread) DescribeThread(free_thread);
437  DescribeThread(alloc_thread);
438}
439
440AddressDescription::AddressDescription(uptr addr, uptr access_size,
441                                       bool shouldLockThreadRegistry) {
442  if (GetShadowAddressInformation(addr, &data.shadow)) {
443    data.kind = kAddressKindShadow;
444    return;
445  }
446  if (GetHeapAddressInformation(addr, access_size, &data.heap)) {
447    data.kind = kAddressKindHeap;
448    return;
449  }
450
451  bool isStackMemory = false;
452  if (shouldLockThreadRegistry) {
453    ThreadRegistryLock l(&asanThreadRegistry());
454    isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
455  } else {
456    isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
457  }
458  if (isStackMemory) {
459    data.kind = kAddressKindStack;
460    return;
461  }
462
463  if (GetGlobalAddressInformation(addr, access_size, &data.global)) {
464    data.kind = kAddressKindGlobal;
465    return;
466  }
467  data.kind = kAddressKindWild;
468  addr = 0;
469}
470
471void PrintAddressDescription(uptr addr, uptr access_size,
472                             const char *bug_type) {
473  ShadowAddressDescription shadow_descr;
474  if (GetShadowAddressInformation(addr, &shadow_descr)) {
475    shadow_descr.Print();
476    return;
477  }
478
479  GlobalAddressDescription global_descr;
480  if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {
481    global_descr.Print(bug_type);
482    return;
483  }
484
485  StackAddressDescription stack_descr;
486  if (GetStackAddressInformation(addr, access_size, &stack_descr)) {
487    stack_descr.Print();
488    return;
489  }
490
491  HeapAddressDescription heap_descr;
492  if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {
493    heap_descr.Print();
494    return;
495  }
496
497  // We exhausted our possibilities. Bail out.
498  Printf(
499      "AddressSanitizer can not describe address in more detail "
500      "(wild memory access suspected).\n");
501}
502}  // namespace __asan
503