Skip to content

Commit

Permalink
[HWASan] Introduce non-zero based and dynamic shadow memory (compiler…
Browse files Browse the repository at this point in the history
…-rt).

Summary:
Retire the fixed shadow memory mapping to avoid conflicts with default
process memory mapping (currently manifests on Android).

Tests on AArch64 show <1% performance loss and code size increase,
making it possible to use dynamic shadow memory by default.

For the simplicity and unifirmity sake, use dynamic shadow memory mapping
with base address accessed via ifunc resolver on all supported platforms.

Keep the fixed shadow memory mapping around to be able to run
performance comparison tests later.

Complementing D45840.

Reviewers: eugenis

Subscribers: srhines, kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits

Differential Revision: https://reviews.llvm.org/D45847

llvm-svn: 330474
  • Loading branch information
alekseyshl committed Apr 20, 2018
1 parent 18f1733 commit c3ec164
Show file tree
Hide file tree
Showing 13 changed files with 409 additions and 70 deletions.
3 changes: 2 additions & 1 deletion compiler-rt/lib/hwasan/CMakeLists.txt
Expand Up @@ -4,11 +4,12 @@ include_directories(..)
set(HWASAN_RTL_SOURCES
hwasan.cc
hwasan_allocator.cc
hwasan_dynamic_shadow.cc
hwasan_interceptors.cc
hwasan_linux.cc
hwasan_poisoning.cc
hwasan_report.cc
hwasan_thread.cc
hwasan_poisoning.cc
)

set(HWASAN_RTL_CXX_SOURCES
Expand Down
15 changes: 10 additions & 5 deletions compiler-rt/lib/hwasan/hwasan.cc
Expand Up @@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//

#include "hwasan.h"
#include "hwasan_mapping.h"
#include "hwasan_thread.h"
#include "hwasan_poisoning.h"
#include "sanitizer_common/sanitizer_atomic.h"
Expand Down Expand Up @@ -157,6 +158,8 @@ static void HWAsanCheckFailed(const char *file, int line, const char *cond,

using namespace __hwasan;

uptr __hwasan_shadow_memory_dynamic_address; // Global interface symbol.

void __hwasan_init() {
CHECK(!hwasan_init_is_running);
if (hwasan_inited) return;
Expand All @@ -179,11 +182,13 @@ void __hwasan_init() {

DisableCoreDumperIfNecessary();
if (!InitShadow()) {
Printf("FATAL: HWAddressSanitizer can not mmap the shadow memory.\n");
Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
Printf("FATAL: Disabling ASLR is known to cause this error.\n");
Printf("FATAL: If running under GDB, try "
"'set disable-randomization off'.\n");
Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
if (HWASAN_FIXED_MAPPING) {
Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
Printf("FATAL: Disabling ASLR is known to cause this error.\n");
Printf("FATAL: If running under GDB, try "
"'set disable-randomization off'.\n");
}
DumpProcessMap();
Die();
}
Expand Down
12 changes: 0 additions & 12 deletions compiler-rt/lib/hwasan/hwasan.h
Expand Up @@ -32,16 +32,6 @@

typedef u8 tag_t;

// Reasonable values are 4 (for 1/16th shadow) and 6 (for 1/64th).
const uptr kShadowScale = 4;
const uptr kShadowAlignment = 1UL << kShadowScale;

#define MEM_TO_SHADOW_OFFSET(mem) ((uptr)(mem) >> kShadowScale)
#define MEM_TO_SHADOW(mem) ((uptr)(mem) >> kShadowScale)
#define SHADOW_TO_MEM(shadow) ((uptr)(shadow) << kShadowScale)

#define MEM_IS_APP(mem) MemIsApp((uptr)(mem))

// TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address
// translation and can be used to store a tag.
const unsigned kAddressTagShift = 56;
Expand Down Expand Up @@ -69,8 +59,6 @@ extern int hwasan_inited;
extern bool hwasan_init_is_running;
extern int hwasan_report_count;

bool MemIsApp(uptr p);

bool ProtectRange(uptr beg, uptr end);
bool InitShadow();
char *GetProcSelfMaps();
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/hwasan/hwasan_allocator.cc
Expand Up @@ -20,6 +20,7 @@
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "hwasan.h"
#include "hwasan_allocator.h"
#include "hwasan_mapping.h"
#include "hwasan_thread.h"
#include "hwasan_poisoning.h"

Expand Down
132 changes: 132 additions & 0 deletions compiler-rt/lib/hwasan/hwasan_dynamic_shadow.cc
@@ -0,0 +1,132 @@
//===-- hwasan_dynamic_shadow.cc --------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \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 "hwasan_mapping.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.

namespace __hwasan {

static void UnmapFromTo(uptr from, uptr to) {
if (to == from)
return;
CHECK(to >= from);
uptr res = internal_munmap(reinterpret_cast<void *>(from), to - from);
if (UNLIKELY(internal_iserror(res))) {
Report("ERROR: %s failed to unmap 0x%zx (%zd) bytes at address %p\n",
SanitizerToolName, to - from, to - from, from);
CHECK("unable to unmap" && 0);
}
}

// Returns an address aligned to 8 pages, such that one page on the left and
// shadow_size_bytes bytes on the right of it are mapped r/o.
static uptr MapDynamicShadow(uptr shadow_size_bytes) {
const uptr granularity = GetMmapGranularity();
const uptr alignment = granularity * SHADOW_GRANULARITY;
const uptr left_padding = granularity;
const uptr shadow_size =
RoundUpTo(shadow_size_bytes, granularity);
const uptr map_size = shadow_size + left_padding + alignment;

const uptr map_start = (uptr)MmapNoAccess(map_size);
CHECK_NE(map_start, ~(uptr)0);

const uptr shadow_start = RoundUpTo(map_start + left_padding, alignment);

UnmapFromTo(map_start, shadow_start - left_padding);
UnmapFromTo(shadow_start + shadow_size, map_start + map_size);

return shadow_start;
}

} // namespace __hwasan

#if HWASAN_PREMAP_SHADOW

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());
}

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 "C"

#endif // HWASAN_PREMAP_SHADOW

namespace __hwasan {

uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
#if HWASAN_PREMAP_SHADOW
if (IsPremapShadowAvailable())
return FindPremappedShadowStart(shadow_size_bytes);
#endif
return MapDynamicShadow(shadow_size_bytes);
}

} // namespace __hwasan
27 changes: 27 additions & 0 deletions compiler-rt/lib/hwasan/hwasan_dynamic_shadow.h
@@ -0,0 +1,27 @@
//===-- hwasan_dynamic_shadow.h ---------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file is a part of HWAddressSanitizer. It reserves dynamic shadow memory
/// region.
///
//===----------------------------------------------------------------------===//

#ifndef HWASAN_PREMAP_SHADOW_H
#define HWASAN_PREMAP_SHADOW_H

#include "sanitizer_common/sanitizer_internal_defs.h"

namespace __hwasan {

uptr FindDynamicShadowStart(uptr shadow_size_bytes);

} // namespace __hwasan

#endif // HWASAN_PREMAP_SHADOW_H
1 change: 1 addition & 0 deletions compiler-rt/lib/hwasan/hwasan_interceptors.cc
Expand Up @@ -17,6 +17,7 @@

#include "interception/interception.h"
#include "hwasan.h"
#include "hwasan_mapping.h"
#include "hwasan_thread.h"
#include "hwasan_poisoning.h"
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
Expand Down
4 changes: 4 additions & 0 deletions compiler-rt/lib/hwasan/hwasan_interface_internal.h
Expand Up @@ -18,6 +18,7 @@
#include "sanitizer_common/sanitizer_internal_defs.h"

extern "C" {

SANITIZER_INTERFACE_ATTRIBUTE
void __hwasan_init();

Expand All @@ -31,6 +32,9 @@ using __sanitizer::u32;
using __sanitizer::u16;
using __sanitizer::u8;

SANITIZER_INTERFACE_ATTRIBUTE
extern uptr __hwasan_shadow_memory_dynamic_address;

SANITIZER_INTERFACE_ATTRIBUTE
void __hwasan_loadN(uptr, uptr);
SANITIZER_INTERFACE_ATTRIBUTE
Expand Down

0 comments on commit c3ec164

Please sign in to comment.