blob: 0033b61c1bc422dca5bc3b733301c998e4903697 [file] [log] [blame]
Alexey Samsonove5f58952012-06-04 13:50:10 +00001//===-- asan_poisoning.cc -------------------------------------------------===//
Kostya Serebryany1e172b42011-11-30 01:07:02 +00002//
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 Serebryany218a9b72011-11-30 18:50:23 +000012// Shadow memory poisoning by ASan RTL and by user application.
Kostya Serebryany1e172b42011-11-30 01:07:02 +000013//===----------------------------------------------------------------------===//
14
Alexey Samsonov7e843492013-03-28 15:42:43 +000015#include "asan_poisoning.h"
Kostya Serebryanyeb280932012-12-28 15:24:16 +000016#include "sanitizer_common/sanitizer_libc.h"
Dmitry Vyukov6866dba2013-10-15 13:28:51 +000017#include "sanitizer_common/sanitizer_flags.h"
Kostya Serebryany1e172b42011-11-30 01:07:02 +000018
Kostya Serebryany1e172b42011-11-30 01:07:02 +000019namespace __asan {
20
Kostya Serebryanyee392552012-05-31 15:02:07 +000021void PoisonShadow(uptr addr, uptr size, u8 value) {
Kostya Serebryany73bad812012-12-20 11:54:21 +000022 if (!flags()->poison_heap) return;
Kostya Serebryany218a9b72011-11-30 18:50:23 +000023 CHECK(AddrIsAlignedByGranularity(addr));
Alexey Samsonov7e843492013-03-28 15:42:43 +000024 CHECK(AddrIsInMem(addr));
Kostya Serebryany218a9b72011-11-30 18:50:23 +000025 CHECK(AddrIsAlignedByGranularity(addr + size));
Alexey Samsonov7e843492013-03-28 15:42:43 +000026 CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY));
Kostya Serebryanya27bdf72013-04-05 14:40:25 +000027 CHECK(REAL(memset));
Alexey Samsonov7e843492013-03-28 15:42:43 +000028 FastPoisonShadow(addr, size, value);
Kostya Serebryany218a9b72011-11-30 18:50:23 +000029}
30
Kostya Serebryany3f4c3872012-05-31 14:35:53 +000031void PoisonShadowPartialRightRedzone(uptr addr,
32 uptr size,
33 uptr redzone_size,
Kostya Serebryanyee392552012-05-31 15:02:07 +000034 u8 value) {
Kostya Serebryany73bad812012-12-20 11:54:21 +000035 if (!flags()->poison_heap) return;
Kostya Serebryany218a9b72011-11-30 18:50:23 +000036 CHECK(AddrIsAlignedByGranularity(addr));
Alexey Samsonov7e843492013-03-28 15:42:43 +000037 CHECK(AddrIsInMem(addr));
38 FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value);
Kostya Serebryany218a9b72011-11-30 18:50:23 +000039}
40
Kostya Serebryany1e172b42011-11-30 01:07:02 +000041struct ShadowSegmentEndpoint {
Kostya Serebryanyee392552012-05-31 15:02:07 +000042 u8 *chunk;
43 s8 offset; // in [0, SHADOW_GRANULARITY)
44 s8 value; // = *chunk;
Kostya Serebryany1e172b42011-11-30 01:07:02 +000045
Kostya Serebryany3f4c3872012-05-31 14:35:53 +000046 explicit ShadowSegmentEndpoint(uptr address) {
Kostya Serebryanyee392552012-05-31 15:02:07 +000047 chunk = (u8*)MemToShadow(address);
Kostya Serebryany1e172b42011-11-30 01:07:02 +000048 offset = address & (SHADOW_GRANULARITY - 1);
49 value = *chunk;
50 }
51};
52
53} // namespace __asan
54
55// ---------------------- Interface ---------------- {{{1
56using 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 Serebryany9aead372012-05-31 14:11:07 +000068void __asan_poison_memory_region(void const volatile *addr, uptr size) {
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +000069 if (!flags()->allow_user_poisoning || size == 0) return;
Kostya Serebryany3f4c3872012-05-31 14:35:53 +000070 uptr beg_addr = (uptr)addr;
71 uptr end_addr = beg_addr + size;
Dmitry Vyukov6866dba2013-10-15 13:28:51 +000072 if (common_flags()->verbosity >= 1) {
Alexey Samsonov5bcca4e2012-06-06 10:46:00 +000073 Printf("Trying to poison memory region [%p, %p)\n",
74 (void*)beg_addr, (void*)end_addr);
Kostya Serebryany1e172b42011-11-30 01:07:02 +000075 }
76 ShadowSegmentEndpoint beg(beg_addr);
77 ShadowSegmentEndpoint end(end_addr);
78 if (beg.chunk == end.chunk) {
79 CHECK(beg.offset < end.offset);
Kostya Serebryanyee392552012-05-31 15:02:07 +000080 s8 value = beg.value;
Kostya Serebryany1e172b42011-11-30 01:07:02 +000081 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 Serebryany2d8b3bd2011-12-02 18:42:04 +000086 *beg.chunk = Min(value, beg.offset);
Kostya Serebryany1e172b42011-11-30 01:07:02 +000087 } 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 Serebryany2d8b3bd2011-12-02 18:42:04 +000099 *beg.chunk = Min(beg.value, beg.offset);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000100 }
101 beg.chunk++;
102 }
Alexey Samsonov09672ca2012-02-08 13:45:31 +0000103 REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000104 // 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 Serebryany9aead372012-05-31 14:11:07 +0000110void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +0000111 if (!flags()->allow_user_poisoning || size == 0) return;
Kostya Serebryany3f4c3872012-05-31 14:35:53 +0000112 uptr beg_addr = (uptr)addr;
113 uptr end_addr = beg_addr + size;
Dmitry Vyukov6866dba2013-10-15 13:28:51 +0000114 if (common_flags()->verbosity >= 1) {
Alexey Samsonov5bcca4e2012-06-06 10:46:00 +0000115 Printf("Trying to unpoison memory region [%p, %p)\n",
116 (void*)beg_addr, (void*)end_addr);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000117 }
118 ShadowSegmentEndpoint beg(beg_addr);
119 ShadowSegmentEndpoint end(end_addr);
120 if (beg.chunk == end.chunk) {
121 CHECK(beg.offset < end.offset);
Kostya Serebryanyee392552012-05-31 15:02:07 +0000122 s8 value = beg.value;
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000123 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 Serebryany2d8b3bd2011-12-02 18:42:04 +0000127 *beg.chunk = Max(value, end.offset);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000128 }
129 return;
130 }
131 CHECK(beg.chunk < end.chunk);
132 if (beg.offset > 0) {
133 *beg.chunk = 0;
134 beg.chunk++;
135 }
Alexey Samsonov09672ca2012-02-08 13:45:31 +0000136 REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000137 if (end.offset > 0 && end.value != 0) {
Kostya Serebryany2d8b3bd2011-12-02 18:42:04 +0000138 *end.chunk = Max(end.value, end.offset);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000139 }
140}
141
142bool __asan_address_is_poisoned(void const volatile *addr) {
Kostya Serebryany3f4c3872012-05-31 14:35:53 +0000143 return __asan::AddressIsPoisoned((uptr)addr);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000144}
Alexey Samsonovd4b5db82012-12-04 01:38:15 +0000145
Kostya Serebryanyeb280932012-12-28 15:24:16 +0000146uptr __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 Serebryanydc0d1792013-04-10 13:59:32 +0000173#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
186extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000187u16 __sanitizer_unaligned_load16(const uu16 *p) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000188 CHECK_SMALL_REGION(p, sizeof(*p), false);
189 return *p;
190}
191
192extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000193u32 __sanitizer_unaligned_load32(const uu32 *p) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000194 CHECK_SMALL_REGION(p, sizeof(*p), false);
195 return *p;
196}
197
198extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000199u64 __sanitizer_unaligned_load64(const uu64 *p) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000200 CHECK_SMALL_REGION(p, sizeof(*p), false);
201 return *p;
202}
203
204extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000205void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000206 CHECK_SMALL_REGION(p, sizeof(*p), true);
207 *p = x;
208}
209
210extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000211void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000212 CHECK_SMALL_REGION(p, sizeof(*p), true);
213 *p = x;
214}
215
216extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000217void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000218 CHECK_SMALL_REGION(p, sizeof(*p), true);
219 *p = x;
220}
221
Alexey Samsonovd4b5db82012-12-04 01:38:15 +0000222// 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.
224static 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 Serebryanybc9940e2012-12-14 12:15:09 +0000238 *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
Alexey Samsonovd4b5db82012-12-04 01:38:15 +0000239 } 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
247void __asan_poison_stack_memory(uptr addr, uptr size) {
Dmitry Vyukov6866dba2013-10-15 13:28:51 +0000248 if (common_flags()->verbosity > 0)
Alexey Samsonovd4b5db82012-12-04 01:38:15 +0000249 Report("poisoning: %p %zx\n", (void*)addr, size);
250 PoisonAlignedStackMemory(addr, size, true);
251}
252
253void __asan_unpoison_stack_memory(uptr addr, uptr size) {
Dmitry Vyukov6866dba2013-10-15 13:28:51 +0000254 if (common_flags()->verbosity > 0)
Alexey Samsonovd4b5db82012-12-04 01:38:15 +0000255 Report("unpoisoning: %p %zx\n", (void*)addr, size);
256 PoisonAlignedStackMemory(addr, size, false);
257}