blob: e9cdab64abe1aa7c38ad96688c636b09b61d927a [file] [log] [blame]
Evgenii Stepanov0a3637d2016-07-06 13:20:59 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "linker_cfi.h"
18
19#include "linker_debug.h"
20#include "linker_globals.h"
21#include "private/bionic_page.h"
22#include "private/bionic_prctl.h"
23
24#include <sys/mman.h>
25#include <sys/types.h>
26#include <cstdint>
27
28// Update shadow without making it writable by preparing the data on the side and mremap-ing it in
29// place.
30class ShadowWrite {
31 char* shadow_start;
32 char* shadow_end;
33 char* aligned_start;
34 char* aligned_end;
35 char* tmp_start;
36
37 public:
38 ShadowWrite(uint16_t* s, uint16_t* e) {
39 shadow_start = reinterpret_cast<char*>(s);
40 shadow_end = reinterpret_cast<char*>(e);
41 aligned_start = reinterpret_cast<char*>(PAGE_START(reinterpret_cast<uintptr_t>(shadow_start)));
42 aligned_end = reinterpret_cast<char*>(PAGE_END(reinterpret_cast<uintptr_t>(shadow_end)));
43 tmp_start =
44 reinterpret_cast<char*>(mmap(nullptr, aligned_end - aligned_start, PROT_READ | PROT_WRITE,
45 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
46 CHECK(tmp_start != MAP_FAILED);
47 memcpy(tmp_start, aligned_start, shadow_start - aligned_start);
48 memcpy(tmp_start + (shadow_end - aligned_start), shadow_end, aligned_end - shadow_end);
49 }
50
51 uint16_t* begin() {
52 return reinterpret_cast<uint16_t*>(tmp_start + (shadow_start - aligned_start));
53 }
54
55 uint16_t* end() {
56 return reinterpret_cast<uint16_t*>(tmp_start + (shadow_end - aligned_start));
57 }
58
59 ~ShadowWrite() {
60 size_t size = aligned_end - aligned_start;
61 mprotect(tmp_start, size, PROT_READ);
62 void* res = mremap(tmp_start, size, size, MREMAP_MAYMOVE | MREMAP_FIXED,
63 reinterpret_cast<void*>(aligned_start));
64 CHECK(res != MAP_FAILED);
65 }
66};
67
68void CFIShadowWriter::FixupVmaName() {
69 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, *shadow_start, kShadowSize, "cfi shadow");
70}
71
72void CFIShadowWriter::AddConstant(uintptr_t begin, uintptr_t end, uint16_t v) {
73 uint16_t* shadow_begin = MemToShadow(begin);
74 uint16_t* shadow_end = MemToShadow(end - 1) + 1;
75
76 ShadowWrite sw(shadow_begin, shadow_end);
77 std::fill(sw.begin(), sw.end(), v);
78}
79
80void CFIShadowWriter::AddUnchecked(uintptr_t begin, uintptr_t end) {
81 AddConstant(begin, end, kUncheckedShadow);
82}
83
84void CFIShadowWriter::AddInvalid(uintptr_t begin, uintptr_t end) {
85 AddConstant(begin, end, kInvalidShadow);
86}
87
88void CFIShadowWriter::Add(uintptr_t begin, uintptr_t end, uintptr_t cfi_check) {
89 CHECK((cfi_check & (kCfiCheckAlign - 1)) == 0);
90
91 // Don't fill anything below cfi_check. We can not represent those addresses
92 // in the shadow, and must make sure at codegen to place all valid call
93 // targets above cfi_check.
94 begin = std::max(begin, cfi_check) & ~(kShadowAlign - 1);
95 uint16_t* shadow_begin = MemToShadow(begin);
96 uint16_t* shadow_end = MemToShadow(end - 1) + 1;
97
98 ShadowWrite sw(shadow_begin, shadow_end);
Evgenii Stepanov636a2ec2017-01-20 13:47:04 -080099 uint16_t sv_begin = ((begin + kShadowAlign - cfi_check) >> kCfiCheckGranularity) + kRegularShadowMin;
Evgenii Stepanov0a3637d2016-07-06 13:20:59 -0700100
101 // With each step of the loop below, __cfi_check address computation base is increased by
102 // 2**ShadowGranularity.
103 // To compensate for that, each next shadow value must be increased by 2**ShadowGranularity /
104 // 2**CfiCheckGranularity.
105 uint16_t sv_step = 1 << (kShadowGranularity - kCfiCheckGranularity);
Evgenii Stepanov636a2ec2017-01-20 13:47:04 -0800106 uint16_t sv = sv_begin;
Evgenii Stepanov0a3637d2016-07-06 13:20:59 -0700107 for (uint16_t& s : sw) {
Evgenii Stepanov636a2ec2017-01-20 13:47:04 -0800108 if (sv < sv_begin) {
109 // If shadow value wraps around, also fall back to unchecked. This means the binary is too
110 // large. FIXME: consider using a (slow) resolution function instead.
111 s = kUncheckedShadow;
112 continue;
113 }
Evgenii Stepanov0a3637d2016-07-06 13:20:59 -0700114 // If there is something there already, fall back to unchecked. This may happen in rare cases
115 // with MAP_FIXED libraries. FIXME: consider using a (slow) resolution function instead.
116 s = (s == kInvalidShadow) ? sv : kUncheckedShadow;
117 sv += sv_step;
118 }
119}
120
121static soinfo* find_libdl(soinfo* solist) {
122 for (soinfo* si = solist; si != nullptr; si = si->next) {
123 const char* soname = si->get_soname();
124 if (soname && strcmp(soname, "libdl.so") == 0) {
125 return si;
126 }
127 }
128 return nullptr;
129}
130
131static uintptr_t soinfo_find_symbol(soinfo* si, const char* s) {
132 SymbolName name(s);
133 const ElfW(Sym) * sym;
134 if (si->find_symbol_by_name(name, nullptr, &sym) && sym) {
135 return si->resolve_symbol_address(sym);
136 }
137 return 0;
138}
139
140uintptr_t soinfo_find_cfi_check(soinfo* si) {
141 return soinfo_find_symbol(si, "__cfi_check");
142}
143
144uintptr_t CFIShadowWriter::MapShadow() {
145 void* p =
146 mmap(nullptr, kShadowSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
147 CHECK(p != MAP_FAILED);
148 return reinterpret_cast<uintptr_t>(p);
149}
150
151bool CFIShadowWriter::AddLibrary(soinfo* si) {
152 CHECK(shadow_start != nullptr);
153 if (si->base == 0 || si->size == 0) {
154 return true;
155 }
156 uintptr_t cfi_check = soinfo_find_cfi_check(si);
157 if (cfi_check == 0) {
158 INFO("[ CFI add 0x%zx + 0x%zx %s ]", static_cast<uintptr_t>(si->base),
159 static_cast<uintptr_t>(si->size), si->get_soname());
160 AddUnchecked(si->base, si->base + si->size);
161 return true;
162 }
163
164 INFO("[ CFI add 0x%zx + 0x%zx %s: 0x%zx ]", static_cast<uintptr_t>(si->base),
165 static_cast<uintptr_t>(si->size), si->get_soname(), cfi_check);
166#ifdef __arm__
167 // Require Thumb encoding.
168 if ((cfi_check & 1UL) != 1UL) {
169 DL_ERR("__cfi_check in not a Thumb function in the library \"%s\"", si->get_soname());
170 return false;
171 }
172 cfi_check &= ~1UL;
173#endif
174 if ((cfi_check & (kCfiCheckAlign - 1)) != 0) {
175 DL_ERR("unaligned __cfi_check in the library \"%s\"", si->get_soname());
176 return false;
177 }
178 Add(si->base, si->base + si->size, cfi_check);
179 return true;
180}
181
182// Pass the shadow mapping address to libdl.so. In return, we get an pointer to the location
183// libdl.so uses to store the address.
184bool CFIShadowWriter::NotifyLibDl(soinfo* solist, uintptr_t p) {
185 soinfo* libdl = find_libdl(solist);
186 if (libdl == nullptr) {
187 DL_ERR("CFI could not find libdl");
188 return false;
189 }
190
191 uintptr_t cfi_init = soinfo_find_symbol(libdl, "__cfi_init");
192 CHECK(cfi_init != 0);
193 shadow_start = reinterpret_cast<uintptr_t* (*)(uintptr_t)>(cfi_init)(p);
194 CHECK(shadow_start != nullptr);
195 CHECK(*shadow_start == p);
196 return true;
197}
198
199bool CFIShadowWriter::MaybeInit(soinfo* new_si, soinfo* solist) {
200 CHECK(initial_link_done);
Evgenii Stepanov636a2ec2017-01-20 13:47:04 -0800201 CHECK(shadow_start == nullptr);
Evgenii Stepanov0a3637d2016-07-06 13:20:59 -0700202 // Check if CFI shadow must be initialized at this time.
203 bool found = false;
204 if (new_si == nullptr) {
205 // This is the case when we've just completed the initial link. There may have been earlier
206 // calls to MaybeInit that were skipped. Look though the entire solist.
207 for (soinfo* si = solist; si != nullptr; si = si->next) {
208 if (soinfo_find_cfi_check(si)) {
209 found = true;
210 break;
211 }
212 }
213 } else {
214 // See if the new library uses CFI.
215 found = soinfo_find_cfi_check(new_si);
216 }
217
218 // Nothing found.
219 if (!found) {
220 return true;
221 }
222
223 // Init shadow and add all currently loaded libraries (not just the new ones).
224 if (!NotifyLibDl(solist, MapShadow()))
225 return false;
226 for (soinfo* si = solist; si != nullptr; si = si->next) {
227 if (!AddLibrary(si))
228 return false;
229 }
230 FixupVmaName();
231 return true;
232}
233
234bool CFIShadowWriter::AfterLoad(soinfo* si, soinfo* solist) {
235 if (!initial_link_done) {
236 // Too early.
237 return true;
238 }
239
240 if (shadow_start == nullptr) {
241 return MaybeInit(si, solist);
242 }
243
244 // Add the new library to the CFI shadow.
245 if (!AddLibrary(si))
246 return false;
247 FixupVmaName();
248 return true;
249}
250
251void CFIShadowWriter::BeforeUnload(soinfo* si) {
252 if (shadow_start == nullptr) return;
253 if (si->base == 0 || si->size == 0) return;
254 INFO("[ CFI remove 0x%zx + 0x%zx: %s ]", static_cast<uintptr_t>(si->base),
255 static_cast<uintptr_t>(si->size), si->get_soname());
256 AddInvalid(si->base, si->base + si->size);
257 FixupVmaName();
258}
259
260bool CFIShadowWriter::InitialLinkDone(soinfo* solist) {
Evgenii Stepanov636a2ec2017-01-20 13:47:04 -0800261 CHECK(!initial_link_done);
Evgenii Stepanov0a3637d2016-07-06 13:20:59 -0700262 initial_link_done = true;
263 return MaybeInit(nullptr, solist);
264}
265
266// Find __cfi_check in the caller and let it handle the problem. Since caller_pc is likely not a
267// valid CFI target, we can not use CFI shadow for lookup. This does not need to be fast, do the
268// regular symbol lookup.
269void CFIShadowWriter::CfiFail(uint64_t CallSiteTypeId, void* Ptr, void* DiagData, void* CallerPc) {
270 soinfo* si = find_containing_library(CallerPc);
271 if (!si) {
272 __builtin_trap();
273 }
274
275 uintptr_t cfi_check = soinfo_find_cfi_check(si);
276 if (!cfi_check) {
277 __builtin_trap();
278 }
279
280 reinterpret_cast<CFICheckFn>(cfi_check)(CallSiteTypeId, Ptr, DiagData);
281}