168d75effSDimitry Andric //===-- msan_report.cpp ---------------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of MemorySanitizer.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric // Error reporting.
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric 
1406c3fb27SDimitry Andric #include "msan_report.h"
1506c3fb27SDimitry Andric 
1668d75effSDimitry Andric #include "msan.h"
1768d75effSDimitry Andric #include "msan_chained_origin_depot.h"
1868d75effSDimitry Andric #include "msan_origin.h"
1968d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_internal.h"
2068d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
2168d75effSDimitry Andric #include "sanitizer_common/sanitizer_flags.h"
2268d75effSDimitry Andric #include "sanitizer_common/sanitizer_mutex.h"
2368d75effSDimitry Andric #include "sanitizer_common/sanitizer_report_decorator.h"
2468d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
2506c3fb27SDimitry Andric #include "sanitizer_common/sanitizer_stacktrace_printer.h"
2668d75effSDimitry Andric #include "sanitizer_common/sanitizer_symbolizer.h"
2768d75effSDimitry Andric 
2868d75effSDimitry Andric using namespace __sanitizer;
2968d75effSDimitry Andric 
3068d75effSDimitry Andric namespace __msan {
3168d75effSDimitry Andric 
3268d75effSDimitry Andric class Decorator: public __sanitizer::SanitizerCommonDecorator {
3368d75effSDimitry Andric  public:
Decorator()3468d75effSDimitry Andric   Decorator() : SanitizerCommonDecorator() { }
Origin() const3568d75effSDimitry Andric   const char *Origin() const { return Magenta(); }
Name() const3668d75effSDimitry Andric   const char *Name() const { return Green(); }
3768d75effSDimitry Andric };
3868d75effSDimitry Andric 
DescribeStackOrigin(const char * so,uptr pc)3968d75effSDimitry Andric static void DescribeStackOrigin(const char *so, uptr pc) {
4068d75effSDimitry Andric   Decorator d;
4168d75effSDimitry Andric   Printf("%s", d.Origin());
42bdd1243dSDimitry Andric   if (so) {
4368d75effSDimitry Andric     Printf(
4468d75effSDimitry Andric         "  %sUninitialized value was created by an allocation of '%s%s%s'"
45bdd1243dSDimitry Andric         " in the stack frame%s\n",
46bdd1243dSDimitry Andric         d.Origin(), d.Name(), so, d.Origin(), d.Default());
47bdd1243dSDimitry Andric   } else {
48bdd1243dSDimitry Andric     Printf("  %sUninitialized value was created in the stack frame%s\n",
49bdd1243dSDimitry Andric            d.Origin(), d.Default());
5068d75effSDimitry Andric   }
51bdd1243dSDimitry Andric 
52bdd1243dSDimitry Andric   if (pc)
53bdd1243dSDimitry Andric     StackTrace(&pc, 1).Print();
5468d75effSDimitry Andric }
5568d75effSDimitry Andric 
DescribeOrigin(u32 id)5668d75effSDimitry Andric static void DescribeOrigin(u32 id) {
5768d75effSDimitry Andric   VPrintf(1, "  raw origin id: %d\n", id);
5868d75effSDimitry Andric   Decorator d;
5968d75effSDimitry Andric   Origin o = Origin::FromRawId(id);
6068d75effSDimitry Andric   while (o.isChainedOrigin()) {
6168d75effSDimitry Andric     StackTrace stack;
6268d75effSDimitry Andric     o = o.getNextChainedOrigin(&stack);
6368d75effSDimitry Andric     Printf("  %sUninitialized value was stored to memory at%s\n", d.Origin(),
6468d75effSDimitry Andric            d.Default());
6568d75effSDimitry Andric     stack.Print();
6668d75effSDimitry Andric   }
6768d75effSDimitry Andric   if (o.isStackOrigin()) {
6868d75effSDimitry Andric     uptr pc;
6968d75effSDimitry Andric     const char *so = GetStackOriginDescr(o.getStackId(), &pc);
7068d75effSDimitry Andric     DescribeStackOrigin(so, pc);
7168d75effSDimitry Andric   } else {
7268d75effSDimitry Andric     StackTrace stack = o.getStackTraceForHeapOrigin();
7368d75effSDimitry Andric     switch (stack.tag) {
7468d75effSDimitry Andric       case StackTrace::TAG_ALLOC:
7568d75effSDimitry Andric         Printf("  %sUninitialized value was created by a heap allocation%s\n",
7668d75effSDimitry Andric                d.Origin(), d.Default());
7768d75effSDimitry Andric         break;
7868d75effSDimitry Andric       case StackTrace::TAG_DEALLOC:
7968d75effSDimitry Andric         Printf("  %sUninitialized value was created by a heap deallocation%s\n",
8068d75effSDimitry Andric                d.Origin(), d.Default());
8168d75effSDimitry Andric         break;
8268d75effSDimitry Andric       case STACK_TRACE_TAG_POISON:
8368d75effSDimitry Andric         Printf("  %sMemory was marked as uninitialized%s\n", d.Origin(),
8468d75effSDimitry Andric                d.Default());
8568d75effSDimitry Andric         break;
86bdd1243dSDimitry Andric       case STACK_TRACE_TAG_FIELDS:
87bdd1243dSDimitry Andric         Printf("  %sMember fields were destroyed%s\n", d.Origin(), d.Default());
88bdd1243dSDimitry Andric         break;
89bdd1243dSDimitry Andric       case STACK_TRACE_TAG_VPTR:
90bdd1243dSDimitry Andric         Printf("  %sVirtual table ptr was destroyed%s\n", d.Origin(),
91bdd1243dSDimitry Andric                d.Default());
92bdd1243dSDimitry Andric         break;
9368d75effSDimitry Andric       default:
9468d75effSDimitry Andric         Printf("  %sUninitialized value was created%s\n", d.Origin(),
9568d75effSDimitry Andric                d.Default());
9668d75effSDimitry Andric         break;
9768d75effSDimitry Andric     }
9868d75effSDimitry Andric     stack.Print();
9968d75effSDimitry Andric   }
10068d75effSDimitry Andric }
10168d75effSDimitry Andric 
ReportUMR(StackTrace * stack,u32 origin)10268d75effSDimitry Andric void ReportUMR(StackTrace *stack, u32 origin) {
10368d75effSDimitry Andric   if (!__msan::flags()->report_umrs) return;
10468d75effSDimitry Andric 
10568d75effSDimitry Andric   ScopedErrorReportLock l;
10668d75effSDimitry Andric 
10768d75effSDimitry Andric   Decorator d;
10868d75effSDimitry Andric   Printf("%s", d.Warning());
10968d75effSDimitry Andric   Report("WARNING: MemorySanitizer: use-of-uninitialized-value\n");
11068d75effSDimitry Andric   Printf("%s", d.Default());
11168d75effSDimitry Andric   stack->Print();
11268d75effSDimitry Andric   if (origin) {
11368d75effSDimitry Andric     DescribeOrigin(origin);
11468d75effSDimitry Andric   }
11568d75effSDimitry Andric   ReportErrorSummary("use-of-uninitialized-value", stack);
11668d75effSDimitry Andric }
11768d75effSDimitry Andric 
ReportExpectedUMRNotFound(StackTrace * stack)11868d75effSDimitry Andric void ReportExpectedUMRNotFound(StackTrace *stack) {
11968d75effSDimitry Andric   ScopedErrorReportLock l;
12068d75effSDimitry Andric 
12168d75effSDimitry Andric   Printf("WARNING: Expected use of uninitialized value not found\n");
12268d75effSDimitry Andric   stack->Print();
12368d75effSDimitry Andric }
12468d75effSDimitry Andric 
ReportStats()12568d75effSDimitry Andric void ReportStats() {
12668d75effSDimitry Andric   ScopedErrorReportLock l;
12768d75effSDimitry Andric 
12868d75effSDimitry Andric   if (__msan_get_track_origins() > 0) {
129349cc55cSDimitry Andric     StackDepotStats stack_depot_stats = StackDepotGetStats();
13068d75effSDimitry Andric     // FIXME: we want this at normal exit, too!
13168d75effSDimitry Andric     // FIXME: but only with verbosity=1 or something
132349cc55cSDimitry Andric     Printf("Unique heap origins: %zu\n", stack_depot_stats.n_uniq_ids);
133349cc55cSDimitry Andric     Printf("Stack depot allocated bytes: %zu\n", stack_depot_stats.allocated);
13468d75effSDimitry Andric 
135349cc55cSDimitry Andric     StackDepotStats chained_origin_depot_stats = ChainedOriginDepotGetStats();
13668d75effSDimitry Andric     Printf("Unique origin histories: %zu\n",
137349cc55cSDimitry Andric            chained_origin_depot_stats.n_uniq_ids);
13868d75effSDimitry Andric     Printf("History depot allocated bytes: %zu\n",
139349cc55cSDimitry Andric            chained_origin_depot_stats.allocated);
14068d75effSDimitry Andric   }
14168d75effSDimitry Andric }
14268d75effSDimitry Andric 
ReportAtExitStatistics()14368d75effSDimitry Andric void ReportAtExitStatistics() {
14468d75effSDimitry Andric   ScopedErrorReportLock l;
14568d75effSDimitry Andric 
14668d75effSDimitry Andric   if (msan_report_count > 0) {
14768d75effSDimitry Andric     Decorator d;
14868d75effSDimitry Andric     Printf("%s", d.Warning());
14968d75effSDimitry Andric     Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count);
15068d75effSDimitry Andric     Printf("%s", d.Default());
15168d75effSDimitry Andric   }
15268d75effSDimitry Andric }
15368d75effSDimitry Andric 
15468d75effSDimitry Andric class OriginSet {
15568d75effSDimitry Andric  public:
OriginSet()15668d75effSDimitry Andric   OriginSet() : next_id_(0) {}
insert(u32 o)15768d75effSDimitry Andric   int insert(u32 o) {
15868d75effSDimitry Andric     // Scan from the end for better locality.
15968d75effSDimitry Andric     for (int i = next_id_ - 1; i >= 0; --i)
16068d75effSDimitry Andric       if (origins_[i] == o) return i;
16168d75effSDimitry Andric     if (next_id_ == kMaxSize_) return OVERFLOW;
16268d75effSDimitry Andric     int id = next_id_++;
16368d75effSDimitry Andric     origins_[id] = o;
16468d75effSDimitry Andric     return id;
16568d75effSDimitry Andric   }
size()16668d75effSDimitry Andric   int size() { return next_id_; }
get(int id)16768d75effSDimitry Andric   u32 get(int id) { return origins_[id]; }
asChar(int id)16868d75effSDimitry Andric   static char asChar(int id) {
16968d75effSDimitry Andric     switch (id) {
17068d75effSDimitry Andric       case MISSING:
17168d75effSDimitry Andric         return '.';
17268d75effSDimitry Andric       case OVERFLOW:
17368d75effSDimitry Andric         return '*';
17468d75effSDimitry Andric       default:
17568d75effSDimitry Andric         return 'A' + id;
17668d75effSDimitry Andric     }
17768d75effSDimitry Andric   }
17868d75effSDimitry Andric   static const int OVERFLOW = -1;
17968d75effSDimitry Andric   static const int MISSING = -2;
18068d75effSDimitry Andric 
18168d75effSDimitry Andric  private:
18268d75effSDimitry Andric   static const int kMaxSize_ = 'Z' - 'A' + 1;
18368d75effSDimitry Andric   u32 origins_[kMaxSize_];
18468d75effSDimitry Andric   int next_id_;
18568d75effSDimitry Andric };
18668d75effSDimitry Andric 
DescribeMemoryRange(const void * x,uptr size)18768d75effSDimitry Andric void DescribeMemoryRange(const void *x, uptr size) {
18868d75effSDimitry Andric   // Real limits.
18968d75effSDimitry Andric   uptr start = MEM_TO_SHADOW(x);
19068d75effSDimitry Andric   uptr end = start + size;
19168d75effSDimitry Andric   // Scan limits: align start down to 4; align size up to 16.
19268d75effSDimitry Andric   uptr s = start & ~3UL;
19368d75effSDimitry Andric   size = end - s;
19468d75effSDimitry Andric   size = (size + 15) & ~15UL;
19568d75effSDimitry Andric   uptr e = s + size;
19668d75effSDimitry Andric 
19768d75effSDimitry Andric   // Single letter names to origin id mapping.
19868d75effSDimitry Andric   OriginSet origin_set;
19968d75effSDimitry Andric 
20068d75effSDimitry Andric   uptr pos = 0;  // Offset from aligned start.
20168d75effSDimitry Andric   bool with_origins = __msan_get_track_origins();
20268d75effSDimitry Andric   // True if there is at least 1 poisoned bit in the last 4-byte group.
20368d75effSDimitry Andric   bool last_quad_poisoned;
20468d75effSDimitry Andric   int origin_ids[4];  // Single letter origin ids for the current line.
20568d75effSDimitry Andric 
20668d75effSDimitry Andric   Decorator d;
20768d75effSDimitry Andric   Printf("%s", d.Warning());
208349cc55cSDimitry Andric   uptr start_x = reinterpret_cast<uptr>(x);
209349cc55cSDimitry Andric   Printf("Shadow map [%p, %p) of [%p, %p), %zu bytes:\n",
210349cc55cSDimitry Andric          reinterpret_cast<void *>(start), reinterpret_cast<void *>(end),
211349cc55cSDimitry Andric          reinterpret_cast<void *>(start_x),
212349cc55cSDimitry Andric          reinterpret_cast<void *>(start_x + end - start), end - start);
21368d75effSDimitry Andric   Printf("%s", d.Default());
21468d75effSDimitry Andric   while (s < e) {
21568d75effSDimitry Andric     // Line start.
21668d75effSDimitry Andric     if (pos % 16 == 0) {
21768d75effSDimitry Andric       for (int i = 0; i < 4; ++i) origin_ids[i] = -1;
218349cc55cSDimitry Andric       Printf("%p[%p]:", reinterpret_cast<void *>(s),
219349cc55cSDimitry Andric              reinterpret_cast<void *>(start_x - start + s));
22068d75effSDimitry Andric     }
22168d75effSDimitry Andric     // Group start.
22268d75effSDimitry Andric     if (pos % 4 == 0) {
22368d75effSDimitry Andric       Printf(" ");
22468d75effSDimitry Andric       last_quad_poisoned = false;
22568d75effSDimitry Andric     }
22668d75effSDimitry Andric     // Print shadow byte.
22768d75effSDimitry Andric     if (s < start || s >= end) {
22868d75effSDimitry Andric       Printf("..");
22968d75effSDimitry Andric     } else {
23068d75effSDimitry Andric       unsigned char v = *(unsigned char *)s;
23168d75effSDimitry Andric       if (v) last_quad_poisoned = true;
23268d75effSDimitry Andric       Printf("%x%x", v >> 4, v & 0xf);
23368d75effSDimitry Andric     }
23468d75effSDimitry Andric     // Group end.
23568d75effSDimitry Andric     if (pos % 4 == 3 && with_origins) {
23668d75effSDimitry Andric       int id = OriginSet::MISSING;
23768d75effSDimitry Andric       if (last_quad_poisoned) {
23868d75effSDimitry Andric         u32 o = *(u32 *)SHADOW_TO_ORIGIN(s - 3);
23968d75effSDimitry Andric         id = origin_set.insert(o);
24068d75effSDimitry Andric       }
24168d75effSDimitry Andric       origin_ids[(pos % 16) / 4] = id;
24268d75effSDimitry Andric     }
24368d75effSDimitry Andric     // Line end.
24468d75effSDimitry Andric     if (pos % 16 == 15) {
24568d75effSDimitry Andric       if (with_origins) {
24668d75effSDimitry Andric         Printf("  |");
24768d75effSDimitry Andric         for (int i = 0; i < 4; ++i) {
24868d75effSDimitry Andric           char c = OriginSet::asChar(origin_ids[i]);
24968d75effSDimitry Andric           Printf("%c", c);
25068d75effSDimitry Andric           if (i != 3) Printf(" ");
25168d75effSDimitry Andric         }
25268d75effSDimitry Andric         Printf("|");
25368d75effSDimitry Andric       }
25468d75effSDimitry Andric       Printf("\n");
25568d75effSDimitry Andric     }
25668d75effSDimitry Andric     size--;
25768d75effSDimitry Andric     s++;
25868d75effSDimitry Andric     pos++;
25968d75effSDimitry Andric   }
26068d75effSDimitry Andric 
26168d75effSDimitry Andric   Printf("\n");
26268d75effSDimitry Andric 
26368d75effSDimitry Andric   for (int i = 0; i < origin_set.size(); ++i) {
26468d75effSDimitry Andric     u32 o = origin_set.get(i);
26568d75effSDimitry Andric     Printf("Origin %c (origin_id %x):\n", OriginSet::asChar(i), o);
26668d75effSDimitry Andric     DescribeOrigin(o);
26768d75effSDimitry Andric   }
26868d75effSDimitry Andric }
26968d75effSDimitry Andric 
ReportUMRInsideAddressRange(const char * function,const void * start,uptr size,uptr offset)27006c3fb27SDimitry Andric void ReportUMRInsideAddressRange(const char *function, const void *start,
27106c3fb27SDimitry Andric                                  uptr size, uptr offset) {
272*5f757f3fSDimitry Andric   function = StackTracePrinter::GetOrInit()->StripFunctionName(function);
27368d75effSDimitry Andric   Decorator d;
27468d75effSDimitry Andric   Printf("%s", d.Warning());
27568d75effSDimitry Andric   Printf("%sUninitialized bytes in %s%s%s at offset %zu inside [%p, %zu)%s\n",
27606c3fb27SDimitry Andric          d.Warning(), d.Name(), function, d.Warning(), offset, start, size,
27768d75effSDimitry Andric          d.Default());
27868d75effSDimitry Andric   if (__sanitizer::Verbosity())
27968d75effSDimitry Andric     DescribeMemoryRange(start, size);
28068d75effSDimitry Andric }
28168d75effSDimitry Andric 
28268d75effSDimitry Andric }  // namespace __msan
283