1//===-- asan_poisoning.cpp ------------------------------------------------===//
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// Shadow memory poisoning by ASan RTL and by user application.
12//===----------------------------------------------------------------------===//
13
14#include "asan_poisoning.h"
15
16#include "asan_report.h"
17#include "asan_stack.h"
18#include "sanitizer_common/sanitizer_atomic.h"
19#include "sanitizer_common/sanitizer_flags.h"
20#include "sanitizer_common/sanitizer_interface_internal.h"
21#include "sanitizer_common/sanitizer_internal_defs.h"
22#include "sanitizer_common/sanitizer_libc.h"
23#include "sanitizer_common/sanitizer_ring_buffer.h"
24#include "sanitizer_common/sanitizer_stackdepot.h"
25
26namespace __asan {
27
28using PoisonRecordRingBuffer = RingBuffer<PoisonRecord>;
29
30static atomic_uint8_t can_poison_memory;
31
32static Mutex poison_records_mutex;
33static PoisonRecordRingBuffer *poison_records
34 SANITIZER_GUARDED_BY(poison_records_mutex) = nullptr;
35
36void AddPoisonRecord(const PoisonRecord &new_record) {
37 if (flags()->poison_history_size <= 0)
38 return;
39
40 GenericScopedLock<Mutex> l(&poison_records_mutex);
41
42 if (poison_records == nullptr)
43 poison_records = PoisonRecordRingBuffer::New(Size: flags()->poison_history_size);
44
45 poison_records->push(t: new_record);
46}
47
48bool FindPoisonRecord(uptr addr, PoisonRecord& match, bool& is_full) {
49 if (flags()->poison_history_size <= 0)
50 return false;
51
52 GenericScopedLock<Mutex> l(&poison_records_mutex);
53
54 const uptr records_count = poison_records ? poison_records->size() : 0;
55 is_full = records_count >= static_cast<uptr>(flags()->poison_history_size);
56
57 for (uptr i = 0; i < records_count; i++) {
58 PoisonRecord record = (*poison_records)[i];
59 if (record.begin <= addr && addr < record.end) {
60 internal_memcpy(dest: &match, src: &record, n: sizeof(record));
61 return true;
62 }
63 }
64
65 return false;
66}
67
68void SANITIZER_ACQUIRE(poison_records_mutex) AcquirePoisonRecords() {
69 poison_records_mutex.Lock();
70}
71
72void SANITIZER_RELEASE(poison_records_mutex) ReleasePoisonRecords() {
73 poison_records_mutex.Unlock();
74}
75
76void SetCanPoisonMemory(bool value) {
77 atomic_store(a: &can_poison_memory, v: value, mo: memory_order_release);
78}
79
80bool CanPoisonMemory() {
81 return atomic_load(a: &can_poison_memory, mo: memory_order_acquire);
82}
83
84void PoisonShadow(uptr addr, uptr size, u8 value) {
85 if (value && !CanPoisonMemory()) return;
86 CHECK(AddrIsAlignedByGranularity(addr));
87 CHECK(AddrIsInMem(addr));
88 CHECK(AddrIsAlignedByGranularity(addr + size));
89 CHECK(AddrIsInMem(addr + size - ASAN_SHADOW_GRANULARITY));
90 CHECK(REAL(memset));
91 FastPoisonShadow(aligned_beg: addr, aligned_size: size, value);
92}
93
94void PoisonShadowPartialRightRedzone(uptr addr,
95 uptr size,
96 uptr redzone_size,
97 u8 value) {
98 if (!CanPoisonMemory()) return;
99 CHECK(AddrIsAlignedByGranularity(addr));
100 CHECK(AddrIsInMem(addr));
101 FastPoisonShadowPartialRightRedzone(aligned_addr: addr, size, redzone_size, value);
102}
103
104struct ShadowSegmentEndpoint {
105 u8 *chunk;
106 s8 offset; // in [0, ASAN_SHADOW_GRANULARITY)
107 s8 value; // = *chunk;
108
109 explicit ShadowSegmentEndpoint(uptr address) {
110 chunk = (u8*)MemToShadow(p: address);
111 offset = address & (ASAN_SHADOW_GRANULARITY - 1);
112 value = *chunk;
113 }
114};
115
116void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
117 uptr end = ptr + size;
118 if (Verbosity()) {
119 Printf(format: "__asan_%spoison_intra_object_redzone [%p,%p) %zd\n",
120 poison ? "" : "un", (void *)ptr, (void *)end, size);
121 if (Verbosity() >= 2)
122 PRINT_CURRENT_STACK();
123 }
124 CHECK(size);
125 CHECK_LE(size, 4096);
126 CHECK(IsAligned(end, ASAN_SHADOW_GRANULARITY));
127 if (!IsAligned(a: ptr, ASAN_SHADOW_GRANULARITY)) {
128 *(u8 *)MemToShadow(p: ptr) =
129 poison ? static_cast<u8>(ptr % ASAN_SHADOW_GRANULARITY) : 0;
130 ptr |= ASAN_SHADOW_GRANULARITY - 1;
131 ptr++;
132 }
133 for (; ptr < end; ptr += ASAN_SHADOW_GRANULARITY)
134 *(u8*)MemToShadow(p: ptr) = poison ? kAsanIntraObjectRedzone : 0;
135}
136
137} // namespace __asan
138
139// ---------------------- Interface ---------------- {{{1
140using namespace __asan;
141
142static void RecordPoison(uptr beg_addr, uptr end_addr) {
143 if (LIKELY(beg_addr >= end_addr || flags()->poison_history_size == 0))
144 return;
145 GET_STACK_TRACE(/*max_size=*/16, /*fast=*/false);
146 u32 current_tid = GetCurrentTidOrInvalid();
147
148 u32 stack_id = StackDepotPut(stack);
149
150 PoisonRecord record;
151 record.stack_id = stack_id;
152 record.thread_id = current_tid;
153 record.begin = beg_addr;
154 record.end = end_addr;
155 AddPoisonRecord(new_record: record);
156}
157
158// Current implementation of __asan_(un)poison_memory_region doesn't check
159// that user program (un)poisons the memory it owns. It poisons memory
160// conservatively, and unpoisons progressively to make sure asan shadow
161// mapping invariant is preserved (see detailed mapping description here:
162// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm).
163//
164// * if user asks to poison region [left, right), the program poisons
165// at least [left, AlignDown(right)).
166// * if user asks to unpoison region [left, right), the program unpoisons
167// at most [AlignDown(left), right).
168void __asan_poison_memory_region(void const volatile *addr, uptr size) {
169 if (!flags()->allow_user_poisoning || size == 0) return;
170 uptr beg_addr = (uptr)addr;
171 uptr end_addr = beg_addr + size;
172 VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
173 (void *)end_addr);
174
175 RecordPoison(beg_addr, end_addr);
176
177 ShadowSegmentEndpoint beg(beg_addr);
178 ShadowSegmentEndpoint end(end_addr);
179 if (beg.chunk == end.chunk) {
180 CHECK_LT(beg.offset, end.offset);
181 s8 value = beg.value;
182 CHECK_EQ(value, end.value);
183 // We can only poison memory if the byte in end.offset is unaddressable.
184 // No need to re-poison memory if it is poisoned already.
185 if (value > 0 && value <= end.offset) {
186 if (beg.offset > 0) {
187 *beg.chunk = Min(a: value, b: beg.offset);
188 } else {
189 *beg.chunk = kAsanUserPoisonedMemoryMagic;
190 }
191 }
192 return;
193 }
194 CHECK_LT(beg.chunk, end.chunk);
195 if (beg.offset > 0) {
196 // Mark bytes from beg.offset as unaddressable.
197 if (beg.value == 0) {
198 *beg.chunk = beg.offset;
199 } else {
200 *beg.chunk = Min(a: beg.value, b: beg.offset);
201 }
202 beg.chunk++;
203 }
204 REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
205 // Poison if byte in end.offset is unaddressable.
206 if (end.value > 0 && end.value <= end.offset) {
207 *end.chunk = kAsanUserPoisonedMemoryMagic;
208 }
209}
210
211void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
212 if (!flags()->allow_user_poisoning || size == 0) return;
213 uptr beg_addr = (uptr)addr;
214 uptr end_addr = beg_addr + size;
215 VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
216 (void *)end_addr);
217
218 // Note: we don't need to update the poison tracking here. Since the shadow
219 // memory will be unpoisoned, the poison tracking ring buffer entries will be
220 // ignored.
221
222 ShadowSegmentEndpoint beg(beg_addr);
223 ShadowSegmentEndpoint end(end_addr);
224 if (beg.chunk == end.chunk) {
225 CHECK_LT(beg.offset, end.offset);
226 s8 value = beg.value;
227 CHECK_EQ(value, end.value);
228 // We unpoison memory bytes up to enbytes up to end.offset if it is not
229 // unpoisoned already.
230 if (value != 0) {
231 *beg.chunk = Max(a: value, b: end.offset);
232 }
233 return;
234 }
235 CHECK_LT(beg.chunk, end.chunk);
236 REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk);
237 if (end.offset > 0 && end.value != 0) {
238 *end.chunk = Max(a: end.value, b: end.offset);
239 }
240}
241
242int __asan_address_is_poisoned(void const volatile *addr) {
243 return __asan::AddressIsPoisoned(a: (uptr)addr);
244}
245
246uptr __asan_region_is_poisoned(uptr beg, uptr size) {
247 if (!size)
248 return 0;
249 uptr last = beg + size - 1;
250 if (!AddrIsInMem(a: beg))
251 return beg;
252 if (!AddrIsInMem(a: last))
253 return last;
254 CHECK_LE(beg, last);
255 // First check the last application byte, i.e. last granule, then check
256 // the ASAN_SHADOW_GRANULARITY-aligned region by calling mem_is_zero
257 // on the corresponding shadow (first granule is fully checked).
258 if (!__asan::AddressIsPoisoned(a: last)) {
259 uptr aligned_b = RoundDownTo(x: beg, ASAN_SHADOW_GRANULARITY);
260 uptr aligned_e = RoundDownTo(x: last, ASAN_SHADOW_GRANULARITY);
261 if (aligned_b == aligned_e) // one granule case => last check is enough.
262 return 0;
263 CHECK_LT(aligned_b, aligned_e);
264 uptr shadow_beg = MemToShadow(p: aligned_b);
265 uptr shadow_end = MemToShadow(p: aligned_e);
266 CHECK_LT(shadow_beg, shadow_end);
267 if (__sanitizer::mem_is_zero(mem: (const char*)shadow_beg,
268 size: shadow_end - shadow_beg))
269 return 0;
270 }
271 // The fast check failed, so we have a poisoned byte somewhere.
272 // Find it slowly.
273 for (; beg <= last; beg++)
274 if (__asan::AddressIsPoisoned(a: beg))
275 return beg;
276 UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found");
277 return 0;
278}
279
280#define CHECK_SMALL_REGION(p, size, isWrite) \
281 do { \
282 uptr __p = reinterpret_cast<uptr>(p); \
283 uptr __size = size; \
284 if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \
285 __asan::AddressIsPoisoned(__p + __size - 1))) { \
286 GET_CURRENT_PC_BP_SP; \
287 uptr __bad = __asan_region_is_poisoned(__p, __size); \
288 __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\
289 } \
290 } while (false)
291
292
293extern "C" SANITIZER_INTERFACE_ATTRIBUTE
294u16 __sanitizer_unaligned_load16(const uu16 *p) {
295 CHECK_SMALL_REGION(p, sizeof(*p), false);
296 return *p;
297}
298
299extern "C" SANITIZER_INTERFACE_ATTRIBUTE
300u32 __sanitizer_unaligned_load32(const uu32 *p) {
301 CHECK_SMALL_REGION(p, sizeof(*p), false);
302 return *p;
303}
304
305extern "C" SANITIZER_INTERFACE_ATTRIBUTE
306u64 __sanitizer_unaligned_load64(const uu64 *p) {
307 CHECK_SMALL_REGION(p, sizeof(*p), false);
308 return *p;
309}
310
311extern "C" SANITIZER_INTERFACE_ATTRIBUTE
312void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
313 CHECK_SMALL_REGION(p, sizeof(*p), true);
314 *p = x;
315}
316
317extern "C" SANITIZER_INTERFACE_ATTRIBUTE
318void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
319 CHECK_SMALL_REGION(p, sizeof(*p), true);
320 *p = x;
321}
322
323extern "C" SANITIZER_INTERFACE_ATTRIBUTE
324void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
325 CHECK_SMALL_REGION(p, sizeof(*p), true);
326 *p = x;
327}
328
329extern "C" SANITIZER_INTERFACE_ATTRIBUTE
330void __asan_poison_cxx_array_cookie(uptr p) {
331 if (SANITIZER_WORDSIZE != 64) return;
332 if (!flags()->poison_array_cookie) return;
333 uptr s = MEM_TO_SHADOW(p);
334 *reinterpret_cast<u8*>(s) = kAsanArrayCookieMagic;
335}
336
337extern "C" SANITIZER_INTERFACE_ATTRIBUTE
338uptr __asan_load_cxx_array_cookie(uptr *p) {
339 if (SANITIZER_WORDSIZE != 64) return *p;
340 if (!flags()->poison_array_cookie) return *p;
341 uptr s = MEM_TO_SHADOW(reinterpret_cast<uptr>(p));
342 u8 sval = *reinterpret_cast<u8*>(s);
343 if (sval == kAsanArrayCookieMagic) return *p;
344 // If sval is not kAsanArrayCookieMagic it can only be freed memory,
345 // which means that we are going to get double-free. So, return 0 to avoid
346 // infinite loop of destructors. We don't want to report a double-free here
347 // though, so print a warning just in case.
348 // CHECK_EQ(sval, kAsanHeapFreeMagic);
349 if (sval == kAsanHeapFreeMagic) {
350 Report(format: "AddressSanitizer: loaded array cookie from free-d memory; "
351 "expect a double-free report\n");
352 return 0;
353 }
354 // The cookie may remain unpoisoned if e.g. it comes from a custom
355 // operator new defined inside a class.
356 return *p;
357}
358
359// This is a simplified version of __asan_(un)poison_memory_region, which
360// assumes that left border of region to be poisoned is properly aligned.
361static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
362 if (size == 0) return;
363 uptr aligned_size = size & ~(ASAN_SHADOW_GRANULARITY - 1);
364 PoisonShadow(addr, size: aligned_size,
365 value: do_poison ? kAsanStackUseAfterScopeMagic : 0);
366 if (size == aligned_size)
367 return;
368 s8 end_offset = (s8)(size - aligned_size);
369 s8* shadow_end = (s8*)MemToShadow(p: addr + aligned_size);
370 s8 end_value = *shadow_end;
371 if (do_poison) {
372 // If possible, mark all the bytes mapping to last shadow byte as
373 // unaddressable.
374 if (end_value > 0 && end_value <= end_offset)
375 *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
376 } else {
377 // If necessary, mark few first bytes mapping to last shadow byte
378 // as addressable
379 if (end_value != 0)
380 *shadow_end = Max(a: end_value, b: end_offset);
381 }
382}
383
384void __asan_set_shadow_00(uptr addr, uptr size) {
385 REAL(memset)((void *)addr, 0, size);
386}
387
388void __asan_set_shadow_01(uptr addr, uptr size) {
389 REAL(memset)((void *)addr, 0x01, size);
390}
391
392void __asan_set_shadow_02(uptr addr, uptr size) {
393 REAL(memset)((void *)addr, 0x02, size);
394}
395
396void __asan_set_shadow_03(uptr addr, uptr size) {
397 REAL(memset)((void *)addr, 0x03, size);
398}
399
400void __asan_set_shadow_04(uptr addr, uptr size) {
401 REAL(memset)((void *)addr, 0x04, size);
402}
403
404void __asan_set_shadow_05(uptr addr, uptr size) {
405 REAL(memset)((void *)addr, 0x05, size);
406}
407
408void __asan_set_shadow_06(uptr addr, uptr size) {
409 REAL(memset)((void *)addr, 0x06, size);
410}
411
412void __asan_set_shadow_07(uptr addr, uptr size) {
413 REAL(memset)((void *)addr, 0x07, size);
414}
415
416void __asan_set_shadow_f1(uptr addr, uptr size) {
417 REAL(memset)((void *)addr, 0xf1, size);
418}
419
420void __asan_set_shadow_f2(uptr addr, uptr size) {
421 REAL(memset)((void *)addr, 0xf2, size);
422}
423
424void __asan_set_shadow_f3(uptr addr, uptr size) {
425 REAL(memset)((void *)addr, 0xf3, size);
426}
427
428void __asan_set_shadow_f5(uptr addr, uptr size) {
429 REAL(memset)((void *)addr, 0xf5, size);
430}
431
432void __asan_set_shadow_f8(uptr addr, uptr size) {
433 REAL(memset)((void *)addr, 0xf8, size);
434}
435
436void __asan_poison_stack_memory(uptr addr, uptr size) {
437 VReport(1, "poisoning: %p %zx\n", (void *)addr, size);
438 PoisonAlignedStackMemory(addr, size, do_poison: true);
439}
440
441void __asan_unpoison_stack_memory(uptr addr, uptr size) {
442 VReport(1, "unpoisoning: %p %zx\n", (void *)addr, size);
443 PoisonAlignedStackMemory(addr, size, do_poison: false);
444}
445
446static void FixUnalignedStorage(uptr storage_beg, uptr storage_end,
447 uptr &old_beg, uptr &old_end, uptr &new_beg,
448 uptr &new_end) {
449 constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
450 if (UNLIKELY(!AddrIsAlignedByGranularity(storage_end))) {
451 uptr end_down = RoundDownTo(x: storage_end, boundary: granularity);
452 // Ignore the last unaligned granule if the storage is followed by
453 // unpoisoned byte, because we can't poison the prefix anyway. Don't call
454 // AddressIsPoisoned at all if container changes does not affect the last
455 // granule at all.
456 if ((((old_end != new_end) && Max(a: old_end, b: new_end) > end_down) ||
457 ((old_beg != new_beg) && Max(a: old_beg, b: new_beg) > end_down)) &&
458 !AddressIsPoisoned(a: storage_end)) {
459 old_beg = Min(a: end_down, b: old_beg);
460 old_end = Min(a: end_down, b: old_end);
461 new_beg = Min(a: end_down, b: new_beg);
462 new_end = Min(a: end_down, b: new_end);
463 }
464 }
465
466 // Handle misaligned begin and cut it off.
467 if (UNLIKELY(!AddrIsAlignedByGranularity(storage_beg))) {
468 uptr beg_up = RoundUpTo(size: storage_beg, boundary: granularity);
469 // The first unaligned granule needs special handling only if we had bytes
470 // there before and will have none after.
471 if ((new_beg == new_end || new_beg >= beg_up) && old_beg != old_end &&
472 old_beg < beg_up) {
473 // Keep granule prefix outside of the storage unpoisoned.
474 uptr beg_down = RoundDownTo(x: storage_beg, boundary: granularity);
475 *(u8 *)MemToShadow(p: beg_down) = storage_beg - beg_down;
476 old_beg = Max(a: beg_up, b: old_beg);
477 old_end = Max(a: beg_up, b: old_end);
478 new_beg = Max(a: beg_up, b: new_beg);
479 new_end = Max(a: beg_up, b: new_end);
480 }
481 }
482}
483
484void __sanitizer_annotate_contiguous_container(const void *beg_p,
485 const void *end_p,
486 const void *old_mid_p,
487 const void *new_mid_p) {
488 if (!flags()->detect_container_overflow)
489 return;
490 VPrintf(3, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p,
491 new_mid_p);
492 uptr storage_beg = reinterpret_cast<uptr>(beg_p);
493 uptr storage_end = reinterpret_cast<uptr>(end_p);
494 uptr old_end = reinterpret_cast<uptr>(old_mid_p);
495 uptr new_end = reinterpret_cast<uptr>(new_mid_p);
496 uptr old_beg = storage_beg;
497 uptr new_beg = storage_beg;
498 uptr granularity = ASAN_SHADOW_GRANULARITY;
499 if (!(storage_beg <= old_end && storage_beg <= new_end &&
500 old_end <= storage_end && new_end <= storage_end)) {
501 GET_STACK_TRACE_FATAL_HERE;
502 ReportBadParamsToAnnotateContiguousContainer(beg: storage_beg, end: storage_end,
503 old_mid: old_end, new_mid: new_end, stack: &stack);
504 }
505 CHECK_LE(storage_end - storage_beg,
506 FIRST_32_SECOND_64(1UL << 30, 1ULL << 40)); // Sanity check.
507
508 if (old_end == new_end)
509 return; // Nothing to do here.
510
511 RecordPoison(beg_addr: new_end, end_addr: old_end);
512
513 FixUnalignedStorage(storage_beg, storage_end, old_beg, old_end, new_beg,
514 new_end);
515
516 uptr a = RoundDownTo(x: Min(a: old_end, b: new_end), boundary: granularity);
517 uptr c = RoundUpTo(size: Max(a: old_end, b: new_end), boundary: granularity);
518 uptr d1 = RoundDownTo(x: old_end, boundary: granularity);
519 // uptr d2 = RoundUpTo(old_mid, granularity);
520 // Currently we should be in this state:
521 // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good.
522 // Make a quick sanity check that we are indeed in this state.
523 //
524 // FIXME: Two of these three checks are disabled until we fix
525 // https://github.com/google/sanitizers/issues/258.
526 // if (d1 != d2)
527 // DCHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
528 //
529 // NOTE: curly brackets for the "if" below to silence a MSVC warning.
530 if (a + granularity <= d1) {
531 DCHECK_EQ(*(u8 *)MemToShadow(a), 0);
532 }
533 // if (d2 + granularity <= c && c <= end)
534 // DCHECK_EQ(*(u8 *)MemToShadow(c - granularity),
535 // kAsanContiguousContainerOOBMagic);
536
537 uptr b1 = RoundDownTo(x: new_end, boundary: granularity);
538 uptr b2 = RoundUpTo(size: new_end, boundary: granularity);
539 // New state:
540 // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good.
541 if (b1 > a)
542 PoisonShadow(addr: a, size: b1 - a, value: 0);
543 else if (c > b2)
544 PoisonShadow(addr: b2, size: c - b2, value: kAsanContiguousContainerOOBMagic);
545 if (b1 != b2) {
546 CHECK_EQ(b2 - b1, granularity);
547 *(u8 *)MemToShadow(p: b1) = static_cast<u8>(new_end - b1);
548 }
549}
550
551// Annotates a double ended contiguous memory area like std::deque's chunk.
552// It allows detecting buggy accesses to allocated but not used begining
553// or end items of such a container.
554void __sanitizer_annotate_double_ended_contiguous_container(
555 const void *storage_beg_p, const void *storage_end_p,
556 const void *old_container_beg_p, const void *old_container_end_p,
557 const void *new_container_beg_p, const void *new_container_end_p) {
558 if (!flags()->detect_container_overflow)
559 return;
560
561 VPrintf(3, "contiguous_container: %p %p %p %p %p %p\n", storage_beg_p,
562 storage_end_p, old_container_beg_p, old_container_end_p,
563 new_container_beg_p, new_container_end_p);
564
565 uptr storage_beg = reinterpret_cast<uptr>(storage_beg_p);
566 uptr storage_end = reinterpret_cast<uptr>(storage_end_p);
567 uptr old_beg = reinterpret_cast<uptr>(old_container_beg_p);
568 uptr old_end = reinterpret_cast<uptr>(old_container_end_p);
569 uptr new_beg = reinterpret_cast<uptr>(new_container_beg_p);
570 uptr new_end = reinterpret_cast<uptr>(new_container_end_p);
571
572 constexpr uptr granularity = ASAN_SHADOW_GRANULARITY;
573
574 if (!(old_beg <= old_end && new_beg <= new_end) ||
575 !(storage_beg <= new_beg && new_end <= storage_end) ||
576 !(storage_beg <= old_beg && old_end <= storage_end)) {
577 GET_STACK_TRACE_FATAL_HERE;
578 ReportBadParamsToAnnotateDoubleEndedContiguousContainer(
579 storage_beg, storage_end, old_container_beg: old_beg, old_container_end: old_end, new_container_beg: new_beg, new_container_end: new_end, stack: &stack);
580 }
581 CHECK_LE(storage_end - storage_beg,
582 FIRST_32_SECOND_64(1UL << 30, 1ULL << 40)); // Sanity check.
583
584 if ((old_beg == old_end && new_beg == new_end) ||
585 (old_beg == new_beg && old_end == new_end))
586 return; // Nothing to do here.
587
588 RecordPoison(beg_addr: old_beg, end_addr: new_beg);
589 RecordPoison(beg_addr: new_end, end_addr: old_end);
590
591 FixUnalignedStorage(storage_beg, storage_end, old_beg, old_end, new_beg,
592 new_end);
593
594 // Handle non-intersecting new/old containers separately have simpler
595 // intersecting case.
596 if (old_beg == old_end || new_beg == new_end || new_end <= old_beg ||
597 old_end <= new_beg) {
598 if (old_beg != old_end) {
599 // Poisoning the old container.
600 uptr a = RoundDownTo(x: old_beg, boundary: granularity);
601 uptr b = RoundUpTo(size: old_end, boundary: granularity);
602 PoisonShadow(addr: a, size: b - a, value: kAsanContiguousContainerOOBMagic);
603 }
604
605 if (new_beg != new_end) {
606 // Unpoisoning the new container.
607 uptr a = RoundDownTo(x: new_beg, boundary: granularity);
608 uptr b = RoundDownTo(x: new_end, boundary: granularity);
609 PoisonShadow(addr: a, size: b - a, value: 0);
610 if (!AddrIsAlignedByGranularity(a: new_end))
611 *(u8 *)MemToShadow(p: b) = static_cast<u8>(new_end - b);
612 }
613
614 return;
615 }
616
617 // Intersection of old and new containers is not empty.
618 CHECK_LT(new_beg, old_end);
619 CHECK_GT(new_end, old_beg);
620
621 if (new_beg < old_beg) {
622 // Round down because we can't poison prefixes.
623 uptr a = RoundDownTo(x: new_beg, boundary: granularity);
624 // Round down and ignore the [c, old_beg) as its state defined by unchanged
625 // [old_beg, old_end).
626 uptr c = RoundDownTo(x: old_beg, boundary: granularity);
627 PoisonShadow(addr: a, size: c - a, value: 0);
628 } else if (new_beg > old_beg) {
629 // Round down and poison [a, old_beg) because it was unpoisoned only as a
630 // prefix.
631 uptr a = RoundDownTo(x: old_beg, boundary: granularity);
632 // Round down and ignore the [c, new_beg) as its state defined by unchanged
633 // [new_beg, old_end).
634 uptr c = RoundDownTo(x: new_beg, boundary: granularity);
635
636 PoisonShadow(addr: a, size: c - a, value: kAsanContiguousContainerOOBMagic);
637 }
638
639 if (new_end > old_end) {
640 // Round down to poison the prefix.
641 uptr a = RoundDownTo(x: old_end, boundary: granularity);
642 // Round down and handle remainder below.
643 uptr c = RoundDownTo(x: new_end, boundary: granularity);
644 PoisonShadow(addr: a, size: c - a, value: 0);
645 if (!AddrIsAlignedByGranularity(a: new_end))
646 *(u8 *)MemToShadow(p: c) = static_cast<u8>(new_end - c);
647 } else if (new_end < old_end) {
648 // Round up and handle remained below.
649 uptr a2 = RoundUpTo(size: new_end, boundary: granularity);
650 // Round up to poison entire granule as we had nothing in [old_end, c2).
651 uptr c2 = RoundUpTo(size: old_end, boundary: granularity);
652 PoisonShadow(addr: a2, size: c2 - a2, value: kAsanContiguousContainerOOBMagic);
653
654 if (!AddrIsAlignedByGranularity(a: new_end)) {
655 uptr a = RoundDownTo(x: new_end, boundary: granularity);
656 *(u8 *)MemToShadow(p: a) = static_cast<u8>(new_end - a);
657 }
658 }
659}
660
661static const void *FindBadAddress(uptr begin, uptr end, bool poisoned) {
662 CHECK_LE(begin, end);
663 constexpr uptr kMaxRangeToCheck = 32;
664 if (end - begin > kMaxRangeToCheck * 2) {
665 if (auto *bad = FindBadAddress(begin, end: begin + kMaxRangeToCheck, poisoned))
666 return bad;
667 if (auto *bad = FindBadAddress(begin: end - kMaxRangeToCheck, end, poisoned))
668 return bad;
669 }
670
671 for (uptr i = begin; i < end; ++i)
672 if (AddressIsPoisoned(a: i) != poisoned)
673 return reinterpret_cast<const void *>(i);
674 return nullptr;
675}
676
677const void *__sanitizer_contiguous_container_find_bad_address(
678 const void *beg_p, const void *mid_p, const void *end_p) {
679 if (!flags()->detect_container_overflow)
680 return nullptr;
681 uptr granularity = ASAN_SHADOW_GRANULARITY;
682 uptr beg = reinterpret_cast<uptr>(beg_p);
683 uptr end = reinterpret_cast<uptr>(end_p);
684 uptr mid = reinterpret_cast<uptr>(mid_p);
685 CHECK_LE(beg, mid);
686 CHECK_LE(mid, end);
687 // If the byte after the storage is unpoisoned, everything in the granule
688 // before must stay unpoisoned.
689 uptr annotations_end =
690 (!AddrIsAlignedByGranularity(a: end) && !AddressIsPoisoned(a: end))
691 ? RoundDownTo(x: end, boundary: granularity)
692 : end;
693 beg = Min(a: beg, b: annotations_end);
694 mid = Min(a: mid, b: annotations_end);
695 if (auto *bad = FindBadAddress(begin: beg, end: mid, poisoned: false))
696 return bad;
697 if (auto *bad = FindBadAddress(begin: mid, end: annotations_end, poisoned: true))
698 return bad;
699 return FindBadAddress(begin: annotations_end, end, poisoned: false);
700}
701
702int __sanitizer_verify_contiguous_container(const void *beg_p,
703 const void *mid_p,
704 const void *end_p) {
705 return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p,
706 end_p) == nullptr;
707}
708
709const void *__sanitizer_double_ended_contiguous_container_find_bad_address(
710 const void *storage_beg_p, const void *container_beg_p,
711 const void *container_end_p, const void *storage_end_p) {
712 if (!flags()->detect_container_overflow)
713 return nullptr;
714 uptr granularity = ASAN_SHADOW_GRANULARITY;
715 uptr storage_beg = reinterpret_cast<uptr>(storage_beg_p);
716 uptr storage_end = reinterpret_cast<uptr>(storage_end_p);
717 uptr beg = reinterpret_cast<uptr>(container_beg_p);
718 uptr end = reinterpret_cast<uptr>(container_end_p);
719
720 // The prefix of the firs granule of the container is unpoisoned.
721 if (beg != end)
722 beg = Max(a: storage_beg, b: RoundDownTo(x: beg, boundary: granularity));
723
724 // If the byte after the storage is unpoisoned, the prefix of the last granule
725 // is unpoisoned.
726 uptr annotations_end = (!AddrIsAlignedByGranularity(a: storage_end) &&
727 !AddressIsPoisoned(a: storage_end))
728 ? RoundDownTo(x: storage_end, boundary: granularity)
729 : storage_end;
730 storage_beg = Min(a: storage_beg, b: annotations_end);
731 beg = Min(a: beg, b: annotations_end);
732 end = Min(a: end, b: annotations_end);
733
734 if (auto *bad = FindBadAddress(begin: storage_beg, end: beg, poisoned: true))
735 return bad;
736 if (auto *bad = FindBadAddress(begin: beg, end, poisoned: false))
737 return bad;
738 if (auto *bad = FindBadAddress(begin: end, end: annotations_end, poisoned: true))
739 return bad;
740 return FindBadAddress(begin: annotations_end, end: storage_end, poisoned: false);
741}
742
743int __sanitizer_verify_double_ended_contiguous_container(
744 const void *storage_beg_p, const void *container_beg_p,
745 const void *container_end_p, const void *storage_end_p) {
746 return __sanitizer_double_ended_contiguous_container_find_bad_address(
747 storage_beg_p, container_beg_p, container_end_p, storage_end_p) ==
748 nullptr;
749}
750
751extern "C" SANITIZER_INTERFACE_ATTRIBUTE
752void __asan_poison_intra_object_redzone(uptr ptr, uptr size) {
753 AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, poison: true);
754}
755
756extern "C" SANITIZER_INTERFACE_ATTRIBUTE
757void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) {
758 AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, poison: false);
759}
760
761// --- Implementation of LSan-specific functions --- {{{1
762namespace __lsan {
763bool WordIsPoisoned(uptr addr) {
764 return (__asan_region_is_poisoned(beg: addr, size: sizeof(uptr)) != 0);
765}
766}
767