blob: 59309535d88e5cb88d6f65b613dce59da999dd94 [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"
Kostya Serebryany1e172b42011-11-30 01:07:02 +000017
Kostya Serebryany1e172b42011-11-30 01:07:02 +000018namespace __asan {
19
Kostya Serebryanyee392552012-05-31 15:02:07 +000020void PoisonShadow(uptr addr, uptr size, u8 value) {
Kostya Serebryany73bad812012-12-20 11:54:21 +000021 if (!flags()->poison_heap) return;
Kostya Serebryany218a9b72011-11-30 18:50:23 +000022 CHECK(AddrIsAlignedByGranularity(addr));
Alexey Samsonov7e843492013-03-28 15:42:43 +000023 CHECK(AddrIsInMem(addr));
Kostya Serebryany218a9b72011-11-30 18:50:23 +000024 CHECK(AddrIsAlignedByGranularity(addr + size));
Alexey Samsonov7e843492013-03-28 15:42:43 +000025 CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY));
Kostya Serebryanya27bdf72013-04-05 14:40:25 +000026 CHECK(REAL(memset));
Alexey Samsonov7e843492013-03-28 15:42:43 +000027 FastPoisonShadow(addr, size, value);
Kostya Serebryany218a9b72011-11-30 18:50:23 +000028}
29
Kostya Serebryany3f4c3872012-05-31 14:35:53 +000030void PoisonShadowPartialRightRedzone(uptr addr,
31 uptr size,
32 uptr redzone_size,
Kostya Serebryanyee392552012-05-31 15:02:07 +000033 u8 value) {
Kostya Serebryany73bad812012-12-20 11:54:21 +000034 if (!flags()->poison_heap) return;
Kostya Serebryany218a9b72011-11-30 18:50:23 +000035 CHECK(AddrIsAlignedByGranularity(addr));
Alexey Samsonov7e843492013-03-28 15:42:43 +000036 CHECK(AddrIsInMem(addr));
37 FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value);
Kostya Serebryany218a9b72011-11-30 18:50:23 +000038}
39
Kostya Serebryany1e172b42011-11-30 01:07:02 +000040struct ShadowSegmentEndpoint {
Kostya Serebryanyee392552012-05-31 15:02:07 +000041 u8 *chunk;
42 s8 offset; // in [0, SHADOW_GRANULARITY)
43 s8 value; // = *chunk;
Kostya Serebryany1e172b42011-11-30 01:07:02 +000044
Kostya Serebryany3f4c3872012-05-31 14:35:53 +000045 explicit ShadowSegmentEndpoint(uptr address) {
Kostya Serebryanyee392552012-05-31 15:02:07 +000046 chunk = (u8*)MemToShadow(address);
Kostya Serebryany1e172b42011-11-30 01:07:02 +000047 offset = address & (SHADOW_GRANULARITY - 1);
48 value = *chunk;
49 }
50};
51
52} // namespace __asan
53
54// ---------------------- Interface ---------------- {{{1
55using 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 Serebryany9aead372012-05-31 14:11:07 +000067void __asan_poison_memory_region(void const volatile *addr, uptr size) {
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +000068 if (!flags()->allow_user_poisoning || size == 0) return;
Kostya Serebryany3f4c3872012-05-31 14:35:53 +000069 uptr beg_addr = (uptr)addr;
70 uptr end_addr = beg_addr + size;
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +000071 if (flags()->verbosity >= 1) {
Alexey Samsonov5bcca4e2012-06-06 10:46:00 +000072 Printf("Trying to poison memory region [%p, %p)\n",
73 (void*)beg_addr, (void*)end_addr);
Kostya Serebryany1e172b42011-11-30 01:07:02 +000074 }
75 ShadowSegmentEndpoint beg(beg_addr);
76 ShadowSegmentEndpoint end(end_addr);
77 if (beg.chunk == end.chunk) {
78 CHECK(beg.offset < end.offset);
Kostya Serebryanyee392552012-05-31 15:02:07 +000079 s8 value = beg.value;
Kostya Serebryany1e172b42011-11-30 01:07:02 +000080 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 Serebryany2d8b3bd2011-12-02 18:42:04 +000085 *beg.chunk = Min(value, beg.offset);
Kostya Serebryany1e172b42011-11-30 01:07:02 +000086 } 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 Serebryany2d8b3bd2011-12-02 18:42:04 +000098 *beg.chunk = Min(beg.value, beg.offset);
Kostya Serebryany1e172b42011-11-30 01:07:02 +000099 }
100 beg.chunk++;
101 }
Alexey Samsonov09672ca2012-02-08 13:45:31 +0000102 REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000103 // 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 Serebryany9aead372012-05-31 14:11:07 +0000109void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +0000110 if (!flags()->allow_user_poisoning || size == 0) return;
Kostya Serebryany3f4c3872012-05-31 14:35:53 +0000111 uptr beg_addr = (uptr)addr;
112 uptr end_addr = beg_addr + size;
Alexey Samsonovcb8c4dc2012-07-09 14:36:04 +0000113 if (flags()->verbosity >= 1) {
Alexey Samsonov5bcca4e2012-06-06 10:46:00 +0000114 Printf("Trying to unpoison memory region [%p, %p)\n",
115 (void*)beg_addr, (void*)end_addr);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000116 }
117 ShadowSegmentEndpoint beg(beg_addr);
118 ShadowSegmentEndpoint end(end_addr);
119 if (beg.chunk == end.chunk) {
120 CHECK(beg.offset < end.offset);
Kostya Serebryanyee392552012-05-31 15:02:07 +0000121 s8 value = beg.value;
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000122 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 Serebryany2d8b3bd2011-12-02 18:42:04 +0000126 *beg.chunk = Max(value, end.offset);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000127 }
128 return;
129 }
130 CHECK(beg.chunk < end.chunk);
131 if (beg.offset > 0) {
132 *beg.chunk = 0;
133 beg.chunk++;
134 }
Alexey Samsonov09672ca2012-02-08 13:45:31 +0000135 REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000136 if (end.offset > 0 && end.value != 0) {
Kostya Serebryany2d8b3bd2011-12-02 18:42:04 +0000137 *end.chunk = Max(end.value, end.offset);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000138 }
139}
140
141bool __asan_address_is_poisoned(void const volatile *addr) {
Kostya Serebryany3f4c3872012-05-31 14:35:53 +0000142 return __asan::AddressIsPoisoned((uptr)addr);
Kostya Serebryany1e172b42011-11-30 01:07:02 +0000143}
Alexey Samsonovd4b5db82012-12-04 01:38:15 +0000144
Kostya Serebryanyeb280932012-12-28 15:24:16 +0000145uptr __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 Serebryanydc0d1792013-04-10 13:59:32 +0000172#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
185extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000186u16 __sanitizer_unaligned_load16(const uu16 *p) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000187 CHECK_SMALL_REGION(p, sizeof(*p), false);
188 return *p;
189}
190
191extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000192u32 __sanitizer_unaligned_load32(const uu32 *p) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000193 CHECK_SMALL_REGION(p, sizeof(*p), false);
194 return *p;
195}
196
197extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000198u64 __sanitizer_unaligned_load64(const uu64 *p) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000199 CHECK_SMALL_REGION(p, sizeof(*p), false);
200 return *p;
201}
202
203extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000204void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000205 CHECK_SMALL_REGION(p, sizeof(*p), true);
206 *p = x;
207}
208
209extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000210void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000211 CHECK_SMALL_REGION(p, sizeof(*p), true);
212 *p = x;
213}
214
215extern "C" SANITIZER_INTERFACE_ATTRIBUTE
Evgeniy Stepanov2e9ffcb2013-06-04 13:49:10 +0000216void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
Kostya Serebryanydc0d1792013-04-10 13:59:32 +0000217 CHECK_SMALL_REGION(p, sizeof(*p), true);
218 *p = x;
219}
220
Alexey Samsonovd4b5db82012-12-04 01:38:15 +0000221// 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.
223static 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 Serebryanybc9940e2012-12-14 12:15:09 +0000237 *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
Alexey Samsonovd4b5db82012-12-04 01:38:15 +0000238 } 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
246void __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
252void __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}