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