Alexey Samsonov | e5f5895 | 2012-06-04 13:50:10 +0000 | [diff] [blame] | 1 | //===-- asan_poisoning.cc -------------------------------------------------===// |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file is a part of AddressSanitizer, an address sanity checker. |
| 11 | // |
Kostya Serebryany | 218a9b7 | 2011-11-30 18:50:23 +0000 | [diff] [blame] | 12 | // Shadow memory poisoning by ASan RTL and by user application. |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 13 | //===----------------------------------------------------------------------===// |
| 14 | |
Alexey Samsonov | 7e84349 | 2013-03-28 15:42:43 +0000 | [diff] [blame] | 15 | #include "asan_poisoning.h" |
Kostya Serebryany | eb28093 | 2012-12-28 15:24:16 +0000 | [diff] [blame] | 16 | #include "sanitizer_common/sanitizer_libc.h" |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 17 | |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 18 | namespace __asan { |
| 19 | |
Kostya Serebryany | ee39255 | 2012-05-31 15:02:07 +0000 | [diff] [blame] | 20 | void PoisonShadow(uptr addr, uptr size, u8 value) { |
Kostya Serebryany | 73bad81 | 2012-12-20 11:54:21 +0000 | [diff] [blame] | 21 | if (!flags()->poison_heap) return; |
Kostya Serebryany | 218a9b7 | 2011-11-30 18:50:23 +0000 | [diff] [blame] | 22 | CHECK(AddrIsAlignedByGranularity(addr)); |
Alexey Samsonov | 7e84349 | 2013-03-28 15:42:43 +0000 | [diff] [blame] | 23 | CHECK(AddrIsInMem(addr)); |
Kostya Serebryany | 218a9b7 | 2011-11-30 18:50:23 +0000 | [diff] [blame] | 24 | CHECK(AddrIsAlignedByGranularity(addr + size)); |
Alexey Samsonov | 7e84349 | 2013-03-28 15:42:43 +0000 | [diff] [blame] | 25 | CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY)); |
Kostya Serebryany | a27bdf7 | 2013-04-05 14:40:25 +0000 | [diff] [blame] | 26 | CHECK(REAL(memset)); |
Alexey Samsonov | 7e84349 | 2013-03-28 15:42:43 +0000 | [diff] [blame] | 27 | FastPoisonShadow(addr, size, value); |
Kostya Serebryany | 218a9b7 | 2011-11-30 18:50:23 +0000 | [diff] [blame] | 28 | } |
| 29 | |
Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 30 | void PoisonShadowPartialRightRedzone(uptr addr, |
| 31 | uptr size, |
| 32 | uptr redzone_size, |
Kostya Serebryany | ee39255 | 2012-05-31 15:02:07 +0000 | [diff] [blame] | 33 | u8 value) { |
Kostya Serebryany | 73bad81 | 2012-12-20 11:54:21 +0000 | [diff] [blame] | 34 | if (!flags()->poison_heap) return; |
Kostya Serebryany | 218a9b7 | 2011-11-30 18:50:23 +0000 | [diff] [blame] | 35 | CHECK(AddrIsAlignedByGranularity(addr)); |
Alexey Samsonov | 7e84349 | 2013-03-28 15:42:43 +0000 | [diff] [blame] | 36 | CHECK(AddrIsInMem(addr)); |
| 37 | FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); |
Kostya Serebryany | 218a9b7 | 2011-11-30 18:50:23 +0000 | [diff] [blame] | 38 | } |
| 39 | |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 40 | struct ShadowSegmentEndpoint { |
Kostya Serebryany | ee39255 | 2012-05-31 15:02:07 +0000 | [diff] [blame] | 41 | u8 *chunk; |
| 42 | s8 offset; // in [0, SHADOW_GRANULARITY) |
| 43 | s8 value; // = *chunk; |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 44 | |
Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 45 | explicit ShadowSegmentEndpoint(uptr address) { |
Kostya Serebryany | ee39255 | 2012-05-31 15:02:07 +0000 | [diff] [blame] | 46 | chunk = (u8*)MemToShadow(address); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 47 | offset = address & (SHADOW_GRANULARITY - 1); |
| 48 | value = *chunk; |
| 49 | } |
| 50 | }; |
| 51 | |
| 52 | } // namespace __asan |
| 53 | |
| 54 | // ---------------------- Interface ---------------- {{{1 |
| 55 | using namespace __asan; // NOLINT |
| 56 | |
| 57 | // Current implementation of __asan_(un)poison_memory_region doesn't check |
| 58 | // that user program (un)poisons the memory it owns. It poisons memory |
| 59 | // conservatively, and unpoisons progressively to make sure asan shadow |
| 60 | // mapping invariant is preserved (see detailed mapping description here: |
| 61 | // http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm). |
| 62 | // |
| 63 | // * if user asks to poison region [left, right), the program poisons |
| 64 | // at least [left, AlignDown(right)). |
| 65 | // * if user asks to unpoison region [left, right), the program unpoisons |
| 66 | // at most [AlignDown(left), right). |
Kostya Serebryany | 9aead37 | 2012-05-31 14:11:07 +0000 | [diff] [blame] | 67 | void __asan_poison_memory_region(void const volatile *addr, uptr size) { |
Alexey Samsonov | cb8c4dc | 2012-07-09 14:36:04 +0000 | [diff] [blame] | 68 | if (!flags()->allow_user_poisoning || size == 0) return; |
Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 69 | uptr beg_addr = (uptr)addr; |
| 70 | uptr end_addr = beg_addr + size; |
Alexey Samsonov | cb8c4dc | 2012-07-09 14:36:04 +0000 | [diff] [blame] | 71 | if (flags()->verbosity >= 1) { |
Alexey Samsonov | 5bcca4e | 2012-06-06 10:46:00 +0000 | [diff] [blame] | 72 | Printf("Trying to poison memory region [%p, %p)\n", |
| 73 | (void*)beg_addr, (void*)end_addr); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 74 | } |
| 75 | ShadowSegmentEndpoint beg(beg_addr); |
| 76 | ShadowSegmentEndpoint end(end_addr); |
| 77 | if (beg.chunk == end.chunk) { |
| 78 | CHECK(beg.offset < end.offset); |
Kostya Serebryany | ee39255 | 2012-05-31 15:02:07 +0000 | [diff] [blame] | 79 | s8 value = beg.value; |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 80 | CHECK(value == end.value); |
| 81 | // We can only poison memory if the byte in end.offset is unaddressable. |
| 82 | // No need to re-poison memory if it is poisoned already. |
| 83 | if (value > 0 && value <= end.offset) { |
| 84 | if (beg.offset > 0) { |
Kostya Serebryany | 2d8b3bd | 2011-12-02 18:42:04 +0000 | [diff] [blame] | 85 | *beg.chunk = Min(value, beg.offset); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 86 | } else { |
| 87 | *beg.chunk = kAsanUserPoisonedMemoryMagic; |
| 88 | } |
| 89 | } |
| 90 | return; |
| 91 | } |
| 92 | CHECK(beg.chunk < end.chunk); |
| 93 | if (beg.offset > 0) { |
| 94 | // Mark bytes from beg.offset as unaddressable. |
| 95 | if (beg.value == 0) { |
| 96 | *beg.chunk = beg.offset; |
| 97 | } else { |
Kostya Serebryany | 2d8b3bd | 2011-12-02 18:42:04 +0000 | [diff] [blame] | 98 | *beg.chunk = Min(beg.value, beg.offset); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 99 | } |
| 100 | beg.chunk++; |
| 101 | } |
Alexey Samsonov | 09672ca | 2012-02-08 13:45:31 +0000 | [diff] [blame] | 102 | REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 103 | // Poison if byte in end.offset is unaddressable. |
| 104 | if (end.value > 0 && end.value <= end.offset) { |
| 105 | *end.chunk = kAsanUserPoisonedMemoryMagic; |
| 106 | } |
| 107 | } |
| 108 | |
Kostya Serebryany | 9aead37 | 2012-05-31 14:11:07 +0000 | [diff] [blame] | 109 | void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { |
Alexey Samsonov | cb8c4dc | 2012-07-09 14:36:04 +0000 | [diff] [blame] | 110 | if (!flags()->allow_user_poisoning || size == 0) return; |
Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 111 | uptr beg_addr = (uptr)addr; |
| 112 | uptr end_addr = beg_addr + size; |
Alexey Samsonov | cb8c4dc | 2012-07-09 14:36:04 +0000 | [diff] [blame] | 113 | if (flags()->verbosity >= 1) { |
Alexey Samsonov | 5bcca4e | 2012-06-06 10:46:00 +0000 | [diff] [blame] | 114 | Printf("Trying to unpoison memory region [%p, %p)\n", |
| 115 | (void*)beg_addr, (void*)end_addr); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 116 | } |
| 117 | ShadowSegmentEndpoint beg(beg_addr); |
| 118 | ShadowSegmentEndpoint end(end_addr); |
| 119 | if (beg.chunk == end.chunk) { |
| 120 | CHECK(beg.offset < end.offset); |
Kostya Serebryany | ee39255 | 2012-05-31 15:02:07 +0000 | [diff] [blame] | 121 | s8 value = beg.value; |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 122 | CHECK(value == end.value); |
| 123 | // We unpoison memory bytes up to enbytes up to end.offset if it is not |
| 124 | // unpoisoned already. |
| 125 | if (value != 0) { |
Kostya Serebryany | 2d8b3bd | 2011-12-02 18:42:04 +0000 | [diff] [blame] | 126 | *beg.chunk = Max(value, end.offset); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 127 | } |
| 128 | return; |
| 129 | } |
| 130 | CHECK(beg.chunk < end.chunk); |
| 131 | if (beg.offset > 0) { |
| 132 | *beg.chunk = 0; |
| 133 | beg.chunk++; |
| 134 | } |
Alexey Samsonov | 09672ca | 2012-02-08 13:45:31 +0000 | [diff] [blame] | 135 | REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 136 | if (end.offset > 0 && end.value != 0) { |
Kostya Serebryany | 2d8b3bd | 2011-12-02 18:42:04 +0000 | [diff] [blame] | 137 | *end.chunk = Max(end.value, end.offset); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 138 | } |
| 139 | } |
| 140 | |
| 141 | bool __asan_address_is_poisoned(void const volatile *addr) { |
Kostya Serebryany | 3f4c387 | 2012-05-31 14:35:53 +0000 | [diff] [blame] | 142 | return __asan::AddressIsPoisoned((uptr)addr); |
Kostya Serebryany | 1e172b4 | 2011-11-30 01:07:02 +0000 | [diff] [blame] | 143 | } |
Alexey Samsonov | d4b5db8 | 2012-12-04 01:38:15 +0000 | [diff] [blame] | 144 | |
Kostya Serebryany | eb28093 | 2012-12-28 15:24:16 +0000 | [diff] [blame] | 145 | uptr __asan_region_is_poisoned(uptr beg, uptr size) { |
| 146 | if (!size) return 0; |
| 147 | uptr end = beg + size; |
| 148 | if (!AddrIsInMem(beg)) return beg; |
| 149 | if (!AddrIsInMem(end)) return end; |
| 150 | uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY); |
| 151 | uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY); |
| 152 | uptr shadow_beg = MemToShadow(aligned_b); |
| 153 | uptr shadow_end = MemToShadow(aligned_e); |
| 154 | // First check the first and the last application bytes, |
| 155 | // then check the SHADOW_GRANULARITY-aligned region by calling |
| 156 | // mem_is_zero on the corresponding shadow. |
| 157 | if (!__asan::AddressIsPoisoned(beg) && |
| 158 | !__asan::AddressIsPoisoned(end - 1) && |
| 159 | (shadow_end <= shadow_beg || |
| 160 | __sanitizer::mem_is_zero((const char *)shadow_beg, |
| 161 | shadow_end - shadow_beg))) |
| 162 | return 0; |
| 163 | // The fast check failed, so we have a poisoned byte somewhere. |
| 164 | // Find it slowly. |
| 165 | for (; beg < end; beg++) |
| 166 | if (__asan::AddressIsPoisoned(beg)) |
| 167 | return beg; |
| 168 | UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found"); |
| 169 | return 0; |
| 170 | } |
| 171 | |
Kostya Serebryany | dc0d179 | 2013-04-10 13:59:32 +0000 | [diff] [blame] | 172 | #define CHECK_SMALL_REGION(p, size, isWrite) \ |
| 173 | do { \ |
| 174 | uptr __p = reinterpret_cast<uptr>(p); \ |
| 175 | uptr __size = size; \ |
| 176 | if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \ |
| 177 | __asan::AddressIsPoisoned(__p + __size - 1))) { \ |
| 178 | GET_CURRENT_PC_BP_SP; \ |
| 179 | uptr __bad = __asan_region_is_poisoned(__p, __size); \ |
| 180 | __asan_report_error(pc, bp, sp, __bad, isWrite, __size);\ |
| 181 | } \ |
| 182 | } while (false); \ |
| 183 | |
| 184 | |
| 185 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE |
Evgeniy Stepanov | 2e9ffcb | 2013-06-04 13:49:10 +0000 | [diff] [blame] | 186 | u16 __sanitizer_unaligned_load16(const uu16 *p) { |
Kostya Serebryany | dc0d179 | 2013-04-10 13:59:32 +0000 | [diff] [blame] | 187 | CHECK_SMALL_REGION(p, sizeof(*p), false); |
| 188 | return *p; |
| 189 | } |
| 190 | |
| 191 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE |
Evgeniy Stepanov | 2e9ffcb | 2013-06-04 13:49:10 +0000 | [diff] [blame] | 192 | u32 __sanitizer_unaligned_load32(const uu32 *p) { |
Kostya Serebryany | dc0d179 | 2013-04-10 13:59:32 +0000 | [diff] [blame] | 193 | CHECK_SMALL_REGION(p, sizeof(*p), false); |
| 194 | return *p; |
| 195 | } |
| 196 | |
| 197 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE |
Evgeniy Stepanov | 2e9ffcb | 2013-06-04 13:49:10 +0000 | [diff] [blame] | 198 | u64 __sanitizer_unaligned_load64(const uu64 *p) { |
Kostya Serebryany | dc0d179 | 2013-04-10 13:59:32 +0000 | [diff] [blame] | 199 | CHECK_SMALL_REGION(p, sizeof(*p), false); |
| 200 | return *p; |
| 201 | } |
| 202 | |
| 203 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE |
Evgeniy Stepanov | 2e9ffcb | 2013-06-04 13:49:10 +0000 | [diff] [blame] | 204 | void __sanitizer_unaligned_store16(uu16 *p, u16 x) { |
Kostya Serebryany | dc0d179 | 2013-04-10 13:59:32 +0000 | [diff] [blame] | 205 | CHECK_SMALL_REGION(p, sizeof(*p), true); |
| 206 | *p = x; |
| 207 | } |
| 208 | |
| 209 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE |
Evgeniy Stepanov | 2e9ffcb | 2013-06-04 13:49:10 +0000 | [diff] [blame] | 210 | void __sanitizer_unaligned_store32(uu32 *p, u32 x) { |
Kostya Serebryany | dc0d179 | 2013-04-10 13:59:32 +0000 | [diff] [blame] | 211 | CHECK_SMALL_REGION(p, sizeof(*p), true); |
| 212 | *p = x; |
| 213 | } |
| 214 | |
| 215 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE |
Evgeniy Stepanov | 2e9ffcb | 2013-06-04 13:49:10 +0000 | [diff] [blame] | 216 | void __sanitizer_unaligned_store64(uu64 *p, u64 x) { |
Kostya Serebryany | dc0d179 | 2013-04-10 13:59:32 +0000 | [diff] [blame] | 217 | CHECK_SMALL_REGION(p, sizeof(*p), true); |
| 218 | *p = x; |
| 219 | } |
| 220 | |
Alexey Samsonov | d4b5db8 | 2012-12-04 01:38:15 +0000 | [diff] [blame] | 221 | // This is a simplified version of __asan_(un)poison_memory_region, which |
| 222 | // assumes that left border of region to be poisoned is properly aligned. |
| 223 | static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { |
| 224 | if (size == 0) return; |
| 225 | uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1); |
| 226 | PoisonShadow(addr, aligned_size, |
| 227 | do_poison ? kAsanStackUseAfterScopeMagic : 0); |
| 228 | if (size == aligned_size) |
| 229 | return; |
| 230 | s8 end_offset = (s8)(size - aligned_size); |
| 231 | s8* shadow_end = (s8*)MemToShadow(addr + aligned_size); |
| 232 | s8 end_value = *shadow_end; |
| 233 | if (do_poison) { |
| 234 | // If possible, mark all the bytes mapping to last shadow byte as |
| 235 | // unaddressable. |
| 236 | if (end_value > 0 && end_value <= end_offset) |
Kostya Serebryany | bc9940e | 2012-12-14 12:15:09 +0000 | [diff] [blame] | 237 | *shadow_end = (s8)kAsanStackUseAfterScopeMagic; |
Alexey Samsonov | d4b5db8 | 2012-12-04 01:38:15 +0000 | [diff] [blame] | 238 | } else { |
| 239 | // If necessary, mark few first bytes mapping to last shadow byte |
| 240 | // as addressable |
| 241 | if (end_value != 0) |
| 242 | *shadow_end = Max(end_value, end_offset); |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | void __asan_poison_stack_memory(uptr addr, uptr size) { |
| 247 | if (flags()->verbosity > 0) |
| 248 | Report("poisoning: %p %zx\n", (void*)addr, size); |
| 249 | PoisonAlignedStackMemory(addr, size, true); |
| 250 | } |
| 251 | |
| 252 | void __asan_unpoison_stack_memory(uptr addr, uptr size) { |
| 253 | if (flags()->verbosity > 0) |
| 254 | Report("unpoisoning: %p %zx\n", (void*)addr, size); |
| 255 | PoisonAlignedStackMemory(addr, size, false); |
| 256 | } |