xref: /openbsd-src/gnu/llvm/compiler-rt/lib/msan/msan_report.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- msan_report.cpp ---------------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of MemorySanitizer.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Error reporting.
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick 
143cab2bb3Spatrick #include "msan.h"
153cab2bb3Spatrick #include "msan_chained_origin_depot.h"
163cab2bb3Spatrick #include "msan_origin.h"
173cab2bb3Spatrick #include "msan_report.h"
183cab2bb3Spatrick #include "sanitizer_common/sanitizer_allocator_internal.h"
193cab2bb3Spatrick #include "sanitizer_common/sanitizer_common.h"
203cab2bb3Spatrick #include "sanitizer_common/sanitizer_flags.h"
213cab2bb3Spatrick #include "sanitizer_common/sanitizer_mutex.h"
223cab2bb3Spatrick #include "sanitizer_common/sanitizer_report_decorator.h"
233cab2bb3Spatrick #include "sanitizer_common/sanitizer_stackdepot.h"
243cab2bb3Spatrick #include "sanitizer_common/sanitizer_symbolizer.h"
253cab2bb3Spatrick 
263cab2bb3Spatrick using namespace __sanitizer;
273cab2bb3Spatrick 
283cab2bb3Spatrick namespace __msan {
293cab2bb3Spatrick 
303cab2bb3Spatrick class Decorator: public __sanitizer::SanitizerCommonDecorator {
313cab2bb3Spatrick  public:
Decorator()323cab2bb3Spatrick   Decorator() : SanitizerCommonDecorator() { }
Origin() const333cab2bb3Spatrick   const char *Origin() const { return Magenta(); }
Name() const343cab2bb3Spatrick   const char *Name() const { return Green(); }
353cab2bb3Spatrick };
363cab2bb3Spatrick 
DescribeStackOrigin(const char * so,uptr pc)373cab2bb3Spatrick static void DescribeStackOrigin(const char *so, uptr pc) {
383cab2bb3Spatrick   Decorator d;
393cab2bb3Spatrick   Printf("%s", d.Origin());
40*810390e3Srobert   if (so) {
413cab2bb3Spatrick     Printf(
423cab2bb3Spatrick         "  %sUninitialized value was created by an allocation of '%s%s%s'"
43*810390e3Srobert         " in the stack frame%s\n",
44*810390e3Srobert         d.Origin(), d.Name(), so, d.Origin(), d.Default());
45*810390e3Srobert   } else {
46*810390e3Srobert     Printf("  %sUninitialized value was created in the stack frame%s\n",
47*810390e3Srobert            d.Origin(), d.Default());
483cab2bb3Spatrick   }
49*810390e3Srobert 
50*810390e3Srobert   if (pc)
51*810390e3Srobert     StackTrace(&pc, 1).Print();
523cab2bb3Spatrick }
533cab2bb3Spatrick 
DescribeOrigin(u32 id)543cab2bb3Spatrick static void DescribeOrigin(u32 id) {
553cab2bb3Spatrick   VPrintf(1, "  raw origin id: %d\n", id);
563cab2bb3Spatrick   Decorator d;
573cab2bb3Spatrick   Origin o = Origin::FromRawId(id);
583cab2bb3Spatrick   while (o.isChainedOrigin()) {
593cab2bb3Spatrick     StackTrace stack;
603cab2bb3Spatrick     o = o.getNextChainedOrigin(&stack);
613cab2bb3Spatrick     Printf("  %sUninitialized value was stored to memory at%s\n", d.Origin(),
623cab2bb3Spatrick            d.Default());
633cab2bb3Spatrick     stack.Print();
643cab2bb3Spatrick   }
653cab2bb3Spatrick   if (o.isStackOrigin()) {
663cab2bb3Spatrick     uptr pc;
673cab2bb3Spatrick     const char *so = GetStackOriginDescr(o.getStackId(), &pc);
683cab2bb3Spatrick     DescribeStackOrigin(so, pc);
693cab2bb3Spatrick   } else {
703cab2bb3Spatrick     StackTrace stack = o.getStackTraceForHeapOrigin();
713cab2bb3Spatrick     switch (stack.tag) {
723cab2bb3Spatrick       case StackTrace::TAG_ALLOC:
733cab2bb3Spatrick         Printf("  %sUninitialized value was created by a heap allocation%s\n",
743cab2bb3Spatrick                d.Origin(), d.Default());
753cab2bb3Spatrick         break;
763cab2bb3Spatrick       case StackTrace::TAG_DEALLOC:
773cab2bb3Spatrick         Printf("  %sUninitialized value was created by a heap deallocation%s\n",
783cab2bb3Spatrick                d.Origin(), d.Default());
793cab2bb3Spatrick         break;
803cab2bb3Spatrick       case STACK_TRACE_TAG_POISON:
813cab2bb3Spatrick         Printf("  %sMemory was marked as uninitialized%s\n", d.Origin(),
823cab2bb3Spatrick                d.Default());
833cab2bb3Spatrick         break;
84*810390e3Srobert       case STACK_TRACE_TAG_FIELDS:
85*810390e3Srobert         Printf("  %sMember fields were destroyed%s\n", d.Origin(), d.Default());
86*810390e3Srobert         break;
87*810390e3Srobert       case STACK_TRACE_TAG_VPTR:
88*810390e3Srobert         Printf("  %sVirtual table ptr was destroyed%s\n", d.Origin(),
89*810390e3Srobert                d.Default());
90*810390e3Srobert         break;
913cab2bb3Spatrick       default:
923cab2bb3Spatrick         Printf("  %sUninitialized value was created%s\n", d.Origin(),
933cab2bb3Spatrick                d.Default());
943cab2bb3Spatrick         break;
953cab2bb3Spatrick     }
963cab2bb3Spatrick     stack.Print();
973cab2bb3Spatrick   }
983cab2bb3Spatrick }
993cab2bb3Spatrick 
ReportUMR(StackTrace * stack,u32 origin)1003cab2bb3Spatrick void ReportUMR(StackTrace *stack, u32 origin) {
1013cab2bb3Spatrick   if (!__msan::flags()->report_umrs) return;
1023cab2bb3Spatrick 
1033cab2bb3Spatrick   ScopedErrorReportLock l;
1043cab2bb3Spatrick 
1053cab2bb3Spatrick   Decorator d;
1063cab2bb3Spatrick   Printf("%s", d.Warning());
1073cab2bb3Spatrick   Report("WARNING: MemorySanitizer: use-of-uninitialized-value\n");
1083cab2bb3Spatrick   Printf("%s", d.Default());
1093cab2bb3Spatrick   stack->Print();
1103cab2bb3Spatrick   if (origin) {
1113cab2bb3Spatrick     DescribeOrigin(origin);
1123cab2bb3Spatrick   }
1133cab2bb3Spatrick   ReportErrorSummary("use-of-uninitialized-value", stack);
1143cab2bb3Spatrick }
1153cab2bb3Spatrick 
ReportExpectedUMRNotFound(StackTrace * stack)1163cab2bb3Spatrick void ReportExpectedUMRNotFound(StackTrace *stack) {
1173cab2bb3Spatrick   ScopedErrorReportLock l;
1183cab2bb3Spatrick 
1193cab2bb3Spatrick   Printf("WARNING: Expected use of uninitialized value not found\n");
1203cab2bb3Spatrick   stack->Print();
1213cab2bb3Spatrick }
1223cab2bb3Spatrick 
ReportStats()1233cab2bb3Spatrick void ReportStats() {
1243cab2bb3Spatrick   ScopedErrorReportLock l;
1253cab2bb3Spatrick 
1263cab2bb3Spatrick   if (__msan_get_track_origins() > 0) {
127*810390e3Srobert     StackDepotStats stack_depot_stats = StackDepotGetStats();
1283cab2bb3Spatrick     // FIXME: we want this at normal exit, too!
1293cab2bb3Spatrick     // FIXME: but only with verbosity=1 or something
130*810390e3Srobert     Printf("Unique heap origins: %zu\n", stack_depot_stats.n_uniq_ids);
131*810390e3Srobert     Printf("Stack depot allocated bytes: %zu\n", stack_depot_stats.allocated);
1323cab2bb3Spatrick 
133*810390e3Srobert     StackDepotStats chained_origin_depot_stats = ChainedOriginDepotGetStats();
1343cab2bb3Spatrick     Printf("Unique origin histories: %zu\n",
135*810390e3Srobert            chained_origin_depot_stats.n_uniq_ids);
1363cab2bb3Spatrick     Printf("History depot allocated bytes: %zu\n",
137*810390e3Srobert            chained_origin_depot_stats.allocated);
1383cab2bb3Spatrick   }
1393cab2bb3Spatrick }
1403cab2bb3Spatrick 
ReportAtExitStatistics()1413cab2bb3Spatrick void ReportAtExitStatistics() {
1423cab2bb3Spatrick   ScopedErrorReportLock l;
1433cab2bb3Spatrick 
1443cab2bb3Spatrick   if (msan_report_count > 0) {
1453cab2bb3Spatrick     Decorator d;
1463cab2bb3Spatrick     Printf("%s", d.Warning());
1473cab2bb3Spatrick     Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count);
1483cab2bb3Spatrick     Printf("%s", d.Default());
1493cab2bb3Spatrick   }
1503cab2bb3Spatrick }
1513cab2bb3Spatrick 
1523cab2bb3Spatrick class OriginSet {
1533cab2bb3Spatrick  public:
OriginSet()1543cab2bb3Spatrick   OriginSet() : next_id_(0) {}
insert(u32 o)1553cab2bb3Spatrick   int insert(u32 o) {
1563cab2bb3Spatrick     // Scan from the end for better locality.
1573cab2bb3Spatrick     for (int i = next_id_ - 1; i >= 0; --i)
1583cab2bb3Spatrick       if (origins_[i] == o) return i;
1593cab2bb3Spatrick     if (next_id_ == kMaxSize_) return OVERFLOW;
1603cab2bb3Spatrick     int id = next_id_++;
1613cab2bb3Spatrick     origins_[id] = o;
1623cab2bb3Spatrick     return id;
1633cab2bb3Spatrick   }
size()1643cab2bb3Spatrick   int size() { return next_id_; }
get(int id)1653cab2bb3Spatrick   u32 get(int id) { return origins_[id]; }
asChar(int id)1663cab2bb3Spatrick   static char asChar(int id) {
1673cab2bb3Spatrick     switch (id) {
1683cab2bb3Spatrick       case MISSING:
1693cab2bb3Spatrick         return '.';
1703cab2bb3Spatrick       case OVERFLOW:
1713cab2bb3Spatrick         return '*';
1723cab2bb3Spatrick       default:
1733cab2bb3Spatrick         return 'A' + id;
1743cab2bb3Spatrick     }
1753cab2bb3Spatrick   }
1763cab2bb3Spatrick   static const int OVERFLOW = -1;
1773cab2bb3Spatrick   static const int MISSING = -2;
1783cab2bb3Spatrick 
1793cab2bb3Spatrick  private:
1803cab2bb3Spatrick   static const int kMaxSize_ = 'Z' - 'A' + 1;
1813cab2bb3Spatrick   u32 origins_[kMaxSize_];
1823cab2bb3Spatrick   int next_id_;
1833cab2bb3Spatrick };
1843cab2bb3Spatrick 
DescribeMemoryRange(const void * x,uptr size)1853cab2bb3Spatrick void DescribeMemoryRange(const void *x, uptr size) {
1863cab2bb3Spatrick   // Real limits.
1873cab2bb3Spatrick   uptr start = MEM_TO_SHADOW(x);
1883cab2bb3Spatrick   uptr end = start + size;
1893cab2bb3Spatrick   // Scan limits: align start down to 4; align size up to 16.
1903cab2bb3Spatrick   uptr s = start & ~3UL;
1913cab2bb3Spatrick   size = end - s;
1923cab2bb3Spatrick   size = (size + 15) & ~15UL;
1933cab2bb3Spatrick   uptr e = s + size;
1943cab2bb3Spatrick 
1953cab2bb3Spatrick   // Single letter names to origin id mapping.
1963cab2bb3Spatrick   OriginSet origin_set;
1973cab2bb3Spatrick 
1983cab2bb3Spatrick   uptr pos = 0;  // Offset from aligned start.
1993cab2bb3Spatrick   bool with_origins = __msan_get_track_origins();
2003cab2bb3Spatrick   // True if there is at least 1 poisoned bit in the last 4-byte group.
2013cab2bb3Spatrick   bool last_quad_poisoned;
2023cab2bb3Spatrick   int origin_ids[4];  // Single letter origin ids for the current line.
2033cab2bb3Spatrick 
2043cab2bb3Spatrick   Decorator d;
2053cab2bb3Spatrick   Printf("%s", d.Warning());
206*810390e3Srobert   uptr start_x = reinterpret_cast<uptr>(x);
207*810390e3Srobert   Printf("Shadow map [%p, %p) of [%p, %p), %zu bytes:\n",
208*810390e3Srobert          reinterpret_cast<void *>(start), reinterpret_cast<void *>(end),
209*810390e3Srobert          reinterpret_cast<void *>(start_x),
210*810390e3Srobert          reinterpret_cast<void *>(start_x + end - start), end - start);
2113cab2bb3Spatrick   Printf("%s", d.Default());
2123cab2bb3Spatrick   while (s < e) {
2133cab2bb3Spatrick     // Line start.
2143cab2bb3Spatrick     if (pos % 16 == 0) {
2153cab2bb3Spatrick       for (int i = 0; i < 4; ++i) origin_ids[i] = -1;
216*810390e3Srobert       Printf("%p[%p]:", reinterpret_cast<void *>(s),
217*810390e3Srobert              reinterpret_cast<void *>(start_x - start + s));
2183cab2bb3Spatrick     }
2193cab2bb3Spatrick     // Group start.
2203cab2bb3Spatrick     if (pos % 4 == 0) {
2213cab2bb3Spatrick       Printf(" ");
2223cab2bb3Spatrick       last_quad_poisoned = false;
2233cab2bb3Spatrick     }
2243cab2bb3Spatrick     // Print shadow byte.
2253cab2bb3Spatrick     if (s < start || s >= end) {
2263cab2bb3Spatrick       Printf("..");
2273cab2bb3Spatrick     } else {
2283cab2bb3Spatrick       unsigned char v = *(unsigned char *)s;
2293cab2bb3Spatrick       if (v) last_quad_poisoned = true;
2303cab2bb3Spatrick       Printf("%x%x", v >> 4, v & 0xf);
2313cab2bb3Spatrick     }
2323cab2bb3Spatrick     // Group end.
2333cab2bb3Spatrick     if (pos % 4 == 3 && with_origins) {
2343cab2bb3Spatrick       int id = OriginSet::MISSING;
2353cab2bb3Spatrick       if (last_quad_poisoned) {
2363cab2bb3Spatrick         u32 o = *(u32 *)SHADOW_TO_ORIGIN(s - 3);
2373cab2bb3Spatrick         id = origin_set.insert(o);
2383cab2bb3Spatrick       }
2393cab2bb3Spatrick       origin_ids[(pos % 16) / 4] = id;
2403cab2bb3Spatrick     }
2413cab2bb3Spatrick     // Line end.
2423cab2bb3Spatrick     if (pos % 16 == 15) {
2433cab2bb3Spatrick       if (with_origins) {
2443cab2bb3Spatrick         Printf("  |");
2453cab2bb3Spatrick         for (int i = 0; i < 4; ++i) {
2463cab2bb3Spatrick           char c = OriginSet::asChar(origin_ids[i]);
2473cab2bb3Spatrick           Printf("%c", c);
2483cab2bb3Spatrick           if (i != 3) Printf(" ");
2493cab2bb3Spatrick         }
2503cab2bb3Spatrick         Printf("|");
2513cab2bb3Spatrick       }
2523cab2bb3Spatrick       Printf("\n");
2533cab2bb3Spatrick     }
2543cab2bb3Spatrick     size--;
2553cab2bb3Spatrick     s++;
2563cab2bb3Spatrick     pos++;
2573cab2bb3Spatrick   }
2583cab2bb3Spatrick 
2593cab2bb3Spatrick   Printf("\n");
2603cab2bb3Spatrick 
2613cab2bb3Spatrick   for (int i = 0; i < origin_set.size(); ++i) {
2623cab2bb3Spatrick     u32 o = origin_set.get(i);
2633cab2bb3Spatrick     Printf("Origin %c (origin_id %x):\n", OriginSet::asChar(i), o);
2643cab2bb3Spatrick     DescribeOrigin(o);
2653cab2bb3Spatrick   }
2663cab2bb3Spatrick }
2673cab2bb3Spatrick 
ReportUMRInsideAddressRange(const char * what,const void * start,uptr size,uptr offset)2683cab2bb3Spatrick void ReportUMRInsideAddressRange(const char *what, const void *start, uptr size,
2693cab2bb3Spatrick                                  uptr offset) {
2703cab2bb3Spatrick   Decorator d;
2713cab2bb3Spatrick   Printf("%s", d.Warning());
2723cab2bb3Spatrick   Printf("%sUninitialized bytes in %s%s%s at offset %zu inside [%p, %zu)%s\n",
2733cab2bb3Spatrick          d.Warning(), d.Name(), what, d.Warning(), offset, start, size,
2743cab2bb3Spatrick          d.Default());
2753cab2bb3Spatrick   if (__sanitizer::Verbosity())
2763cab2bb3Spatrick     DescribeMemoryRange(start, size);
2773cab2bb3Spatrick }
2783cab2bb3Spatrick 
2793cab2bb3Spatrick }  // namespace __msan
280