1 //===-- msan_report.cpp ---------------------------------------------------===// 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 MemorySanitizer. 10 // 11 // Error reporting. 12 //===----------------------------------------------------------------------===// 13 14 #include "msan_report.h" 15 16 #include "msan.h" 17 #include "msan_chained_origin_depot.h" 18 #include "msan_origin.h" 19 #include "sanitizer_common/sanitizer_allocator_internal.h" 20 #include "sanitizer_common/sanitizer_common.h" 21 #include "sanitizer_common/sanitizer_flags.h" 22 #include "sanitizer_common/sanitizer_mutex.h" 23 #include "sanitizer_common/sanitizer_report_decorator.h" 24 #include "sanitizer_common/sanitizer_stackdepot.h" 25 #include "sanitizer_common/sanitizer_stacktrace_printer.h" 26 #include "sanitizer_common/sanitizer_symbolizer.h" 27 28 using namespace __sanitizer; 29 30 namespace __msan { 31 32 class Decorator: public __sanitizer::SanitizerCommonDecorator { 33 public: 34 Decorator() : SanitizerCommonDecorator() { } 35 const char *Origin() const { return Magenta(); } 36 const char *Name() const { return Green(); } 37 }; 38 39 static void DescribeStackOrigin(const char *so, uptr pc) { 40 Decorator d; 41 Printf("%s", d.Origin()); 42 if (so) { 43 Printf( 44 " %sUninitialized value was created by an allocation of '%s%s%s'" 45 " in the stack frame%s\n", 46 d.Origin(), d.Name(), so, d.Origin(), d.Default()); 47 } else { 48 Printf(" %sUninitialized value was created in the stack frame%s\n", 49 d.Origin(), d.Default()); 50 } 51 52 if (pc) 53 StackTrace(&pc, 1).Print(); 54 } 55 56 static void DescribeOrigin(u32 id) { 57 VPrintf(1, " raw origin id: %d\n", id); 58 Decorator d; 59 Origin o = Origin::FromRawId(id); 60 while (o.isChainedOrigin()) { 61 StackTrace stack; 62 o = o.getNextChainedOrigin(&stack); 63 Printf(" %sUninitialized value was stored to memory at%s\n", d.Origin(), 64 d.Default()); 65 stack.Print(); 66 } 67 if (o.isStackOrigin()) { 68 uptr pc; 69 const char *so = GetStackOriginDescr(o.getStackId(), &pc); 70 DescribeStackOrigin(so, pc); 71 } else { 72 StackTrace stack = o.getStackTraceForHeapOrigin(); 73 switch (stack.tag) { 74 case StackTrace::TAG_ALLOC: 75 Printf(" %sUninitialized value was created by a heap allocation%s\n", 76 d.Origin(), d.Default()); 77 break; 78 case StackTrace::TAG_DEALLOC: 79 Printf(" %sUninitialized value was created by a heap deallocation%s\n", 80 d.Origin(), d.Default()); 81 break; 82 case STACK_TRACE_TAG_POISON: 83 Printf(" %sMemory was marked as uninitialized%s\n", d.Origin(), 84 d.Default()); 85 break; 86 case STACK_TRACE_TAG_FIELDS: 87 Printf(" %sMember fields were destroyed%s\n", d.Origin(), d.Default()); 88 break; 89 case STACK_TRACE_TAG_VPTR: 90 Printf(" %sVirtual table ptr was destroyed%s\n", d.Origin(), 91 d.Default()); 92 break; 93 default: 94 Printf(" %sUninitialized value was created%s\n", d.Origin(), 95 d.Default()); 96 break; 97 } 98 stack.Print(); 99 } 100 } 101 102 void ReportUMR(StackTrace *stack, u32 origin) { 103 if (!__msan::flags()->report_umrs) return; 104 105 ScopedErrorReportLock l; 106 107 Decorator d; 108 Printf("%s", d.Warning()); 109 Report("WARNING: MemorySanitizer: use-of-uninitialized-value\n"); 110 Printf("%s", d.Default()); 111 stack->Print(); 112 if (origin) { 113 DescribeOrigin(origin); 114 } 115 ReportErrorSummary("use-of-uninitialized-value", stack); 116 } 117 118 void ReportExpectedUMRNotFound(StackTrace *stack) { 119 ScopedErrorReportLock l; 120 121 Printf("WARNING: Expected use of uninitialized value not found\n"); 122 stack->Print(); 123 } 124 125 void ReportStats() { 126 ScopedErrorReportLock l; 127 128 if (__msan_get_track_origins() > 0) { 129 StackDepotStats stack_depot_stats = StackDepotGetStats(); 130 // FIXME: we want this at normal exit, too! 131 // FIXME: but only with verbosity=1 or something 132 Printf("Unique heap origins: %zu\n", stack_depot_stats.n_uniq_ids); 133 Printf("Stack depot allocated bytes: %zu\n", stack_depot_stats.allocated); 134 135 StackDepotStats chained_origin_depot_stats = ChainedOriginDepotGetStats(); 136 Printf("Unique origin histories: %zu\n", 137 chained_origin_depot_stats.n_uniq_ids); 138 Printf("History depot allocated bytes: %zu\n", 139 chained_origin_depot_stats.allocated); 140 } 141 } 142 143 void ReportAtExitStatistics() { 144 ScopedErrorReportLock l; 145 146 if (msan_report_count > 0) { 147 Decorator d; 148 Printf("%s", d.Warning()); 149 Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count); 150 Printf("%s", d.Default()); 151 } 152 } 153 154 class OriginSet { 155 public: 156 OriginSet() : next_id_(0) {} 157 int insert(u32 o) { 158 // Scan from the end for better locality. 159 for (int i = next_id_ - 1; i >= 0; --i) 160 if (origins_[i] == o) return i; 161 if (next_id_ == kMaxSize_) return OVERFLOW; 162 int id = next_id_++; 163 origins_[id] = o; 164 return id; 165 } 166 int size() { return next_id_; } 167 u32 get(int id) { return origins_[id]; } 168 static char asChar(int id) { 169 switch (id) { 170 case MISSING: 171 return '.'; 172 case OVERFLOW: 173 return '*'; 174 default: 175 return 'A' + id; 176 } 177 } 178 static const int OVERFLOW = -1; 179 static const int MISSING = -2; 180 181 private: 182 static const int kMaxSize_ = 'Z' - 'A' + 1; 183 u32 origins_[kMaxSize_]; 184 int next_id_; 185 }; 186 187 void DescribeMemoryRange(const void *x, uptr size) { 188 // Real limits. 189 uptr start = MEM_TO_SHADOW(x); 190 uptr end = start + size; 191 // Scan limits: align start down to 4; align size up to 16. 192 uptr s = start & ~3UL; 193 size = end - s; 194 size = (size + 15) & ~15UL; 195 uptr e = s + size; 196 197 // Single letter names to origin id mapping. 198 OriginSet origin_set; 199 200 uptr pos = 0; // Offset from aligned start. 201 bool with_origins = __msan_get_track_origins(); 202 // True if there is at least 1 poisoned bit in the last 4-byte group. 203 bool last_quad_poisoned; 204 int origin_ids[4]; // Single letter origin ids for the current line. 205 206 Decorator d; 207 Printf("%s", d.Warning()); 208 uptr start_x = reinterpret_cast<uptr>(x); 209 Printf("Shadow map [%p, %p) of [%p, %p), %zu bytes:\n", 210 reinterpret_cast<void *>(start), reinterpret_cast<void *>(end), 211 reinterpret_cast<void *>(start_x), 212 reinterpret_cast<void *>(start_x + end - start), end - start); 213 Printf("%s", d.Default()); 214 while (s < e) { 215 // Line start. 216 if (pos % 16 == 0) { 217 for (int i = 0; i < 4; ++i) origin_ids[i] = -1; 218 Printf("%p[%p]:", reinterpret_cast<void *>(s), 219 reinterpret_cast<void *>(start_x - start + s)); 220 } 221 // Group start. 222 if (pos % 4 == 0) { 223 Printf(" "); 224 last_quad_poisoned = false; 225 } 226 // Print shadow byte. 227 if (s < start || s >= end) { 228 Printf(".."); 229 } else { 230 unsigned char v = *(unsigned char *)s; 231 if (v) last_quad_poisoned = true; 232 Printf("%x%x", v >> 4, v & 0xf); 233 } 234 // Group end. 235 if (pos % 4 == 3 && with_origins) { 236 int id = OriginSet::MISSING; 237 if (last_quad_poisoned) { 238 u32 o = *(u32 *)SHADOW_TO_ORIGIN(s - 3); 239 id = origin_set.insert(o); 240 } 241 origin_ids[(pos % 16) / 4] = id; 242 } 243 // Line end. 244 if (pos % 16 == 15) { 245 if (with_origins) { 246 Printf(" |"); 247 for (int i = 0; i < 4; ++i) { 248 char c = OriginSet::asChar(origin_ids[i]); 249 Printf("%c", c); 250 if (i != 3) Printf(" "); 251 } 252 Printf("|"); 253 } 254 Printf("\n"); 255 } 256 size--; 257 s++; 258 pos++; 259 } 260 261 Printf("\n"); 262 263 for (int i = 0; i < origin_set.size(); ++i) { 264 u32 o = origin_set.get(i); 265 Printf("Origin %c (origin_id %x):\n", OriginSet::asChar(i), o); 266 DescribeOrigin(o); 267 } 268 } 269 270 void ReportUMRInsideAddressRange(const char *function, const void *start, 271 uptr size, uptr offset) { 272 function = StackTracePrinter::GetOrInit()->StripFunctionName(function); 273 Decorator d; 274 Printf("%s", d.Warning()); 275 Printf("%sUninitialized bytes in %s%s%s at offset %zu inside [%p, %zu)%s\n", 276 d.Warning(), d.Name(), function, d.Warning(), offset, start, size, 277 d.Default()); 278 if (__sanitizer::Verbosity()) 279 DescribeMemoryRange(start, size); 280 } 281 282 } // namespace __msan 283