xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/msan/msan_poisoning.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
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