1353944Sdim//===-- msan_poisoning.cpp --------------------------------------*- C++ -*-===//
2353944Sdim//
3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353944Sdim// See https://llvm.org/LICENSE.txt for license information.
5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353944Sdim//
7353944Sdim//===----------------------------------------------------------------------===//
8353944Sdim//
9353944Sdim// This file is a part of MemorySanitizer.
10353944Sdim//
11353944Sdim//===----------------------------------------------------------------------===//
12353944Sdim
13353944Sdim#include "msan_poisoning.h"
14353944Sdim
15353944Sdim#include "interception/interception.h"
16353944Sdim#include "msan_origin.h"
17353944Sdim#include "sanitizer_common/sanitizer_common.h"
18353944Sdim
19353944SdimDECLARE_REAL(void *, memset, void *dest, int c, uptr n)
20353944SdimDECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n)
21353944SdimDECLARE_REAL(void *, memmove, void *dest, const void *src, uptr n)
22353944Sdim
23353944Sdimnamespace __msan {
24353944Sdim
25353944Sdimu32 GetOriginIfPoisoned(uptr addr, uptr size) {
26353944Sdim  unsigned char *s = (unsigned char *)MEM_TO_SHADOW(addr);
27353944Sdim  for (uptr i = 0; i < size; ++i)
28353944Sdim    if (s[i]) return *(u32 *)SHADOW_TO_ORIGIN(((uptr)s + i) & ~3UL);
29353944Sdim  return 0;
30353944Sdim}
31353944Sdim
32353944Sdimvoid SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size,
33353944Sdim                         u32 src_origin) {
34353944Sdim  uptr dst_s = MEM_TO_SHADOW(addr);
35353944Sdim  uptr src_s = src_shadow;
36353944Sdim  uptr src_s_end = src_s + size;
37353944Sdim
38353944Sdim  for (; src_s < src_s_end; ++dst_s, ++src_s)
39353944Sdim    if (*(u8 *)src_s) *(u32 *)SHADOW_TO_ORIGIN(dst_s & ~3UL) = src_origin;
40353944Sdim}
41353944Sdim
42353944Sdimvoid CopyOrigin(const void *dst, const void *src, uptr size,
43353944Sdim                StackTrace *stack) {
44353944Sdim  if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return;
45353944Sdim
46353944Sdim  uptr d = (uptr)dst;
47353944Sdim  uptr beg = d & ~3UL;
48353944Sdim  // Copy left unaligned origin if that memory is poisoned.
49353944Sdim  if (beg < d) {
50353944Sdim    u32 o = GetOriginIfPoisoned((uptr)src, d - beg);
51353944Sdim    if (o) {
52353944Sdim      if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack);
53353944Sdim      *(u32 *)MEM_TO_ORIGIN(beg) = o;
54353944Sdim    }
55353944Sdim    beg += 4;
56353944Sdim  }
57353944Sdim
58353944Sdim  uptr end = (d + size) & ~3UL;
59353944Sdim  // If both ends fall into the same 4-byte slot, we are done.
60353944Sdim  if (end < beg) return;
61353944Sdim
62353944Sdim  // Copy right unaligned origin if that memory is poisoned.
63353944Sdim  if (end < d + size) {
64353944Sdim    u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end);
65353944Sdim    if (o) {
66353944Sdim      if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack);
67353944Sdim      *(u32 *)MEM_TO_ORIGIN(end) = o;
68353944Sdim    }
69353944Sdim  }
70353944Sdim
71353944Sdim  if (beg < end) {
72353944Sdim    // Align src up.
73353944Sdim    uptr s = ((uptr)src + 3) & ~3UL;
74353944Sdim    // FIXME: factor out to msan_copy_origin_aligned
75353944Sdim    if (__msan_get_track_origins() > 1) {
76353944Sdim      u32 *src = (u32 *)MEM_TO_ORIGIN(s);
77353944Sdim      u32 *src_s = (u32 *)MEM_TO_SHADOW(s);
78353944Sdim      u32 *src_end = (u32 *)MEM_TO_ORIGIN(s + (end - beg));
79353944Sdim      u32 *dst = (u32 *)MEM_TO_ORIGIN(beg);
80353944Sdim      u32 src_o = 0;
81353944Sdim      u32 dst_o = 0;
82353944Sdim      for (; src < src_end; ++src, ++src_s, ++dst) {
83353944Sdim        if (!*src_s) continue;
84353944Sdim        if (*src != src_o) {
85353944Sdim          src_o = *src;
86353944Sdim          dst_o = ChainOrigin(src_o, stack);
87353944Sdim        }
88353944Sdim        *dst = dst_o;
89353944Sdim      }
90353944Sdim    } else {
91353944Sdim      REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s),
92353944Sdim                   end - beg);
93353944Sdim    }
94353944Sdim  }
95353944Sdim}
96353944Sdim
97353944Sdimvoid MoveShadowAndOrigin(const void *dst, const void *src, uptr size,
98353944Sdim                         StackTrace *stack) {
99353944Sdim  if (!MEM_IS_APP(dst)) return;
100353944Sdim  if (!MEM_IS_APP(src)) return;
101353944Sdim  if (src == dst) return;
102353944Sdim  REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst),
103353944Sdim                (void *)MEM_TO_SHADOW((uptr)src), size);
104353944Sdim  if (__msan_get_track_origins()) CopyOrigin(dst, src, size, stack);
105353944Sdim}
106353944Sdim
107353944Sdimvoid CopyShadowAndOrigin(const void *dst, const void *src, uptr size,
108353944Sdim                         StackTrace *stack) {
109353944Sdim  if (!MEM_IS_APP(dst)) return;
110353944Sdim  if (!MEM_IS_APP(src)) return;
111353944Sdim  REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst),
112353944Sdim               (void *)MEM_TO_SHADOW((uptr)src), size);
113353944Sdim  if (__msan_get_track_origins()) CopyOrigin(dst, src, size, stack);
114353944Sdim}
115353944Sdim
116353944Sdimvoid CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) {
117353944Sdim  REAL(memcpy)(dst, src, size);
118353944Sdim  CopyShadowAndOrigin(dst, src, size, stack);
119353944Sdim}
120353944Sdim
121353944Sdimvoid SetShadow(const void *ptr, uptr size, u8 value) {
122353944Sdim  uptr PageSize = GetPageSizeCached();
123353944Sdim  uptr shadow_beg = MEM_TO_SHADOW(ptr);
124353944Sdim  uptr shadow_end = shadow_beg + size;
125353944Sdim  if (value ||
126353944Sdim      shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
127353944Sdim    REAL(memset)((void *)shadow_beg, value, shadow_end - shadow_beg);
128353944Sdim  } else {
129353944Sdim    uptr page_beg = RoundUpTo(shadow_beg, PageSize);
130353944Sdim    uptr page_end = RoundDownTo(shadow_end, PageSize);
131353944Sdim
132353944Sdim    if (page_beg >= page_end) {
133353944Sdim      REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
134353944Sdim    } else {
135353944Sdim      if (page_beg != shadow_beg) {
136353944Sdim        REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg);
137353944Sdim      }
138353944Sdim      if (page_end != shadow_end) {
139353944Sdim        REAL(memset)((void *)page_end, 0, shadow_end - page_end);
140353944Sdim      }
141353944Sdim      if (!MmapFixedNoReserve(page_beg, page_end - page_beg))
142353944Sdim        Die();
143353944Sdim    }
144353944Sdim  }
145353944Sdim}
146353944Sdim
147353944Sdimvoid SetOrigin(const void *dst, uptr size, u32 origin) {
148353944Sdim  // Origin mapping is 4 bytes per 4 bytes of application memory.
149353944Sdim  // Here we extend the range such that its left and right bounds are both
150353944Sdim  // 4 byte aligned.
151353944Sdim  uptr x = MEM_TO_ORIGIN((uptr)dst);
152353944Sdim  uptr beg = x & ~3UL;               // align down.
153353944Sdim  uptr end = (x + size + 3) & ~3UL;  // align up.
154353944Sdim  u64 origin64 = ((u64)origin << 32) | origin;
155353944Sdim  // This is like memset, but the value is 32-bit. We unroll by 2 to write
156353944Sdim  // 64 bits at once. May want to unroll further to get 128-bit stores.
157353944Sdim  if (beg & 7ULL) {
158353944Sdim    *(u32 *)beg = origin;
159353944Sdim    beg += 4;
160353944Sdim  }
161353944Sdim  for (uptr addr = beg; addr < (end & ~7UL); addr += 8) *(u64 *)addr = origin64;
162353944Sdim  if (end & 7ULL) *(u32 *)(end - 4) = origin;
163353944Sdim}
164353944Sdim
165353944Sdimvoid PoisonMemory(const void *dst, uptr size, StackTrace *stack) {
166353944Sdim  SetShadow(dst, size, (u8)-1);
167353944Sdim
168353944Sdim  if (__msan_get_track_origins()) {
169353944Sdim    Origin o = Origin::CreateHeapOrigin(stack);
170353944Sdim    SetOrigin(dst, size, o.raw_id());
171353944Sdim  }
172353944Sdim}
173353944Sdim
174353944Sdim}  // namespace __msan
175