Skip to content
This repository has been archived by the owner on Nov 8, 2023. It is now read-only.

Commit

Permalink
Merge "Runtime support for CFI"
Browse files Browse the repository at this point in the history
  • Loading branch information
eugenis authored and Gerrit Code Review committed Jan 18, 2017
2 parents 2d261e8 + 0a3637d commit 082bec5
Show file tree
Hide file tree
Showing 25 changed files with 791 additions and 16 deletions.
1 change: 1 addition & 0 deletions libc/include/dlfcn.h
Expand Up @@ -29,6 +29,7 @@
#ifndef __DLFCN_H__
#define __DLFCN_H__

#include <stdint.h>
#include <sys/cdefs.h>

__BEGIN_DECLS
Expand Down
93 changes: 93 additions & 0 deletions libc/private/CFIShadow.h
@@ -0,0 +1,93 @@
/*
* 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.
*/

#ifndef CFI_SHADOW_H
#define CFI_SHADOW_H

#include <stdint.h>

#include "private/bionic_page.h"
#include "private/bionic_macros.h"

constexpr unsigned kLibraryAlignmentBits = 18;
constexpr size_t kLibraryAlignment = 1UL << kLibraryAlignmentBits;

// This class defines format of the shadow region for Control Flow Integrity support.
// See documentation in http://clang.llvm.org/docs/ControlFlowIntegrityDesign.html#shared-library-support.
//
// CFI shadow is effectively a very fast and specialized implementation of dladdr: given an address that
// belongs to a shared library or an executable, it can find the address of a specific export in that
// library (a function called "__cfi_check"). This is only guaranteed to work for
// addresses of possible CFI targets inside a library: indirectly called functions and virtual
// tables. A random address inside a library may not work in the future (but it does in the current
// implementation).
//
// Implementation is a sparse array of uint16_t where each element describes the location of
// __cfi_check for a 2**kShadowGranularity range of memory. Array elements (called "shadow values"
// below) are interpreted as follows.
//
// For an address P and corresponding shadow value V, the address of __cfi_check is calculated as
// align_up(P, 2**kShadowGranularity) - (V - 2) * (2 ** kCfiCheckGranularity)
//
// Special shadow values:
// 0 = kInvalidShadow, this memory range has no valid CFI targets.
// 1 = kUncheckedShadow, any address is this memory range is a valid CFI target
//
// Loader requirement: each aligned 2**kShadowGranularity region of address space may contain at
// most one DSO.
// Compiler requirement: __cfi_check is aligned at kCfiCheckGranularity.
// Compiler requirement: __cfi_check for a given DSO is located below any CFI target for that DSO.
class CFIShadow {
public:
static constexpr uintptr_t kShadowGranularity = kLibraryAlignmentBits;
static constexpr uintptr_t kCfiCheckGranularity = 12;

// Each uint16_t element of the shadow corresponds to this much application memory.
static constexpr uintptr_t kShadowAlign = 1UL << kShadowGranularity;

// Alignment of __cfi_check.
static constexpr uintptr_t kCfiCheckAlign = 1UL << kCfiCheckGranularity; // 4K

#if defined(__aarch64__)
static constexpr uintptr_t kMaxTargetAddr = 0x7fffffffff;
#elif defined (__LP64__)
static constexpr uintptr_t kMaxTargetAddr = 0x7fffffffffff;
#else
static constexpr uintptr_t kMaxTargetAddr = 0xffffffff;
#endif

// Shadow is 2 -> 2**kShadowGranularity.
static constexpr uintptr_t kShadowSize =
align_up((kMaxTargetAddr >> (kShadowGranularity - 1)), PAGE_SIZE);

// Returns offset inside the shadow region for an address.
static constexpr uintptr_t MemToShadowOffset(uintptr_t x) {
return (x >> kShadowGranularity) << 1;
}

typedef int (*CFICheckFn)(uint64_t, void *, void *);

public:
enum ShadowValues : uint16_t {
kInvalidShadow = 0, // Not a valid CFI target.
kUncheckedShadow = 1, // Unchecked, valid CFI target.
kRegularShadowMin = 2 // This and all higher values encode a negative offset to __cfi_check in
// the units of kCfiCheckGranularity, starting with 0 at
// kRegularShadowMin.
};
};

#endif // CFI_SHADOW_H
4 changes: 2 additions & 2 deletions libc/private/bionic_macros.h
Expand Up @@ -48,11 +48,11 @@
? (1UL << (64 - __builtin_clzl(static_cast<unsigned long>(value)))) \
: (1UL << (32 - __builtin_clz(static_cast<unsigned int>(value)))))

static inline uintptr_t align_down(uintptr_t p, size_t align) {
static constexpr uintptr_t align_down(uintptr_t p, size_t align) {
return p & ~(align - 1);
}

static inline uintptr_t align_up(uintptr_t p, size_t align) {
static constexpr uintptr_t align_up(uintptr_t p, size_t align) {
return (p + align - 1) & ~(align - 1);
}

Expand Down
5 changes: 4 additions & 1 deletion libdl/Android.bp
Expand Up @@ -44,7 +44,7 @@ cc_library {
version_script: "libdl.x86_64.map",
},
},
srcs: ["libdl.c"],
srcs: ["libdl.c", "libdl_cfi.cpp"],
cflags: [
"-Wall",
"-Wextra",
Expand All @@ -61,6 +61,9 @@ cc_library {
allow_undefined_symbols: true,
system_shared_libs: [],

// For private/CFIShadow.h.
include_dirs: ["bionic/libc"],

// This is placeholder library the actual implementation is (currently)
// provided by the linker.
shared_libs: [ "ld-android" ],
Expand Down
3 changes: 3 additions & 0 deletions libdl/libdl.arm.map
Expand Up @@ -36,6 +36,9 @@ LIBC_N {

LIBC_PLATFORM {
global:
__cfi_init;
__cfi_slowpath;
__cfi_slowpath_diag;
android_dlwarning;
android_get_application_target_sdk_version;
android_set_application_target_sdk_version;
Expand Down
3 changes: 3 additions & 0 deletions libdl/libdl.arm64.map
Expand Up @@ -35,6 +35,9 @@ LIBC_N {

LIBC_PLATFORM {
global:
__cfi_init;
__cfi_slowpath;
__cfi_slowpath_diag;
android_dlwarning;
android_get_application_target_sdk_version;
android_set_application_target_sdk_version;
Expand Down
3 changes: 3 additions & 0 deletions libdl/libdl.map.txt
Expand Up @@ -35,6 +35,9 @@ LIBC_N {

LIBC_PLATFORM {
global:
__cfi_init;
__cfi_slowpath;
__cfi_slowpath_diag;
android_dlwarning;
android_get_application_target_sdk_version;
android_set_application_target_sdk_version;
Expand Down
3 changes: 3 additions & 0 deletions libdl/libdl.mips.map
Expand Up @@ -35,6 +35,9 @@ LIBC_N {

LIBC_PLATFORM {
global:
__cfi_init;
__cfi_slowpath;
__cfi_slowpath_diag;
android_dlwarning;
android_get_application_target_sdk_version;
android_set_application_target_sdk_version;
Expand Down
3 changes: 3 additions & 0 deletions libdl/libdl.mips64.map
Expand Up @@ -35,6 +35,9 @@ LIBC_N {

LIBC_PLATFORM {
global:
__cfi_init;
__cfi_slowpath;
__cfi_slowpath_diag;
android_dlwarning;
android_get_application_target_sdk_version;
android_set_application_target_sdk_version;
Expand Down
3 changes: 3 additions & 0 deletions libdl/libdl.x86.map
Expand Up @@ -35,6 +35,9 @@ LIBC_N {

LIBC_PLATFORM {
global:
__cfi_init;
__cfi_slowpath;
__cfi_slowpath_diag;
android_dlwarning;
android_get_application_target_sdk_version;
android_set_application_target_sdk_version;
Expand Down
3 changes: 3 additions & 0 deletions libdl/libdl.x86_64.map
Expand Up @@ -35,6 +35,9 @@ LIBC_N {

LIBC_PLATFORM {
global:
__cfi_init;
__cfi_slowpath;
__cfi_slowpath_diag;
android_dlwarning;
android_get_application_target_sdk_version;
android_set_application_target_sdk_version;
Expand Down
77 changes: 77 additions & 0 deletions libdl/libdl_cfi.cpp
@@ -0,0 +1,77 @@
/*
* 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);

extern "C" uintptr_t* __cfi_init(uintptr_t shadow_base) {
shadow_base_storage.v = shadow_base;
static_assert(sizeof(shadow_base_storage) == PAGE_SIZE, "");
mprotect(&shadow_base_storage, PAGE_SIZE, PROT_READ);
return &shadow_base_storage.v;
}

static uint16_t shadow_load(void* p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
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);
uintptr_t aligned_addr = align_up(addr, 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);
}
1 change: 1 addition & 0 deletions linker/Android.bp
Expand Up @@ -18,6 +18,7 @@ cc_binary {
"linker.cpp",
"linker_block_allocator.cpp",
"linker_dlwarning.cpp",
"linker_cfi.cpp",
"linker_gdb_support.cpp",
"linker_globals.cpp",
"linker_libc_support.c",
Expand Down
20 changes: 13 additions & 7 deletions linker/dlfcn.cpp
Expand Up @@ -15,6 +15,7 @@
*/

#include "linker.h"
#include "linker_cfi.h"
#include "linker_globals.h"
#include "linker_dlwarning.h"

Expand Down Expand Up @@ -189,6 +190,10 @@ android_namespace_t* __android_create_namespace(const char* name,
return result;
}

void __cfi_fail(uint64_t CallSiteTypeId, void* Ptr, void *DiagData, void *CallerPc) {
CFIShadowWriter::CfiFail(CallSiteTypeId, Ptr, DiagData, CallerPc);
}

// name_offset: starting index of the name in libdl_info.strtab
#define ELF32_SYM_INITIALIZER(name_offset, value, shndx) \
{ name_offset, \
Expand Down Expand Up @@ -225,11 +230,11 @@ static const char ANDROID_LIBDL_STRTAB[] =
// 012345678901234 567890123456789012345678901234567 8901234567890123456789012345678901 2345678901234567 89
"et_sdk_version\0__loader_android_init_namespaces\0__loader_android_create_namespace\0__loader_dlvsym\0__"
// 4*
// 0000000000111111111122222 2222233333333334444444 4445555555555666666666677777777778 8888888889999999 999
// 0123456789012345678901234 5678901234567890123456 7890123456789012345678901234567890 1234567890123456 789
"loader_android_dlwarning\0"
// 0000000000111111111122222 222223333333333444 4444444555555555566666666667777 77777788888888889999999999
// 0123456789012345678901234 567890123456789012 3456789012345678901234567890123 45678901234567890123456789
"loader_android_dlwarning\0__loader_cfi_fail\0"
#if defined(__arm__)
// 425
// 443
"__loader_dl_unwind_find_exidx\0"
#endif
;
Expand All @@ -255,8 +260,9 @@ static ElfW(Sym) g_libdl_symtab[] = {
ELFW(SYM_INITIALIZER)(348, &__android_create_namespace, 1),
ELFW(SYM_INITIALIZER)(382, &__dlvsym, 1),
ELFW(SYM_INITIALIZER)(398, &__android_dlwarning, 1),
ELFW(SYM_INITIALIZER)(425, &__cfi_fail, 1),
#if defined(__arm__)
ELFW(SYM_INITIALIZER)(425, &__dl_unwind_find_exidx, 1),
ELFW(SYM_INITIALIZER)(443, &__dl_unwind_find_exidx, 1),
#endif
};

Expand All @@ -273,9 +279,9 @@ static ElfW(Sym) g_libdl_symtab[] = {
// Note that adding any new symbols here requires stubbing them out in libdl.
static unsigned g_libdl_buckets[1] = { 1 };
#if defined(__arm__)
static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 };
static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0 };
#else
static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 };
static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 };
#endif

static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8)));
Expand Down
13 changes: 11 additions & 2 deletions linker/linker.cpp
Expand Up @@ -48,6 +48,7 @@

#include "linker.h"
#include "linker_block_allocator.h"
#include "linker_cfi.h"
#include "linker_gdb_support.h"
#include "linker_globals.h"
#include "linker_debug.h"
Expand Down Expand Up @@ -105,6 +106,12 @@ static const char* const kAsanDefaultLdPaths[] = {
// Is ASAN enabled?
static bool g_is_asan = false;

static CFIShadowWriter g_cfi_shadow;

CFIShadowWriter* get_cfi_shadow() {
return &g_cfi_shadow;
}

static bool is_system_library(const std::string& realpath) {
for (const auto& dir : g_default_namespace.get_default_library_paths()) {
if (file_is_in_dir(realpath, dir)) {
Expand Down Expand Up @@ -1226,7 +1233,7 @@ static bool load_library(android_namespace_t* ns,
// target_sdk_version (*candidate != nullptr)
// 2. The library was not found by soname (*candidate is nullptr)
static bool find_loaded_library_by_soname(android_namespace_t* ns,
const char* name, soinfo** candidate) {
const char* name, soinfo** candidate) {
*candidate = nullptr;

// Ignore filename with path.
Expand Down Expand Up @@ -1504,7 +1511,8 @@ bool find_libraries(android_namespace_t* ns,

bool linked = local_group.visit([&](soinfo* si) {
if (!si->is_linked()) {
if (!si->link_image(global_group, local_group, extinfo)) {
if (!si->link_image(global_group, local_group, extinfo) ||
!get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
return false;
}
}
Expand Down Expand Up @@ -1656,6 +1664,7 @@ static void soinfo_unload(soinfo* soinfos[], size_t count) {

while ((si = local_unload_list.pop_front()) != nullptr) {
notify_gdb_of_unload(si);
get_cfi_shadow()->BeforeUnload(si);
soinfo_free(si);
}

Expand Down

0 comments on commit 082bec5

Please sign in to comment.