| 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(format: "%s" , d.Origin()); |
| 42 | if (so) { |
| 43 | Printf( |
| 44 | format: " %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(format: " %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: &stack); |
| 63 | Printf(format: " %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(id: o.getStackId(), pc: &pc); |
| 70 | DescribeStackOrigin(so, pc); |
| 71 | } else { |
| 72 | StackTrace stack = o.getStackTraceForHeapOrigin(); |
| 73 | switch (stack.tag) { |
| 74 | case StackTrace::TAG_ALLOC: |
| 75 | Printf(format: " %sUninitialized value was created by a heap allocation%s\n" , |
| 76 | d.Origin(), d.Default()); |
| 77 | break; |
| 78 | case StackTrace::TAG_DEALLOC: |
| 79 | Printf(format: " %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(format: " %sMemory was marked as uninitialized%s\n" , d.Origin(), |
| 84 | d.Default()); |
| 85 | break; |
| 86 | case STACK_TRACE_TAG_FIELDS: |
| 87 | Printf(format: " %sMember fields were destroyed%s\n" , d.Origin(), d.Default()); |
| 88 | break; |
| 89 | case STACK_TRACE_TAG_VPTR: |
| 90 | Printf(format: " %sVirtual table ptr was destroyed%s\n" , d.Origin(), |
| 91 | d.Default()); |
| 92 | break; |
| 93 | default: |
| 94 | Printf(format: " %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(format: "%s" , d.Warning()); |
| 109 | Report(format: "WARNING: MemorySanitizer: use-of-uninitialized-value\n" ); |
| 110 | Printf(format: "%s" , d.Default()); |
| 111 | stack->Print(); |
| 112 | if (origin) { |
| 113 | DescribeOrigin(id: origin); |
| 114 | } |
| 115 | ReportErrorSummary(error_type: "use-of-uninitialized-value" , trace: stack); |
| 116 | } |
| 117 | |
| 118 | void ReportExpectedUMRNotFound(StackTrace *stack) { |
| 119 | ScopedErrorReportLock l; |
| 120 | |
| 121 | Printf(format: "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(format: "Unique heap origins: %zu\n" , stack_depot_stats.n_uniq_ids); |
| 133 | Printf(format: "Stack depot allocated bytes: %zu\n" , stack_depot_stats.allocated); |
| 134 | |
| 135 | StackDepotStats chained_origin_depot_stats = ChainedOriginDepotGetStats(); |
| 136 | Printf(format: "Unique origin histories: %zu\n" , |
| 137 | chained_origin_depot_stats.n_uniq_ids); |
| 138 | Printf(format: "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(format: "%s" , d.Warning()); |
| 149 | Printf(format: "MemorySanitizer: %d warnings reported.\n" , msan_report_count); |
| 150 | Printf(format: "%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(format: "%s" , d.Warning()); |
| 208 | uptr start_x = reinterpret_cast<uptr>(x); |
| 209 | Printf(format: "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(format: "%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(format: "%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(format: " " ); |
| 224 | last_quad_poisoned = false; |
| 225 | } |
| 226 | // Print shadow byte. |
| 227 | if (s < start || s >= end) { |
| 228 | Printf(format: ".." ); |
| 229 | } else { |
| 230 | unsigned char v = *(unsigned char *)s; |
| 231 | if (v) last_quad_poisoned = true; |
| 232 | Printf(format: "%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(format: " |" ); |
| 247 | for (int i = 0; i < 4; ++i) { |
| 248 | char c = OriginSet::asChar(id: origin_ids[i]); |
| 249 | Printf(format: "%c" , c); |
| 250 | if (i != 3) Printf(format: " " ); |
| 251 | } |
| 252 | Printf(format: "|" ); |
| 253 | } |
| 254 | Printf(format: "\n" ); |
| 255 | } |
| 256 | size--; |
| 257 | s++; |
| 258 | pos++; |
| 259 | } |
| 260 | |
| 261 | Printf(format: "\n" ); |
| 262 | |
| 263 | for (int i = 0; i < origin_set.size(); ++i) { |
| 264 | u32 o = origin_set.get(id: i); |
| 265 | Printf(format: "Origin %c (origin_id %x):\n" , OriginSet::asChar(id: i), o); |
| 266 | DescribeOrigin(id: 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(format: "%s" , d.Warning()); |
| 275 | Printf(format: "%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(x: start, size); |
| 280 | } |
| 281 | |
| 282 | } // namespace __msan |
| 283 | |