| 1 | //===-- asan_descriptions.h -------------------------------------*- C++ -*-===// |
| 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 AddressSanitizer, an address sanity checker. |
| 10 | // |
| 11 | // ASan-private header for asan_descriptions.cpp. |
| 12 | // TODO(filcab): Most struct definitions should move to the interface headers. |
| 13 | //===----------------------------------------------------------------------===// |
| 14 | #ifndef ASAN_DESCRIPTIONS_H |
| 15 | #define ASAN_DESCRIPTIONS_H |
| 16 | |
| 17 | #include "asan_allocator.h" |
| 18 | #include "asan_thread.h" |
| 19 | #include "sanitizer_common/sanitizer_common.h" |
| 20 | #include "sanitizer_common/sanitizer_report_decorator.h" |
| 21 | |
| 22 | namespace __asan { |
| 23 | |
| 24 | void DescribeThread(AsanThreadContext *context); |
| 25 | static inline void DescribeThread(AsanThread *t) { |
| 26 | if (t) DescribeThread(context: t->context()); |
| 27 | } |
| 28 | |
| 29 | class AsanThreadIdAndName { |
| 30 | public: |
| 31 | explicit AsanThreadIdAndName(AsanThreadContext *t); |
| 32 | explicit AsanThreadIdAndName(u32 tid); |
| 33 | |
| 34 | // Contains "T%tid (%name)" or "T%tid" if the name is empty. |
| 35 | const char *c_str() const { return &name[0]; } |
| 36 | |
| 37 | private: |
| 38 | char name[128]; |
| 39 | }; |
| 40 | |
| 41 | class Decorator : public __sanitizer::SanitizerCommonDecorator { |
| 42 | public: |
| 43 | Decorator() : SanitizerCommonDecorator() {} |
| 44 | const char *Access() { return Blue(); } |
| 45 | const char *Location() { return Green(); } |
| 46 | const char *Allocation() { return Magenta(); } |
| 47 | |
| 48 | const char *ShadowByte(u8 byte) { |
| 49 | switch (byte) { |
| 50 | case kAsanHeapLeftRedzoneMagic: |
| 51 | case kAsanArrayCookieMagic: |
| 52 | return Red(); |
| 53 | case kAsanHeapFreeMagic: |
| 54 | return Magenta(); |
| 55 | case kAsanStackLeftRedzoneMagic: |
| 56 | case kAsanStackMidRedzoneMagic: |
| 57 | case kAsanStackRightRedzoneMagic: |
| 58 | return Red(); |
| 59 | case kAsanStackAfterReturnMagic: |
| 60 | return Magenta(); |
| 61 | case kAsanInitializationOrderMagic: |
| 62 | return Cyan(); |
| 63 | case kAsanUserPoisonedMemoryMagic: |
| 64 | case kAsanContiguousContainerOOBMagic: |
| 65 | case kAsanAllocaLeftMagic: |
| 66 | case kAsanAllocaRightMagic: |
| 67 | return Blue(); |
| 68 | case kAsanStackUseAfterScopeMagic: |
| 69 | return Magenta(); |
| 70 | case kAsanGlobalRedzoneMagic: |
| 71 | return Red(); |
| 72 | case kAsanInternalHeapMagic: |
| 73 | return Yellow(); |
| 74 | case kAsanIntraObjectRedzone: |
| 75 | return Yellow(); |
| 76 | default: |
| 77 | return Default(); |
| 78 | } |
| 79 | } |
| 80 | }; |
| 81 | |
| 82 | enum ShadowKind : u8 { |
| 83 | kShadowKindLow, |
| 84 | kShadowKindGap, |
| 85 | kShadowKindHigh, |
| 86 | }; |
| 87 | static const char *const ShadowNames[] = {"low shadow" , "shadow gap" , |
| 88 | "high shadow" }; |
| 89 | |
| 90 | struct ShadowAddressDescription { |
| 91 | uptr addr; |
| 92 | ShadowKind kind; |
| 93 | u8 shadow_byte; |
| 94 | |
| 95 | void Print() const; |
| 96 | }; |
| 97 | |
| 98 | bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr); |
| 99 | bool DescribeAddressIfShadow(uptr addr); |
| 100 | |
| 101 | enum AccessType { |
| 102 | kAccessTypeLeft, |
| 103 | kAccessTypeRight, |
| 104 | kAccessTypeInside, |
| 105 | kAccessTypeUnknown, // This means we have an AddressSanitizer bug! |
| 106 | }; |
| 107 | |
| 108 | struct ChunkAccess { |
| 109 | uptr bad_addr; |
| 110 | sptr offset; |
| 111 | uptr chunk_begin; |
| 112 | uptr chunk_size; |
| 113 | u32 user_requested_alignment : 12; |
| 114 | u32 access_type : 2; |
| 115 | u32 alloc_type : 2; |
| 116 | }; |
| 117 | |
| 118 | struct HeapAddressDescription { |
| 119 | uptr addr; |
| 120 | uptr alloc_tid; |
| 121 | uptr free_tid; |
| 122 | u32 alloc_stack_id; |
| 123 | u32 free_stack_id; |
| 124 | ChunkAccess chunk_access; |
| 125 | |
| 126 | void Print() const; |
| 127 | }; |
| 128 | |
| 129 | bool GetHeapAddressInformation(uptr addr, uptr access_size, |
| 130 | HeapAddressDescription *descr); |
| 131 | bool DescribeAddressIfHeap(uptr addr, uptr access_size = 1); |
| 132 | |
| 133 | struct StackAddressDescription { |
| 134 | uptr addr; |
| 135 | uptr tid; |
| 136 | uptr offset; |
| 137 | uptr frame_pc; |
| 138 | uptr access_size; |
| 139 | const char *frame_descr; |
| 140 | |
| 141 | void Print() const; |
| 142 | }; |
| 143 | |
| 144 | bool GetStackAddressInformation(uptr addr, uptr access_size, |
| 145 | StackAddressDescription *descr); |
| 146 | |
| 147 | struct WildAddressDescription { |
| 148 | uptr addr; |
| 149 | uptr access_size; |
| 150 | |
| 151 | void Print() const; |
| 152 | }; |
| 153 | |
| 154 | struct GlobalAddressDescription { |
| 155 | uptr addr; |
| 156 | // Assume address is close to at most four globals. |
| 157 | static const int kMaxGlobals = 4; |
| 158 | __asan_global globals[kMaxGlobals]; |
| 159 | u32 reg_sites[kMaxGlobals]; |
| 160 | uptr access_size; |
| 161 | u8 size; |
| 162 | |
| 163 | void Print(const char *bug_type = "" ) const; |
| 164 | |
| 165 | // Returns true when this descriptions points inside the same global variable |
| 166 | // as other. Descriptions can have different address within the variable |
| 167 | bool PointsInsideTheSameVariable(const GlobalAddressDescription &other) const; |
| 168 | }; |
| 169 | |
| 170 | bool GetGlobalAddressInformation(uptr addr, uptr access_size, |
| 171 | GlobalAddressDescription *descr); |
| 172 | bool DescribeAddressIfGlobal(uptr addr, uptr access_size, const char *bug_type); |
| 173 | |
| 174 | // General function to describe an address. Will try to describe the address as |
| 175 | // a shadow, global (variable), stack, or heap address. |
| 176 | // bug_type is optional and is used for checking if we're reporting an |
| 177 | // initialization-order-fiasco |
| 178 | // The proper access_size should be passed for stack, global, and heap |
| 179 | // addresses. Defaults to 1. |
| 180 | // Each of the *AddressDescription functions has its own Print() member, which |
| 181 | // may take access_size and bug_type parameters if needed. |
| 182 | void PrintAddressDescription(uptr addr, uptr access_size = 1, |
| 183 | const char *bug_type = "" ); |
| 184 | |
| 185 | enum AddressKind { |
| 186 | kAddressKindWild, |
| 187 | kAddressKindShadow, |
| 188 | kAddressKindHeap, |
| 189 | kAddressKindStack, |
| 190 | kAddressKindGlobal, |
| 191 | }; |
| 192 | |
| 193 | class AddressDescription { |
| 194 | struct AddressDescriptionData { |
| 195 | AddressKind kind; |
| 196 | union { |
| 197 | ShadowAddressDescription shadow; |
| 198 | HeapAddressDescription heap; |
| 199 | StackAddressDescription stack; |
| 200 | GlobalAddressDescription global; |
| 201 | WildAddressDescription wild; |
| 202 | }; |
| 203 | }; |
| 204 | |
| 205 | AddressDescriptionData data; |
| 206 | |
| 207 | public: |
| 208 | AddressDescription() = default; |
| 209 | // shouldLockThreadRegistry allows us to skip locking if we're sure we already |
| 210 | // have done it. |
| 211 | explicit AddressDescription(uptr addr, bool shouldLockThreadRegistry = true) |
| 212 | : AddressDescription(addr, 1, shouldLockThreadRegistry) {} |
| 213 | AddressDescription(uptr addr, uptr access_size, |
| 214 | bool shouldLockThreadRegistry = true); |
| 215 | |
| 216 | uptr Address() const { |
| 217 | switch (data.kind) { |
| 218 | case kAddressKindWild: |
| 219 | return data.wild.addr; |
| 220 | case kAddressKindShadow: |
| 221 | return data.shadow.addr; |
| 222 | case kAddressKindHeap: |
| 223 | return data.heap.addr; |
| 224 | case kAddressKindStack: |
| 225 | return data.stack.addr; |
| 226 | case kAddressKindGlobal: |
| 227 | return data.global.addr; |
| 228 | } |
| 229 | UNREACHABLE("AddressInformation kind is invalid" ); |
| 230 | } |
| 231 | void Print(const char *bug_descr = nullptr) const { |
| 232 | switch (data.kind) { |
| 233 | case kAddressKindWild: |
| 234 | data.wild.Print(); |
| 235 | return; |
| 236 | case kAddressKindShadow: |
| 237 | return data.shadow.Print(); |
| 238 | case kAddressKindHeap: |
| 239 | return data.heap.Print(); |
| 240 | case kAddressKindStack: |
| 241 | return data.stack.Print(); |
| 242 | case kAddressKindGlobal: |
| 243 | // initialization-order-fiasco has a special Print() |
| 244 | return data.global.Print(bug_type: bug_descr); |
| 245 | } |
| 246 | UNREACHABLE("AddressInformation kind is invalid" ); |
| 247 | } |
| 248 | |
| 249 | void StoreTo(AddressDescriptionData *dst) const { *dst = data; } |
| 250 | |
| 251 | const ShadowAddressDescription *AsShadow() const { |
| 252 | return data.kind == kAddressKindShadow ? &data.shadow : nullptr; |
| 253 | } |
| 254 | const HeapAddressDescription *AsHeap() const { |
| 255 | return data.kind == kAddressKindHeap ? &data.heap : nullptr; |
| 256 | } |
| 257 | const StackAddressDescription *AsStack() const { |
| 258 | return data.kind == kAddressKindStack ? &data.stack : nullptr; |
| 259 | } |
| 260 | const GlobalAddressDescription *AsGlobal() const { |
| 261 | return data.kind == kAddressKindGlobal ? &data.global : nullptr; |
| 262 | } |
| 263 | }; |
| 264 | |
| 265 | } // namespace __asan |
| 266 | |
| 267 | #endif // ASAN_DESCRIPTIONS_H |
| 268 | |