168d75effSDimitry Andric //===-- msan_poisoning.cpp --------------------------------------*- C++ -*-===// 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 MemorySanitizer. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric //===----------------------------------------------------------------------===// 1268d75effSDimitry Andric 1368d75effSDimitry Andric #include "msan_poisoning.h" 1468d75effSDimitry Andric 1568d75effSDimitry Andric #include "interception/interception.h" 1668d75effSDimitry Andric #include "msan_origin.h" 1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h" 1868d75effSDimitry Andric 1968d75effSDimitry Andric DECLARE_REAL(void *, memset, void *dest, int c, uptr n) 2068d75effSDimitry Andric DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n) 2168d75effSDimitry Andric DECLARE_REAL(void *, memmove, void *dest, const void *src, uptr n) 2268d75effSDimitry Andric 2368d75effSDimitry Andric namespace __msan { 2468d75effSDimitry Andric 2568d75effSDimitry Andric u32 GetOriginIfPoisoned(uptr addr, uptr size) { 2668d75effSDimitry Andric unsigned char *s = (unsigned char *)MEM_TO_SHADOW(addr); 2768d75effSDimitry Andric for (uptr i = 0; i < size; ++i) 2868d75effSDimitry Andric if (s[i]) return *(u32 *)SHADOW_TO_ORIGIN(((uptr)s + i) & ~3UL); 2968d75effSDimitry Andric return 0; 3068d75effSDimitry Andric } 3168d75effSDimitry Andric 3268d75effSDimitry Andric void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, 3368d75effSDimitry Andric u32 src_origin) { 3468d75effSDimitry Andric uptr dst_s = MEM_TO_SHADOW(addr); 3568d75effSDimitry Andric uptr src_s = src_shadow; 3668d75effSDimitry Andric uptr src_s_end = src_s + size; 3768d75effSDimitry Andric 3868d75effSDimitry Andric for (; src_s < src_s_end; ++dst_s, ++src_s) 3968d75effSDimitry Andric if (*(u8 *)src_s) *(u32 *)SHADOW_TO_ORIGIN(dst_s & ~3UL) = src_origin; 4068d75effSDimitry Andric } 4168d75effSDimitry Andric 4268d75effSDimitry Andric void CopyOrigin(const void *dst, const void *src, uptr size, 4368d75effSDimitry Andric StackTrace *stack) { 4468d75effSDimitry Andric if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; 4568d75effSDimitry Andric 4668d75effSDimitry Andric uptr d = (uptr)dst; 4768d75effSDimitry Andric uptr beg = d & ~3UL; 4868d75effSDimitry Andric // Copy left unaligned origin if that memory is poisoned. 4968d75effSDimitry Andric if (beg < d) { 50*e8d8bef9SDimitry Andric u32 o = GetOriginIfPoisoned((uptr)src, beg + 4 - d); 5168d75effSDimitry Andric if (o) { 5268d75effSDimitry Andric if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); 5368d75effSDimitry Andric *(u32 *)MEM_TO_ORIGIN(beg) = o; 5468d75effSDimitry Andric } 5568d75effSDimitry Andric beg += 4; 5668d75effSDimitry Andric } 5768d75effSDimitry Andric 5868d75effSDimitry Andric uptr end = (d + size) & ~3UL; 5968d75effSDimitry Andric // If both ends fall into the same 4-byte slot, we are done. 6068d75effSDimitry Andric if (end < beg) return; 6168d75effSDimitry Andric 6268d75effSDimitry Andric // Copy right unaligned origin if that memory is poisoned. 6368d75effSDimitry Andric if (end < d + size) { 6468d75effSDimitry Andric u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end); 6568d75effSDimitry Andric if (o) { 6668d75effSDimitry Andric if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); 6768d75effSDimitry Andric *(u32 *)MEM_TO_ORIGIN(end) = o; 6868d75effSDimitry Andric } 6968d75effSDimitry Andric } 7068d75effSDimitry Andric 7168d75effSDimitry Andric if (beg < end) { 7268d75effSDimitry Andric // Align src up. 7368d75effSDimitry Andric uptr s = ((uptr)src + 3) & ~3UL; 7468d75effSDimitry Andric // FIXME: factor out to msan_copy_origin_aligned 7568d75effSDimitry Andric if (__msan_get_track_origins() > 1) { 7668d75effSDimitry Andric u32 *src = (u32 *)MEM_TO_ORIGIN(s); 7768d75effSDimitry Andric u32 *src_s = (u32 *)MEM_TO_SHADOW(s); 7868d75effSDimitry Andric u32 *src_end = (u32 *)MEM_TO_ORIGIN(s + (end - beg)); 7968d75effSDimitry Andric u32 *dst = (u32 *)MEM_TO_ORIGIN(beg); 8068d75effSDimitry Andric u32 src_o = 0; 8168d75effSDimitry Andric u32 dst_o = 0; 8268d75effSDimitry Andric for (; src < src_end; ++src, ++src_s, ++dst) { 8368d75effSDimitry Andric if (!*src_s) continue; 8468d75effSDimitry Andric if (*src != src_o) { 8568d75effSDimitry Andric src_o = *src; 8668d75effSDimitry Andric dst_o = ChainOrigin(src_o, stack); 8768d75effSDimitry Andric } 8868d75effSDimitry Andric *dst = dst_o; 8968d75effSDimitry Andric } 9068d75effSDimitry Andric } else { 9168d75effSDimitry Andric REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), 9268d75effSDimitry Andric end - beg); 9368d75effSDimitry Andric } 9468d75effSDimitry Andric } 9568d75effSDimitry Andric } 9668d75effSDimitry Andric 97*e8d8bef9SDimitry Andric void ReverseCopyOrigin(const void *dst, const void *src, uptr size, 98*e8d8bef9SDimitry Andric StackTrace *stack) { 99*e8d8bef9SDimitry Andric if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) 100*e8d8bef9SDimitry Andric return; 101*e8d8bef9SDimitry Andric 102*e8d8bef9SDimitry Andric uptr d = (uptr)dst; 103*e8d8bef9SDimitry Andric uptr end = (d + size) & ~3UL; 104*e8d8bef9SDimitry Andric 105*e8d8bef9SDimitry Andric // Copy right unaligned origin if that memory is poisoned. 106*e8d8bef9SDimitry Andric if (end < d + size) { 107*e8d8bef9SDimitry Andric u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end); 108*e8d8bef9SDimitry Andric if (o) { 109*e8d8bef9SDimitry Andric if (__msan_get_track_origins() > 1) 110*e8d8bef9SDimitry Andric o = ChainOrigin(o, stack); 111*e8d8bef9SDimitry Andric *(u32 *)MEM_TO_ORIGIN(end) = o; 112*e8d8bef9SDimitry Andric } 113*e8d8bef9SDimitry Andric } 114*e8d8bef9SDimitry Andric 115*e8d8bef9SDimitry Andric uptr beg = d & ~3UL; 116*e8d8bef9SDimitry Andric 117*e8d8bef9SDimitry Andric if (beg + 4 < end) { 118*e8d8bef9SDimitry Andric // Align src up. 119*e8d8bef9SDimitry Andric uptr s = ((uptr)src + 3) & ~3UL; 120*e8d8bef9SDimitry Andric if (__msan_get_track_origins() > 1) { 121*e8d8bef9SDimitry Andric u32 *src = (u32 *)MEM_TO_ORIGIN(s + end - beg - 4); 122*e8d8bef9SDimitry Andric u32 *src_s = (u32 *)MEM_TO_SHADOW(s + end - beg - 4); 123*e8d8bef9SDimitry Andric u32 *src_begin = (u32 *)MEM_TO_ORIGIN(s); 124*e8d8bef9SDimitry Andric u32 *dst = (u32 *)MEM_TO_ORIGIN(end - 4); 125*e8d8bef9SDimitry Andric u32 src_o = 0; 126*e8d8bef9SDimitry Andric u32 dst_o = 0; 127*e8d8bef9SDimitry Andric for (; src >= src_begin; --src, --src_s, --dst) { 128*e8d8bef9SDimitry Andric if (!*src_s) 129*e8d8bef9SDimitry Andric continue; 130*e8d8bef9SDimitry Andric if (*src != src_o) { 131*e8d8bef9SDimitry Andric src_o = *src; 132*e8d8bef9SDimitry Andric dst_o = ChainOrigin(src_o, stack); 133*e8d8bef9SDimitry Andric } 134*e8d8bef9SDimitry Andric *dst = dst_o; 135*e8d8bef9SDimitry Andric } 136*e8d8bef9SDimitry Andric } else { 137*e8d8bef9SDimitry Andric REAL(memmove) 138*e8d8bef9SDimitry Andric ((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), end - beg - 4); 139*e8d8bef9SDimitry Andric } 140*e8d8bef9SDimitry Andric } 141*e8d8bef9SDimitry Andric 142*e8d8bef9SDimitry Andric // Copy left unaligned origin if that memory is poisoned. 143*e8d8bef9SDimitry Andric if (beg < d) { 144*e8d8bef9SDimitry Andric u32 o = GetOriginIfPoisoned((uptr)src, beg + 4 - d); 145*e8d8bef9SDimitry Andric if (o) { 146*e8d8bef9SDimitry Andric if (__msan_get_track_origins() > 1) 147*e8d8bef9SDimitry Andric o = ChainOrigin(o, stack); 148*e8d8bef9SDimitry Andric *(u32 *)MEM_TO_ORIGIN(beg) = o; 149*e8d8bef9SDimitry Andric } 150*e8d8bef9SDimitry Andric } 151*e8d8bef9SDimitry Andric } 152*e8d8bef9SDimitry Andric 153*e8d8bef9SDimitry Andric void MoveOrigin(const void *dst, const void *src, uptr size, 154*e8d8bef9SDimitry Andric StackTrace *stack) { 155*e8d8bef9SDimitry Andric // If destination origin range overlaps with source origin range, move 156*e8d8bef9SDimitry Andric // origins by coping origins in a reverse order; otherwise, copy origins in 157*e8d8bef9SDimitry Andric // a normal order. 158*e8d8bef9SDimitry Andric uptr src_aligned_beg = reinterpret_cast<uptr>(src) & ~3UL; 159*e8d8bef9SDimitry Andric uptr src_aligned_end = (reinterpret_cast<uptr>(src) + size) & ~3UL; 160*e8d8bef9SDimitry Andric uptr dst_aligned_beg = reinterpret_cast<uptr>(dst) & ~3UL; 161*e8d8bef9SDimitry Andric if (dst_aligned_beg < src_aligned_end && dst_aligned_beg >= src_aligned_beg) 162*e8d8bef9SDimitry Andric return ReverseCopyOrigin(dst, src, size, stack); 163*e8d8bef9SDimitry Andric return CopyOrigin(dst, src, size, stack); 164*e8d8bef9SDimitry Andric } 165*e8d8bef9SDimitry Andric 16668d75effSDimitry Andric void MoveShadowAndOrigin(const void *dst, const void *src, uptr size, 16768d75effSDimitry Andric StackTrace *stack) { 16868d75effSDimitry Andric if (!MEM_IS_APP(dst)) return; 16968d75effSDimitry Andric if (!MEM_IS_APP(src)) return; 17068d75effSDimitry Andric if (src == dst) return; 171*e8d8bef9SDimitry Andric // MoveOrigin transfers origins by refering to their shadows. So we 172*e8d8bef9SDimitry Andric // need to move origins before moving shadows. 173*e8d8bef9SDimitry Andric if (__msan_get_track_origins()) 174*e8d8bef9SDimitry Andric MoveOrigin(dst, src, size, stack); 17568d75effSDimitry Andric REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst), 17668d75effSDimitry Andric (void *)MEM_TO_SHADOW((uptr)src), size); 17768d75effSDimitry Andric } 17868d75effSDimitry Andric 17968d75effSDimitry Andric void CopyShadowAndOrigin(const void *dst, const void *src, uptr size, 18068d75effSDimitry Andric StackTrace *stack) { 18168d75effSDimitry Andric if (!MEM_IS_APP(dst)) return; 18268d75effSDimitry Andric if (!MEM_IS_APP(src)) return; 183*e8d8bef9SDimitry Andric // Because origin's range is slightly larger than app range, memcpy may also 184*e8d8bef9SDimitry Andric // cause overlapped origin ranges. 18568d75effSDimitry Andric REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst), 18668d75effSDimitry Andric (void *)MEM_TO_SHADOW((uptr)src), size); 187*e8d8bef9SDimitry Andric if (__msan_get_track_origins()) 188*e8d8bef9SDimitry Andric MoveOrigin(dst, src, size, stack); 18968d75effSDimitry Andric } 19068d75effSDimitry Andric 19168d75effSDimitry Andric void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) { 19268d75effSDimitry Andric REAL(memcpy)(dst, src, size); 19368d75effSDimitry Andric CopyShadowAndOrigin(dst, src, size, stack); 19468d75effSDimitry Andric } 19568d75effSDimitry Andric 19668d75effSDimitry Andric void SetShadow(const void *ptr, uptr size, u8 value) { 19768d75effSDimitry Andric uptr PageSize = GetPageSizeCached(); 19868d75effSDimitry Andric uptr shadow_beg = MEM_TO_SHADOW(ptr); 19968d75effSDimitry Andric uptr shadow_end = shadow_beg + size; 20068d75effSDimitry Andric if (value || 20168d75effSDimitry Andric shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { 20268d75effSDimitry Andric REAL(memset)((void *)shadow_beg, value, shadow_end - shadow_beg); 20368d75effSDimitry Andric } else { 20468d75effSDimitry Andric uptr page_beg = RoundUpTo(shadow_beg, PageSize); 20568d75effSDimitry Andric uptr page_end = RoundDownTo(shadow_end, PageSize); 20668d75effSDimitry Andric 20768d75effSDimitry Andric if (page_beg >= page_end) { 20868d75effSDimitry Andric REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); 20968d75effSDimitry Andric } else { 21068d75effSDimitry Andric if (page_beg != shadow_beg) { 21168d75effSDimitry Andric REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg); 21268d75effSDimitry Andric } 21368d75effSDimitry Andric if (page_end != shadow_end) { 21468d75effSDimitry Andric REAL(memset)((void *)page_end, 0, shadow_end - page_end); 21568d75effSDimitry Andric } 21668d75effSDimitry Andric if (!MmapFixedNoReserve(page_beg, page_end - page_beg)) 21768d75effSDimitry Andric Die(); 21868d75effSDimitry Andric } 21968d75effSDimitry Andric } 22068d75effSDimitry Andric } 22168d75effSDimitry Andric 22268d75effSDimitry Andric void SetOrigin(const void *dst, uptr size, u32 origin) { 22368d75effSDimitry Andric // Origin mapping is 4 bytes per 4 bytes of application memory. 22468d75effSDimitry Andric // Here we extend the range such that its left and right bounds are both 22568d75effSDimitry Andric // 4 byte aligned. 22668d75effSDimitry Andric uptr x = MEM_TO_ORIGIN((uptr)dst); 22768d75effSDimitry Andric uptr beg = x & ~3UL; // align down. 22868d75effSDimitry Andric uptr end = (x + size + 3) & ~3UL; // align up. 22968d75effSDimitry Andric u64 origin64 = ((u64)origin << 32) | origin; 23068d75effSDimitry Andric // This is like memset, but the value is 32-bit. We unroll by 2 to write 23168d75effSDimitry Andric // 64 bits at once. May want to unroll further to get 128-bit stores. 23268d75effSDimitry Andric if (beg & 7ULL) { 23368d75effSDimitry Andric *(u32 *)beg = origin; 23468d75effSDimitry Andric beg += 4; 23568d75effSDimitry Andric } 23668d75effSDimitry Andric for (uptr addr = beg; addr < (end & ~7UL); addr += 8) *(u64 *)addr = origin64; 23768d75effSDimitry Andric if (end & 7ULL) *(u32 *)(end - 4) = origin; 23868d75effSDimitry Andric } 23968d75effSDimitry Andric 24068d75effSDimitry Andric void PoisonMemory(const void *dst, uptr size, StackTrace *stack) { 24168d75effSDimitry Andric SetShadow(dst, size, (u8)-1); 24268d75effSDimitry Andric 24368d75effSDimitry Andric if (__msan_get_track_origins()) { 24468d75effSDimitry Andric Origin o = Origin::CreateHeapOrigin(stack); 24568d75effSDimitry Andric SetOrigin(dst, size, o.raw_id()); 24668d75effSDimitry Andric } 24768d75effSDimitry Andric } 24868d75effSDimitry Andric 24968d75effSDimitry Andric } // namespace __msan 250