xref: /freebsd/contrib/llvm-project/compiler-rt/lib/asan/asan_poisoning.cpp (revision 349cc55c9796c4596a5b9904cd3281af295f878f)
168d75effSDimitry Andric //===-- asan_poisoning.cpp ------------------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of AddressSanitizer, an address sanity checker.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric // Shadow memory poisoning by ASan RTL and by user application.
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric 
1468d75effSDimitry Andric #include "asan_poisoning.h"
1568d75effSDimitry Andric #include "asan_report.h"
1668d75effSDimitry Andric #include "asan_stack.h"
1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_atomic.h"
1868d75effSDimitry Andric #include "sanitizer_common/sanitizer_libc.h"
1968d75effSDimitry Andric #include "sanitizer_common/sanitizer_flags.h"
2068d75effSDimitry Andric 
2168d75effSDimitry Andric namespace __asan {
2268d75effSDimitry Andric 
2368d75effSDimitry Andric static atomic_uint8_t can_poison_memory;
2468d75effSDimitry Andric 
2568d75effSDimitry Andric void SetCanPoisonMemory(bool value) {
2668d75effSDimitry Andric   atomic_store(&can_poison_memory, value, memory_order_release);
2768d75effSDimitry Andric }
2868d75effSDimitry Andric 
2968d75effSDimitry Andric bool CanPoisonMemory() {
3068d75effSDimitry Andric   return atomic_load(&can_poison_memory, memory_order_acquire);
3168d75effSDimitry Andric }
3268d75effSDimitry Andric 
3368d75effSDimitry Andric void PoisonShadow(uptr addr, uptr size, u8 value) {
3468d75effSDimitry Andric   if (value && !CanPoisonMemory()) return;
3568d75effSDimitry Andric   CHECK(AddrIsAlignedByGranularity(addr));
3668d75effSDimitry Andric   CHECK(AddrIsInMem(addr));
3768d75effSDimitry Andric   CHECK(AddrIsAlignedByGranularity(addr + size));
3868d75effSDimitry Andric   CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY));
3968d75effSDimitry Andric   CHECK(REAL(memset));
4068d75effSDimitry Andric   FastPoisonShadow(addr, size, value);
4168d75effSDimitry Andric }
4268d75effSDimitry Andric 
4368d75effSDimitry Andric void PoisonShadowPartialRightRedzone(uptr addr,
4468d75effSDimitry Andric                                      uptr size,
4568d75effSDimitry Andric                                      uptr redzone_size,
4668d75effSDimitry Andric                                      u8 value) {
4768d75effSDimitry Andric   if (!CanPoisonMemory()) return;
4868d75effSDimitry Andric   CHECK(AddrIsAlignedByGranularity(addr));
4968d75effSDimitry Andric   CHECK(AddrIsInMem(addr));
5068d75effSDimitry Andric   FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value);
5168d75effSDimitry Andric }
5268d75effSDimitry Andric 
5368d75effSDimitry Andric struct ShadowSegmentEndpoint {
5468d75effSDimitry Andric   u8 *chunk;
5568d75effSDimitry Andric   s8 offset;  // in [0, SHADOW_GRANULARITY)
5668d75effSDimitry Andric   s8 value;  // = *chunk;
5768d75effSDimitry Andric 
5868d75effSDimitry Andric   explicit ShadowSegmentEndpoint(uptr address) {
5968d75effSDimitry Andric     chunk = (u8*)MemToShadow(address);
6068d75effSDimitry Andric     offset = address & (SHADOW_GRANULARITY - 1);
6168d75effSDimitry Andric     value = *chunk;
6268d75effSDimitry Andric   }
6368d75effSDimitry Andric };
6468d75effSDimitry Andric 
6568d75effSDimitry Andric void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
6668d75effSDimitry Andric   uptr end = ptr + size;
6768d75effSDimitry Andric   if (Verbosity()) {
6868d75effSDimitry Andric     Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n",
69*349cc55cSDimitry Andric            poison ? "" : "un", (void *)ptr, (void *)end, size);
7068d75effSDimitry Andric     if (Verbosity() >= 2)
7168d75effSDimitry Andric       PRINT_CURRENT_STACK();
7268d75effSDimitry Andric   }
7368d75effSDimitry Andric   CHECK(size);
7468d75effSDimitry Andric   CHECK_LE(size, 4096);
7568d75effSDimitry Andric   CHECK(IsAligned(end, SHADOW_GRANULARITY));
7668d75effSDimitry Andric   if (!IsAligned(ptr, SHADOW_GRANULARITY)) {
7768d75effSDimitry Andric     *(u8 *)MemToShadow(ptr) =
7868d75effSDimitry Andric         poison ? static_cast<u8>(ptr % SHADOW_GRANULARITY) : 0;
7968d75effSDimitry Andric     ptr |= SHADOW_GRANULARITY - 1;
8068d75effSDimitry Andric     ptr++;
8168d75effSDimitry Andric   }
8268d75effSDimitry Andric   for (; ptr < end; ptr += SHADOW_GRANULARITY)
8368d75effSDimitry Andric     *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0;
8468d75effSDimitry Andric }
8568d75effSDimitry Andric 
8668d75effSDimitry Andric }  // namespace __asan
8768d75effSDimitry Andric 
8868d75effSDimitry Andric // ---------------------- Interface ---------------- {{{1
8968d75effSDimitry Andric using namespace __asan;
9068d75effSDimitry Andric 
9168d75effSDimitry Andric // Current implementation of __asan_(un)poison_memory_region doesn't check
9268d75effSDimitry Andric // that user program (un)poisons the memory it owns. It poisons memory
9368d75effSDimitry Andric // conservatively, and unpoisons progressively to make sure asan shadow
9468d75effSDimitry Andric // mapping invariant is preserved (see detailed mapping description here:
9568d75effSDimitry Andric // https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm).
9668d75effSDimitry Andric //
9768d75effSDimitry Andric // * if user asks to poison region [left, right), the program poisons
9868d75effSDimitry Andric // at least [left, AlignDown(right)).
9968d75effSDimitry Andric // * if user asks to unpoison region [left, right), the program unpoisons
10068d75effSDimitry Andric // at most [AlignDown(left), right).
10168d75effSDimitry Andric void __asan_poison_memory_region(void const volatile *addr, uptr size) {
10268d75effSDimitry Andric   if (!flags()->allow_user_poisoning || size == 0) return;
10368d75effSDimitry Andric   uptr beg_addr = (uptr)addr;
10468d75effSDimitry Andric   uptr end_addr = beg_addr + size;
10568d75effSDimitry Andric   VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
10668d75effSDimitry Andric           (void *)end_addr);
10768d75effSDimitry Andric   ShadowSegmentEndpoint beg(beg_addr);
10868d75effSDimitry Andric   ShadowSegmentEndpoint end(end_addr);
10968d75effSDimitry Andric   if (beg.chunk == end.chunk) {
11068d75effSDimitry Andric     CHECK_LT(beg.offset, end.offset);
11168d75effSDimitry Andric     s8 value = beg.value;
11268d75effSDimitry Andric     CHECK_EQ(value, end.value);
11368d75effSDimitry Andric     // We can only poison memory if the byte in end.offset is unaddressable.
11468d75effSDimitry Andric     // No need to re-poison memory if it is poisoned already.
11568d75effSDimitry Andric     if (value > 0 && value <= end.offset) {
11668d75effSDimitry Andric       if (beg.offset > 0) {
11768d75effSDimitry Andric         *beg.chunk = Min(value, beg.offset);
11868d75effSDimitry Andric       } else {
11968d75effSDimitry Andric         *beg.chunk = kAsanUserPoisonedMemoryMagic;
12068d75effSDimitry Andric       }
12168d75effSDimitry Andric     }
12268d75effSDimitry Andric     return;
12368d75effSDimitry Andric   }
12468d75effSDimitry Andric   CHECK_LT(beg.chunk, end.chunk);
12568d75effSDimitry Andric   if (beg.offset > 0) {
12668d75effSDimitry Andric     // Mark bytes from beg.offset as unaddressable.
12768d75effSDimitry Andric     if (beg.value == 0) {
12868d75effSDimitry Andric       *beg.chunk = beg.offset;
12968d75effSDimitry Andric     } else {
13068d75effSDimitry Andric       *beg.chunk = Min(beg.value, beg.offset);
13168d75effSDimitry Andric     }
13268d75effSDimitry Andric     beg.chunk++;
13368d75effSDimitry Andric   }
13468d75effSDimitry Andric   REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
13568d75effSDimitry Andric   // Poison if byte in end.offset is unaddressable.
13668d75effSDimitry Andric   if (end.value > 0 && end.value <= end.offset) {
13768d75effSDimitry Andric     *end.chunk = kAsanUserPoisonedMemoryMagic;
13868d75effSDimitry Andric   }
13968d75effSDimitry Andric }
14068d75effSDimitry Andric 
14168d75effSDimitry Andric void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
14268d75effSDimitry Andric   if (!flags()->allow_user_poisoning || size == 0) return;
14368d75effSDimitry Andric   uptr beg_addr = (uptr)addr;
14468d75effSDimitry Andric   uptr end_addr = beg_addr + size;
14568d75effSDimitry Andric   VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
14668d75effSDimitry Andric           (void *)end_addr);
14768d75effSDimitry Andric   ShadowSegmentEndpoint beg(beg_addr);
14868d75effSDimitry Andric   ShadowSegmentEndpoint end(end_addr);
14968d75effSDimitry Andric   if (beg.chunk == end.chunk) {
15068d75effSDimitry Andric     CHECK_LT(beg.offset, end.offset);
15168d75effSDimitry Andric     s8 value = beg.value;
15268d75effSDimitry Andric     CHECK_EQ(value, end.value);
15368d75effSDimitry Andric     // We unpoison memory bytes up to enbytes up to end.offset if it is not
15468d75effSDimitry Andric     // unpoisoned already.
15568d75effSDimitry Andric     if (value != 0) {
15668d75effSDimitry Andric       *beg.chunk = Max(value, end.offset);
15768d75effSDimitry Andric     }
15868d75effSDimitry Andric     return;
15968d75effSDimitry Andric   }
16068d75effSDimitry Andric   CHECK_LT(beg.chunk, end.chunk);
16168d75effSDimitry Andric   if (beg.offset > 0) {
16268d75effSDimitry Andric     *beg.chunk = 0;
16368d75effSDimitry Andric     beg.chunk++;
16468d75effSDimitry Andric   }
16568d75effSDimitry Andric   REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk);
16668d75effSDimitry Andric   if (end.offset > 0 && end.value != 0) {
16768d75effSDimitry Andric     *end.chunk = Max(end.value, end.offset);
16868d75effSDimitry Andric   }
16968d75effSDimitry Andric }
17068d75effSDimitry Andric 
17168d75effSDimitry Andric int __asan_address_is_poisoned(void const volatile *addr) {
17268d75effSDimitry Andric   return __asan::AddressIsPoisoned((uptr)addr);
17368d75effSDimitry Andric }
17468d75effSDimitry Andric 
17568d75effSDimitry Andric uptr __asan_region_is_poisoned(uptr beg, uptr size) {
176fe6060f1SDimitry Andric   if (!size)
177fe6060f1SDimitry Andric     return 0;
17868d75effSDimitry Andric   uptr end = beg + size;
179fe6060f1SDimitry Andric   if (!AddrIsInMem(beg))
180fe6060f1SDimitry Andric     return beg;
181fe6060f1SDimitry Andric   if (!AddrIsInMem(end))
182fe6060f1SDimitry Andric     return end;
18368d75effSDimitry Andric   CHECK_LT(beg, end);
18468d75effSDimitry Andric   uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY);
18568d75effSDimitry Andric   uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY);
18668d75effSDimitry Andric   uptr shadow_beg = MemToShadow(aligned_b);
18768d75effSDimitry Andric   uptr shadow_end = MemToShadow(aligned_e);
18868d75effSDimitry Andric   // First check the first and the last application bytes,
18968d75effSDimitry Andric   // then check the SHADOW_GRANULARITY-aligned region by calling
19068d75effSDimitry Andric   // mem_is_zero on the corresponding shadow.
191fe6060f1SDimitry Andric   if (!__asan::AddressIsPoisoned(beg) && !__asan::AddressIsPoisoned(end - 1) &&
19268d75effSDimitry Andric       (shadow_end <= shadow_beg ||
19368d75effSDimitry Andric        __sanitizer::mem_is_zero((const char *)shadow_beg,
19468d75effSDimitry Andric                                 shadow_end - shadow_beg)))
19568d75effSDimitry Andric     return 0;
19668d75effSDimitry Andric   // The fast check failed, so we have a poisoned byte somewhere.
19768d75effSDimitry Andric   // Find it slowly.
19868d75effSDimitry Andric   for (; beg < end; beg++)
19968d75effSDimitry Andric     if (__asan::AddressIsPoisoned(beg))
20068d75effSDimitry Andric       return beg;
20168d75effSDimitry Andric   UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found");
20268d75effSDimitry Andric   return 0;
20368d75effSDimitry Andric }
20468d75effSDimitry Andric 
20568d75effSDimitry Andric #define CHECK_SMALL_REGION(p, size, isWrite)                  \
20668d75effSDimitry Andric   do {                                                        \
20768d75effSDimitry Andric     uptr __p = reinterpret_cast<uptr>(p);                     \
20868d75effSDimitry Andric     uptr __size = size;                                       \
20968d75effSDimitry Andric     if (UNLIKELY(__asan::AddressIsPoisoned(__p) ||            \
21068d75effSDimitry Andric         __asan::AddressIsPoisoned(__p + __size - 1))) {       \
21168d75effSDimitry Andric       GET_CURRENT_PC_BP_SP;                                   \
21268d75effSDimitry Andric       uptr __bad = __asan_region_is_poisoned(__p, __size);    \
21368d75effSDimitry Andric       __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\
21468d75effSDimitry Andric     }                                                         \
21568d75effSDimitry Andric   } while (false)
21668d75effSDimitry Andric 
21768d75effSDimitry Andric 
21868d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
21968d75effSDimitry Andric u16 __sanitizer_unaligned_load16(const uu16 *p) {
22068d75effSDimitry Andric   CHECK_SMALL_REGION(p, sizeof(*p), false);
22168d75effSDimitry Andric   return *p;
22268d75effSDimitry Andric }
22368d75effSDimitry Andric 
22468d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
22568d75effSDimitry Andric u32 __sanitizer_unaligned_load32(const uu32 *p) {
22668d75effSDimitry Andric   CHECK_SMALL_REGION(p, sizeof(*p), false);
22768d75effSDimitry Andric   return *p;
22868d75effSDimitry Andric }
22968d75effSDimitry Andric 
23068d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
23168d75effSDimitry Andric u64 __sanitizer_unaligned_load64(const uu64 *p) {
23268d75effSDimitry Andric   CHECK_SMALL_REGION(p, sizeof(*p), false);
23368d75effSDimitry Andric   return *p;
23468d75effSDimitry Andric }
23568d75effSDimitry Andric 
23668d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
23768d75effSDimitry Andric void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
23868d75effSDimitry Andric   CHECK_SMALL_REGION(p, sizeof(*p), true);
23968d75effSDimitry Andric   *p = x;
24068d75effSDimitry Andric }
24168d75effSDimitry Andric 
24268d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
24368d75effSDimitry Andric void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
24468d75effSDimitry Andric   CHECK_SMALL_REGION(p, sizeof(*p), true);
24568d75effSDimitry Andric   *p = x;
24668d75effSDimitry Andric }
24768d75effSDimitry Andric 
24868d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
24968d75effSDimitry Andric void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
25068d75effSDimitry Andric   CHECK_SMALL_REGION(p, sizeof(*p), true);
25168d75effSDimitry Andric   *p = x;
25268d75effSDimitry Andric }
25368d75effSDimitry Andric 
25468d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
25568d75effSDimitry Andric void __asan_poison_cxx_array_cookie(uptr p) {
25668d75effSDimitry Andric   if (SANITIZER_WORDSIZE != 64) return;
25768d75effSDimitry Andric   if (!flags()->poison_array_cookie) return;
25868d75effSDimitry Andric   uptr s = MEM_TO_SHADOW(p);
25968d75effSDimitry Andric   *reinterpret_cast<u8*>(s) = kAsanArrayCookieMagic;
26068d75effSDimitry Andric }
26168d75effSDimitry Andric 
26268d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
26368d75effSDimitry Andric uptr __asan_load_cxx_array_cookie(uptr *p) {
26468d75effSDimitry Andric   if (SANITIZER_WORDSIZE != 64) return *p;
26568d75effSDimitry Andric   if (!flags()->poison_array_cookie) return *p;
26668d75effSDimitry Andric   uptr s = MEM_TO_SHADOW(reinterpret_cast<uptr>(p));
26768d75effSDimitry Andric   u8 sval = *reinterpret_cast<u8*>(s);
26868d75effSDimitry Andric   if (sval == kAsanArrayCookieMagic) return *p;
26968d75effSDimitry Andric   // If sval is not kAsanArrayCookieMagic it can only be freed memory,
27068d75effSDimitry Andric   // which means that we are going to get double-free. So, return 0 to avoid
27168d75effSDimitry Andric   // infinite loop of destructors. We don't want to report a double-free here
27268d75effSDimitry Andric   // though, so print a warning just in case.
27368d75effSDimitry Andric   // CHECK_EQ(sval, kAsanHeapFreeMagic);
27468d75effSDimitry Andric   if (sval == kAsanHeapFreeMagic) {
27568d75effSDimitry Andric     Report("AddressSanitizer: loaded array cookie from free-d memory; "
27668d75effSDimitry Andric            "expect a double-free report\n");
27768d75effSDimitry Andric     return 0;
27868d75effSDimitry Andric   }
27968d75effSDimitry Andric   // The cookie may remain unpoisoned if e.g. it comes from a custom
28068d75effSDimitry Andric   // operator new defined inside a class.
28168d75effSDimitry Andric   return *p;
28268d75effSDimitry Andric }
28368d75effSDimitry Andric 
28468d75effSDimitry Andric // This is a simplified version of __asan_(un)poison_memory_region, which
28568d75effSDimitry Andric // assumes that left border of region to be poisoned is properly aligned.
28668d75effSDimitry Andric static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
28768d75effSDimitry Andric   if (size == 0) return;
28868d75effSDimitry Andric   uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1);
28968d75effSDimitry Andric   PoisonShadow(addr, aligned_size,
29068d75effSDimitry Andric                do_poison ? kAsanStackUseAfterScopeMagic : 0);
29168d75effSDimitry Andric   if (size == aligned_size)
29268d75effSDimitry Andric     return;
29368d75effSDimitry Andric   s8 end_offset = (s8)(size - aligned_size);
29468d75effSDimitry Andric   s8* shadow_end = (s8*)MemToShadow(addr + aligned_size);
29568d75effSDimitry Andric   s8 end_value = *shadow_end;
29668d75effSDimitry Andric   if (do_poison) {
29768d75effSDimitry Andric     // If possible, mark all the bytes mapping to last shadow byte as
29868d75effSDimitry Andric     // unaddressable.
29968d75effSDimitry Andric     if (end_value > 0 && end_value <= end_offset)
30068d75effSDimitry Andric       *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
30168d75effSDimitry Andric   } else {
30268d75effSDimitry Andric     // If necessary, mark few first bytes mapping to last shadow byte
30368d75effSDimitry Andric     // as addressable
30468d75effSDimitry Andric     if (end_value != 0)
30568d75effSDimitry Andric       *shadow_end = Max(end_value, end_offset);
30668d75effSDimitry Andric   }
30768d75effSDimitry Andric }
30868d75effSDimitry Andric 
30968d75effSDimitry Andric void __asan_set_shadow_00(uptr addr, uptr size) {
31068d75effSDimitry Andric   REAL(memset)((void *)addr, 0, size);
31168d75effSDimitry Andric }
31268d75effSDimitry Andric 
31368d75effSDimitry Andric void __asan_set_shadow_f1(uptr addr, uptr size) {
31468d75effSDimitry Andric   REAL(memset)((void *)addr, 0xf1, size);
31568d75effSDimitry Andric }
31668d75effSDimitry Andric 
31768d75effSDimitry Andric void __asan_set_shadow_f2(uptr addr, uptr size) {
31868d75effSDimitry Andric   REAL(memset)((void *)addr, 0xf2, size);
31968d75effSDimitry Andric }
32068d75effSDimitry Andric 
32168d75effSDimitry Andric void __asan_set_shadow_f3(uptr addr, uptr size) {
32268d75effSDimitry Andric   REAL(memset)((void *)addr, 0xf3, size);
32368d75effSDimitry Andric }
32468d75effSDimitry Andric 
32568d75effSDimitry Andric void __asan_set_shadow_f5(uptr addr, uptr size) {
32668d75effSDimitry Andric   REAL(memset)((void *)addr, 0xf5, size);
32768d75effSDimitry Andric }
32868d75effSDimitry Andric 
32968d75effSDimitry Andric void __asan_set_shadow_f8(uptr addr, uptr size) {
33068d75effSDimitry Andric   REAL(memset)((void *)addr, 0xf8, size);
33168d75effSDimitry Andric }
33268d75effSDimitry Andric 
33368d75effSDimitry Andric void __asan_poison_stack_memory(uptr addr, uptr size) {
33468d75effSDimitry Andric   VReport(1, "poisoning: %p %zx\n", (void *)addr, size);
33568d75effSDimitry Andric   PoisonAlignedStackMemory(addr, size, true);
33668d75effSDimitry Andric }
33768d75effSDimitry Andric 
33868d75effSDimitry Andric void __asan_unpoison_stack_memory(uptr addr, uptr size) {
33968d75effSDimitry Andric   VReport(1, "unpoisoning: %p %zx\n", (void *)addr, size);
34068d75effSDimitry Andric   PoisonAlignedStackMemory(addr, size, false);
34168d75effSDimitry Andric }
34268d75effSDimitry Andric 
34368d75effSDimitry Andric void __sanitizer_annotate_contiguous_container(const void *beg_p,
34468d75effSDimitry Andric                                                const void *end_p,
34568d75effSDimitry Andric                                                const void *old_mid_p,
34668d75effSDimitry Andric                                                const void *new_mid_p) {
34768d75effSDimitry Andric   if (!flags()->detect_container_overflow) return;
34868d75effSDimitry Andric   VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p,
34968d75effSDimitry Andric           new_mid_p);
35068d75effSDimitry Andric   uptr beg = reinterpret_cast<uptr>(beg_p);
35168d75effSDimitry Andric   uptr end = reinterpret_cast<uptr>(end_p);
35268d75effSDimitry Andric   uptr old_mid = reinterpret_cast<uptr>(old_mid_p);
35368d75effSDimitry Andric   uptr new_mid = reinterpret_cast<uptr>(new_mid_p);
35468d75effSDimitry Andric   uptr granularity = SHADOW_GRANULARITY;
35568d75effSDimitry Andric   if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end &&
35668d75effSDimitry Andric         IsAligned(beg, granularity))) {
35768d75effSDimitry Andric     GET_STACK_TRACE_FATAL_HERE;
35868d75effSDimitry Andric     ReportBadParamsToAnnotateContiguousContainer(beg, end, old_mid, new_mid,
35968d75effSDimitry Andric                                                  &stack);
36068d75effSDimitry Andric   }
36168d75effSDimitry Andric   CHECK_LE(end - beg,
362fe6060f1SDimitry Andric            FIRST_32_SECOND_64(1UL << 30, 1ULL << 40)); // Sanity check.
36368d75effSDimitry Andric 
36468d75effSDimitry Andric   uptr a = RoundDownTo(Min(old_mid, new_mid), granularity);
36568d75effSDimitry Andric   uptr c = RoundUpTo(Max(old_mid, new_mid), granularity);
36668d75effSDimitry Andric   uptr d1 = RoundDownTo(old_mid, granularity);
36768d75effSDimitry Andric   // uptr d2 = RoundUpTo(old_mid, granularity);
36868d75effSDimitry Andric   // Currently we should be in this state:
36968d75effSDimitry Andric   // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good.
37068d75effSDimitry Andric   // Make a quick sanity check that we are indeed in this state.
37168d75effSDimitry Andric   //
37268d75effSDimitry Andric   // FIXME: Two of these three checks are disabled until we fix
37368d75effSDimitry Andric   // https://github.com/google/sanitizers/issues/258.
37468d75effSDimitry Andric   // if (d1 != d2)
37568d75effSDimitry Andric   //  CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
37668d75effSDimitry Andric   if (a + granularity <= d1)
37768d75effSDimitry Andric     CHECK_EQ(*(u8*)MemToShadow(a), 0);
37868d75effSDimitry Andric   // if (d2 + granularity <= c && c <= end)
37968d75effSDimitry Andric   //   CHECK_EQ(*(u8 *)MemToShadow(c - granularity),
38068d75effSDimitry Andric   //            kAsanContiguousContainerOOBMagic);
38168d75effSDimitry Andric 
38268d75effSDimitry Andric   uptr b1 = RoundDownTo(new_mid, granularity);
38368d75effSDimitry Andric   uptr b2 = RoundUpTo(new_mid, granularity);
38468d75effSDimitry Andric   // New state:
38568d75effSDimitry Andric   // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good.
38668d75effSDimitry Andric   PoisonShadow(a, b1 - a, 0);
38768d75effSDimitry Andric   PoisonShadow(b2, c - b2, kAsanContiguousContainerOOBMagic);
38868d75effSDimitry Andric   if (b1 != b2) {
38968d75effSDimitry Andric     CHECK_EQ(b2 - b1, granularity);
39068d75effSDimitry Andric     *(u8*)MemToShadow(b1) = static_cast<u8>(new_mid - b1);
39168d75effSDimitry Andric   }
39268d75effSDimitry Andric }
39368d75effSDimitry Andric 
39468d75effSDimitry Andric const void *__sanitizer_contiguous_container_find_bad_address(
39568d75effSDimitry Andric     const void *beg_p, const void *mid_p, const void *end_p) {
39668d75effSDimitry Andric   if (!flags()->detect_container_overflow)
39768d75effSDimitry Andric     return nullptr;
39868d75effSDimitry Andric   uptr beg = reinterpret_cast<uptr>(beg_p);
39968d75effSDimitry Andric   uptr end = reinterpret_cast<uptr>(end_p);
40068d75effSDimitry Andric   uptr mid = reinterpret_cast<uptr>(mid_p);
40168d75effSDimitry Andric   CHECK_LE(beg, mid);
40268d75effSDimitry Andric   CHECK_LE(mid, end);
40368d75effSDimitry Andric   // Check some bytes starting from beg, some bytes around mid, and some bytes
40468d75effSDimitry Andric   // ending with end.
40568d75effSDimitry Andric   uptr kMaxRangeToCheck = 32;
40668d75effSDimitry Andric   uptr r1_beg = beg;
40768d75effSDimitry Andric   uptr r1_end = Min(beg + kMaxRangeToCheck, mid);
40868d75effSDimitry Andric   uptr r2_beg = Max(beg, mid - kMaxRangeToCheck);
40968d75effSDimitry Andric   uptr r2_end = Min(end, mid + kMaxRangeToCheck);
41068d75effSDimitry Andric   uptr r3_beg = Max(end - kMaxRangeToCheck, mid);
41168d75effSDimitry Andric   uptr r3_end = end;
41268d75effSDimitry Andric   for (uptr i = r1_beg; i < r1_end; i++)
41368d75effSDimitry Andric     if (AddressIsPoisoned(i))
41468d75effSDimitry Andric       return reinterpret_cast<const void *>(i);
41568d75effSDimitry Andric   for (uptr i = r2_beg; i < mid; i++)
41668d75effSDimitry Andric     if (AddressIsPoisoned(i))
41768d75effSDimitry Andric       return reinterpret_cast<const void *>(i);
41868d75effSDimitry Andric   for (uptr i = mid; i < r2_end; i++)
41968d75effSDimitry Andric     if (!AddressIsPoisoned(i))
42068d75effSDimitry Andric       return reinterpret_cast<const void *>(i);
42168d75effSDimitry Andric   for (uptr i = r3_beg; i < r3_end; i++)
42268d75effSDimitry Andric     if (!AddressIsPoisoned(i))
42368d75effSDimitry Andric       return reinterpret_cast<const void *>(i);
42468d75effSDimitry Andric   return nullptr;
42568d75effSDimitry Andric }
42668d75effSDimitry Andric 
42768d75effSDimitry Andric int __sanitizer_verify_contiguous_container(const void *beg_p,
42868d75effSDimitry Andric                                             const void *mid_p,
42968d75effSDimitry Andric                                             const void *end_p) {
43068d75effSDimitry Andric   return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p,
43168d75effSDimitry Andric                                                            end_p) == nullptr;
43268d75effSDimitry Andric }
43368d75effSDimitry Andric 
43468d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
43568d75effSDimitry Andric void __asan_poison_intra_object_redzone(uptr ptr, uptr size) {
43668d75effSDimitry Andric   AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true);
43768d75effSDimitry Andric }
43868d75effSDimitry Andric 
43968d75effSDimitry Andric extern "C" SANITIZER_INTERFACE_ATTRIBUTE
44068d75effSDimitry Andric void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) {
44168d75effSDimitry Andric   AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false);
44268d75effSDimitry Andric }
44368d75effSDimitry Andric 
44468d75effSDimitry Andric // --- Implementation of LSan-specific functions --- {{{1
44568d75effSDimitry Andric namespace __lsan {
44668d75effSDimitry Andric bool WordIsPoisoned(uptr addr) {
44768d75effSDimitry Andric   return (__asan_region_is_poisoned(addr, sizeof(uptr)) != 0);
44868d75effSDimitry Andric }
44968d75effSDimitry Andric }
450