platform_bionic/libdl/libdl_cfi.cpp
Peter Collingbourne 45f0a3b642 Clear pointer tags as required for HWASAN for globals.
A future version of HWASAN will set pointer tags when taking the address of
a global. This means that we need to untag pointers in a couple of cases
where potential global pointers are passed to an interface that expects
untagged pointers:

- The WriteProtected class, whose only instances are globals, passes its
  own address to mprotect. However, our device kernels do not currently
  untag pointers passed to mprotect (the proposed upstream kernel patches
  do, however, untag these pointers), so once HWASAN starts tagging global
  pointers, this will start failing.
- The shadow_load function loads from a shadow that corresponds to the
  address space bounds of loaded binaries. Since these address space
  bounds are untagged, the pointer needs to be untagged to match.

Test: boots
Change-Id: I3f11ce6eb7261752e5ff6d039d04dd45516b236f
2019-07-16 13:38:38 -07:00

91 lines
3.5 KiB
C++

/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sys/mman.h>
#include "private/CFIShadow.h"
__attribute__((__weak__, visibility("default"))) extern "C" void __loader_cfi_fail(
uint64_t CallSiteTypeId, void* Ptr, void* DiagData, void* CallerPc);
// Base address of the CFI shadow. Passed down from the linker in __cfi_init()
// and does not change after that. The contents of the shadow change in
// dlopen/dlclose.
static struct {
uintptr_t v;
char padding[PAGE_SIZE - sizeof(v)];
} shadow_base_storage alignas(PAGE_SIZE);
// __cfi_init is called by the loader as soon as the shadow is mapped. This may happen very early
// during startup, before libdl.so global constructors, and, on i386, even before __libc_sysinfo is
// initialized. This function should not do any system calls.
extern "C" uintptr_t* __cfi_init(uintptr_t shadow_base) {
shadow_base_storage.v = shadow_base;
static_assert(sizeof(shadow_base_storage) == PAGE_SIZE, "");
return &shadow_base_storage.v;
}
// Returns the size of the CFI shadow mapping, or 0 if CFI is not (yet) used in this process.
extern "C" size_t __cfi_shadow_size() {
return shadow_base_storage.v != 0 ? CFIShadow::kShadowSize : 0;
}
static uint16_t shadow_load(void* p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
#ifdef __aarch64__
// Untag the pointer to move it into the address space covered by the shadow.
addr &= (1ULL << 56) - 1;
#endif
uintptr_t ofs = CFIShadow::MemToShadowOffset(addr);
if (ofs > CFIShadow::kShadowSize) return CFIShadow::kInvalidShadow;
return *reinterpret_cast<uint16_t*>(shadow_base_storage.v + ofs);
}
static uintptr_t cfi_check_addr(uint16_t v, void* Ptr) {
uintptr_t addr = reinterpret_cast<uintptr_t>(Ptr);
// The aligned range of [0, kShadowAlign) uses a single shadow element, therefore all pointers in
// this range must get the same aligned_addr below. This matches CFIShadowWriter::Add; not the
// same as align_up().
uintptr_t aligned_addr = align_down(addr, CFIShadow::kShadowAlign) + CFIShadow::kShadowAlign;
uintptr_t p = aligned_addr - (static_cast<uintptr_t>(v - CFIShadow::kRegularShadowMin)
<< CFIShadow::kCfiCheckGranularity);
#ifdef __arm__
// Assume Thumb encoding. FIXME: force thumb at compile time?
p++;
#endif
return p;
}
static inline void cfi_slowpath_common(uint64_t CallSiteTypeId, void* Ptr, void* DiagData) {
uint16_t v = shadow_load(Ptr);
switch (v) {
case CFIShadow::kInvalidShadow:
__loader_cfi_fail(CallSiteTypeId, Ptr, DiagData, __builtin_return_address(0));
break;
case CFIShadow::kUncheckedShadow:
break;
default:
reinterpret_cast<CFIShadow::CFICheckFn>(cfi_check_addr(v, Ptr))(CallSiteTypeId, Ptr, DiagData);
}
}
extern "C" void __cfi_slowpath(uint64_t CallSiteTypeId, void* Ptr) {
cfi_slowpath_common(CallSiteTypeId, Ptr, nullptr);
}
extern "C" void __cfi_slowpath_diag(uint64_t CallSiteTypeId, void* Ptr, void* DiagData) {
cfi_slowpath_common(CallSiteTypeId, Ptr, DiagData);
}