1251034Sed//===-- asan_poisoning.h ----------------------------------------*- C++ -*-===//
2251034Sed//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6251034Sed//
7251034Sed//===----------------------------------------------------------------------===//
8251034Sed//
9251034Sed// This file is a part of AddressSanitizer, an address sanity checker.
10251034Sed//
11251034Sed// Shadow memory poisoning by ASan RTL and by user application.
12251034Sed//===----------------------------------------------------------------------===//
13251034Sed
14251034Sed#include "asan_interceptors.h"
15251034Sed#include "asan_internal.h"
16251034Sed#include "asan_mapping.h"
17276789Sdim#include "sanitizer_common/sanitizer_flags.h"
18353358Sdim#include "sanitizer_common/sanitizer_platform.h"
19251034Sed
20251034Sednamespace __asan {
21251034Sed
22280031Sdim// Enable/disable memory poisoning.
23280031Sdimvoid SetCanPoisonMemory(bool value);
24280031Sdimbool CanPoisonMemory();
25280031Sdim
26251034Sed// Poisons the shadow memory for "size" bytes starting from "addr".
27251034Sedvoid PoisonShadow(uptr addr, uptr size, u8 value);
28251034Sed
29251034Sed// Poisons the shadow memory for "redzone_size" bytes starting from
30251034Sed// "addr + size".
31251034Sedvoid PoisonShadowPartialRightRedzone(uptr addr,
32251034Sed                                     uptr size,
33251034Sed                                     uptr redzone_size,
34251034Sed                                     u8 value);
35251034Sed
36251034Sed// Fast versions of PoisonShadow and PoisonShadowPartialRightRedzone that
37251034Sed// assume that memory addresses are properly aligned. Use in
38251034Sed// performance-critical code with care.
39251034SedALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
40251034Sed                                    u8 value) {
41341825Sdim  DCHECK(!value || CanPoisonMemory());
42353358Sdim#if SANITIZER_FUCHSIA
43353358Sdim  __sanitizer_fill_shadow(aligned_beg, aligned_size, value,
44353358Sdim                          common_flags()->clear_shadow_mmap_threshold);
45353358Sdim#else
46251034Sed  uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
47251034Sed  uptr shadow_end = MEM_TO_SHADOW(
48251034Sed      aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1;
49276789Sdim  // FIXME: Page states are different on Windows, so using the same interface
50276789Sdim  // for mapping shadow and zeroing out pages doesn't "just work", so we should
51276789Sdim  // probably provide higher-level interface for these operations.
52276789Sdim  // For now, just memset on Windows.
53327952Sdim  if (value || SANITIZER_WINDOWS == 1 ||
54341825Sdim      // RTEMS doesn't have have pages, let alone a fast way to zero
55341825Sdim      // them, so default to memset.
56341825Sdim      SANITIZER_RTEMS == 1 ||
57276789Sdim      shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
58276789Sdim    REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
59276789Sdim  } else {
60276789Sdim    uptr page_size = GetPageSizeCached();
61276789Sdim    uptr page_beg = RoundUpTo(shadow_beg, page_size);
62276789Sdim    uptr page_end = RoundDownTo(shadow_end, page_size);
63276789Sdim
64276789Sdim    if (page_beg >= page_end) {
65276789Sdim      REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg);
66276789Sdim    } else {
67276789Sdim      if (page_beg != shadow_beg) {
68276789Sdim        REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg);
69276789Sdim      }
70276789Sdim      if (page_end != shadow_end) {
71276789Sdim        REAL(memset)((void *)page_end, 0, shadow_end - page_end);
72276789Sdim      }
73288943Sdim      ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr);
74276789Sdim    }
75276789Sdim  }
76353358Sdim#endif // SANITIZER_FUCHSIA
77251034Sed}
78251034Sed
79251034SedALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
80251034Sed    uptr aligned_addr, uptr size, uptr redzone_size, u8 value) {
81280031Sdim  DCHECK(CanPoisonMemory());
82274201Sdim  bool poison_partial = flags()->poison_partial;
83251034Sed  u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr);
84251034Sed  for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) {
85251034Sed    if (i + SHADOW_GRANULARITY <= size) {
86251034Sed      *shadow = 0;  // fully addressable
87251034Sed    } else if (i >= size) {
88251034Sed      *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value;  // unaddressable
89251034Sed    } else {
90274201Sdim      // first size-i bytes are addressable
91274201Sdim      *shadow = poison_partial ? static_cast<u8>(size - i) : 0;
92251034Sed    }
93251034Sed  }
94251034Sed}
95251034Sed
96314564Sdim// Calls __sanitizer::ReleaseMemoryPagesToOS() on
97314564Sdim// [MemToShadow(p), MemToShadow(p+size)].
98276789Sdimvoid FlushUnneededASanShadowMemory(uptr p, uptr size);
99276789Sdim
100251034Sed}  // namespace __asan
101