aboutsummaryrefslogtreecommitdiff
path: root/compiler-rt/lib/hwasan/hwasan_dynamic_shadow.cpp
blob: 7642ba6c0bf08fddee2f820a2b4016b6deec0488 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//===-- hwasan_dynamic_shadow.cpp -------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file is a part of HWAddressSanitizer. It reserves dynamic shadow memory
/// region and handles ifunc resolver case, when necessary.
///
//===----------------------------------------------------------------------===//

#include "hwasan_dynamic_shadow.h"

#include <elf.h>
#include <link.h>

#include "hwasan.h"
#include "hwasan_mapping.h"
#include "hwasan_thread_list.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_posix.h"

// The code in this file needs to run in an unrelocated binary. It should not
// access any external symbol, including its own non-hidden globals.

#if SANITIZER_ANDROID
extern "C" {

INTERFACE_ATTRIBUTE void __hwasan_shadow();
decltype(__hwasan_shadow)* __hwasan_premap_shadow();

}  // extern "C"

namespace __hwasan {

// Conservative upper limit.
static uptr PremapShadowSize() {
  return RoundUpTo(GetMaxVirtualAddress() >> kShadowScale,
                   GetMmapGranularity());
}

static uptr PremapShadow() {
  return MapDynamicShadow(PremapShadowSize(), kShadowScale,
                          kShadowBaseAlignment, kHighMemEnd);
}

static bool IsPremapShadowAvailable() {
  const uptr shadow = reinterpret_cast<uptr>(&__hwasan_shadow);
  const uptr resolver = reinterpret_cast<uptr>(&__hwasan_premap_shadow);
  // shadow == resolver is how Android KitKat and older handles ifunc.
  // shadow == 0 just in case.
  return shadow != 0 && shadow != resolver;
}

static uptr FindPremappedShadowStart(uptr shadow_size_bytes) {
  const uptr granularity = GetMmapGranularity();
  const uptr shadow_start = reinterpret_cast<uptr>(&__hwasan_shadow);
  const uptr premap_shadow_size = PremapShadowSize();
  const uptr shadow_size = RoundUpTo(shadow_size_bytes, granularity);

  // We may have mapped too much. Release extra memory.
  UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size);
  return shadow_start;
}

}  // namespace __hwasan

extern "C" {

decltype(__hwasan_shadow)* __hwasan_premap_shadow() {
  // The resolver might be called multiple times. Map the shadow just once.
  static __sanitizer::uptr shadow = 0;
  if (!shadow)
    shadow = __hwasan::PremapShadow();
  return reinterpret_cast<decltype(__hwasan_shadow)*>(shadow);
}

// __hwasan_shadow is a "function" that has the same address as the first byte
// of the shadow mapping.
INTERFACE_ATTRIBUTE __attribute__((ifunc("__hwasan_premap_shadow")))
void __hwasan_shadow();

extern __attribute((weak, visibility("hidden"))) ElfW(Rela) __rela_iplt_start[],
    __rela_iplt_end[];

}  // extern "C"

namespace __hwasan {

void InitShadowGOT() {
  // Call the ifunc resolver for __hwasan_shadow and fill in its GOT entry. This
  // needs to be done before other ifunc resolvers (which are handled by libc)
  // because a resolver might read __hwasan_shadow.
  typedef ElfW(Addr) (*ifunc_resolver_t)(void);
  for (ElfW(Rela) *r = __rela_iplt_start; r != __rela_iplt_end; ++r) {
    ElfW(Addr)* offset = reinterpret_cast<ElfW(Addr)*>(r->r_offset);
    ElfW(Addr) resolver = r->r_addend;
    if (resolver == reinterpret_cast<ElfW(Addr)>(&__hwasan_premap_shadow)) {
      *offset = reinterpret_cast<ifunc_resolver_t>(resolver)();
      break;
    }
  }
}

uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
  if (IsPremapShadowAvailable())
    return FindPremappedShadowStart(shadow_size_bytes);
  return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
                          kHighMemEnd);
}

}  // namespace __hwasan

#elif SANITIZER_FUCHSIA

namespace __hwasan {

void InitShadowGOT() {}

}  // namespace __hwasan

#else
namespace __hwasan {

void InitShadowGOT() {}

uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
#  if defined(HWASAN_ALIASING_MODE)
  constexpr uptr kAliasSize = 1ULL << kAddressTagShift;
  constexpr uptr kNumAliases = 1ULL << kTagBits;
  return MapDynamicShadowAndAliases(shadow_size_bytes, kAliasSize, kNumAliases,
                                    RingBufferSize());
#  endif
  return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
                          kHighMemEnd);
}

}  // namespace __hwasan

#endif  // SANITIZER_ANDROID