2019-02-27 23:44:03 +08:00
|
|
|
//===-- hwasan_linux.cpp ----------------------------------------*- C++ -*-===//
|
2017-12-09 09:31:51 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2017-12-09 09:31:51 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
2018-04-24 02:19:23 +08:00
|
|
|
///
|
|
|
|
/// \file
|
|
|
|
/// This file is a part of HWAddressSanitizer and contains Linux-, NetBSD- and
|
|
|
|
/// FreeBSD-specific code.
|
|
|
|
///
|
2017-12-09 09:31:51 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "sanitizer_common/sanitizer_platform.h"
|
|
|
|
#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
|
|
|
|
|
|
|
|
#include "hwasan.h"
|
2018-04-24 02:19:23 +08:00
|
|
|
#include "hwasan_dynamic_shadow.h"
|
|
|
|
#include "hwasan_interface_internal.h"
|
|
|
|
#include "hwasan_mapping.h"
|
2018-06-08 07:33:33 +08:00
|
|
|
#include "hwasan_report.h"
|
2017-12-09 09:31:51 +08:00
|
|
|
#include "hwasan_thread.h"
|
2018-09-25 07:03:34 +08:00
|
|
|
#include "hwasan_thread_list.h"
|
2017-12-09 09:31:51 +08:00
|
|
|
|
2018-12-13 06:10:52 +08:00
|
|
|
#include <dlfcn.h>
|
2017-12-09 09:31:51 +08:00
|
|
|
#include <elf.h>
|
|
|
|
#include <link.h>
|
|
|
|
#include <pthread.h>
|
2018-04-24 02:19:23 +08:00
|
|
|
#include <signal.h>
|
2017-12-09 09:31:51 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2018-04-24 02:19:23 +08:00
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <sys/time.h>
|
2017-12-09 09:31:51 +08:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <unwind.h>
|
|
|
|
|
|
|
|
#include "sanitizer_common/sanitizer_common.h"
|
|
|
|
#include "sanitizer_common/sanitizer_procmaps.h"
|
|
|
|
|
2019-05-04 02:20:16 +08:00
|
|
|
// Configurations of HWASAN_WITH_INTERCEPTORS and SANITIZER_ANDROID.
|
|
|
|
//
|
|
|
|
// HWASAN_WITH_INTERCEPTORS=OFF, SANITIZER_ANDROID=OFF
|
|
|
|
// Not currently tested.
|
|
|
|
// HWASAN_WITH_INTERCEPTORS=OFF, SANITIZER_ANDROID=ON
|
|
|
|
// Integration tests downstream exist.
|
|
|
|
// HWASAN_WITH_INTERCEPTORS=ON, SANITIZER_ANDROID=OFF
|
|
|
|
// Tested with check-hwasan on x86_64-linux.
|
|
|
|
// HWASAN_WITH_INTERCEPTORS=ON, SANITIZER_ANDROID=ON
|
|
|
|
// Tested with check-hwasan on aarch64-linux-android.
|
|
|
|
#if !SANITIZER_ANDROID
|
hwasan: Use system allocator to realloc and free untagged pointers in interceptor mode.
The Android dynamic loader has a non-standard feature that allows
libraries such as the hwasan runtime to interpose symbols even after
the symbol already has a value. The new value of the symbol is used to
relocate libraries loaded after the interposing library, but existing
libraries keep the old value. This behaviour is activated by the
DF_1_GLOBAL flag in DT_FLAGS_1, which is set by passing -z global to
the linker, which is what we already do to link the hwasan runtime.
What this means in practice is that if we have .so files that depend
on interceptor-mode hwasan without the main executable depending on
it, some of the libraries in the process will be using the hwasan
allocator and some will be using the system allocator, and these
allocators need to interact somehow. For example, if an instrumented
library calls a function such as strdup that allocates memory on
behalf of the caller, the instrumented library can reasonably expect
to be able to call free to deallocate the memory.
We can handle that relatively easily with hwasan by using tag 0 to
represent allocations from the system allocator. If hwasan's realloc
or free functions are passed a pointer with tag 0, the system allocator
is called.
One limitation is that this scheme doesn't work in reverse: if an
instrumented library allocates memory, it must free the memory itself
and cannot pass ownership to a system library. In a future change,
we may want to expose an API for calling the system allocator so
that instrumented libraries can safely transfer ownership of memory
to system libraries.
Differential Revision: https://reviews.llvm.org/D55986
llvm-svn: 350427
2019-01-05 03:21:51 +08:00
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE
|
2018-09-25 07:03:34 +08:00
|
|
|
THREADLOCAL uptr __hwasan_tls;
|
|
|
|
#endif
|
|
|
|
|
2017-12-13 09:16:34 +08:00
|
|
|
namespace __hwasan {
|
2017-12-09 09:31:51 +08:00
|
|
|
|
2018-04-24 02:19:23 +08:00
|
|
|
static void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
|
2017-12-13 09:16:34 +08:00
|
|
|
CHECK_EQ((beg % GetMmapGranularity()), 0);
|
|
|
|
CHECK_EQ(((end + 1) % GetMmapGranularity()), 0);
|
|
|
|
uptr size = end - beg + 1;
|
|
|
|
DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb.
|
2018-07-20 16:33:41 +08:00
|
|
|
if (!MmapFixedNoReserve(beg, size, name)) {
|
2017-12-13 09:16:34 +08:00
|
|
|
Report(
|
|
|
|
"ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
|
|
|
|
"Perhaps you're using ulimit -v\n",
|
|
|
|
size);
|
|
|
|
Abort();
|
|
|
|
}
|
|
|
|
}
|
2017-12-09 09:31:51 +08:00
|
|
|
|
2017-12-13 09:16:34 +08:00
|
|
|
static void ProtectGap(uptr addr, uptr size) {
|
2018-04-24 02:19:23 +08:00
|
|
|
if (!size)
|
|
|
|
return;
|
2017-12-13 09:16:34 +08:00
|
|
|
void *res = MmapFixedNoAccess(addr, size, "shadow gap");
|
2018-04-24 02:19:23 +08:00
|
|
|
if (addr == (uptr)res)
|
|
|
|
return;
|
2017-12-13 09:16:34 +08:00
|
|
|
// A few pages at the start of the address space can not be protected.
|
|
|
|
// But we really want to protect as much as possible, to prevent this memory
|
|
|
|
// being returned as a result of a non-FIXED mmap().
|
|
|
|
if (addr == 0) {
|
|
|
|
uptr step = GetMmapGranularity();
|
|
|
|
while (size > step) {
|
|
|
|
addr += step;
|
|
|
|
size -= step;
|
|
|
|
void *res = MmapFixedNoAccess(addr, size, "shadow gap");
|
2018-04-24 02:19:23 +08:00
|
|
|
if (addr == (uptr)res)
|
|
|
|
return;
|
2017-12-13 09:16:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Report(
|
2018-04-24 02:19:23 +08:00
|
|
|
"ERROR: Failed to protect shadow gap [%p, %p]. "
|
|
|
|
"HWASan cannot proceed correctly. ABORTING.\n", (void *)addr,
|
|
|
|
(void *)(addr + size));
|
2017-12-13 09:16:34 +08:00
|
|
|
DumpProcessMap();
|
|
|
|
Die();
|
|
|
|
}
|
2017-12-09 09:31:51 +08:00
|
|
|
|
2018-04-24 02:19:23 +08:00
|
|
|
static uptr kLowMemStart;
|
|
|
|
static uptr kLowMemEnd;
|
|
|
|
static uptr kLowShadowEnd;
|
|
|
|
static uptr kLowShadowStart;
|
2018-01-12 06:53:30 +08:00
|
|
|
static uptr kHighShadowStart;
|
|
|
|
static uptr kHighShadowEnd;
|
|
|
|
static uptr kHighMemStart;
|
2018-04-24 02:19:23 +08:00
|
|
|
static uptr kHighMemEnd;
|
2018-04-21 04:03:57 +08:00
|
|
|
|
2018-04-24 02:19:23 +08:00
|
|
|
static void PrintRange(uptr start, uptr end, const char *name) {
|
|
|
|
Printf("|| [%p, %p] || %.*s ||\n", (void *)start, (void *)end, 10, name);
|
|
|
|
}
|
2018-04-21 04:03:57 +08:00
|
|
|
|
2018-04-24 02:19:23 +08:00
|
|
|
static void PrintAddressSpaceLayout() {
|
|
|
|
PrintRange(kHighMemStart, kHighMemEnd, "HighMem");
|
|
|
|
if (kHighShadowEnd + 1 < kHighMemStart)
|
|
|
|
PrintRange(kHighShadowEnd + 1, kHighMemStart - 1, "ShadowGap");
|
|
|
|
else
|
|
|
|
CHECK_EQ(kHighShadowEnd + 1, kHighMemStart);
|
|
|
|
PrintRange(kHighShadowStart, kHighShadowEnd, "HighShadow");
|
2018-08-30 06:47:53 +08:00
|
|
|
if (kLowShadowEnd + 1 < kHighShadowStart)
|
|
|
|
PrintRange(kLowShadowEnd + 1, kHighShadowStart - 1, "ShadowGap");
|
|
|
|
else
|
|
|
|
CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
|
|
|
|
PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
|
|
|
|
if (kLowMemEnd + 1 < kLowShadowStart)
|
|
|
|
PrintRange(kLowMemEnd + 1, kLowShadowStart - 1, "ShadowGap");
|
|
|
|
else
|
|
|
|
CHECK_EQ(kLowMemEnd + 1, kLowShadowStart);
|
|
|
|
PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
|
|
|
|
CHECK_EQ(0, kLowMemStart);
|
2018-04-24 02:19:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static uptr GetHighMemEnd() {
|
2018-04-24 01:26:33 +08:00
|
|
|
// HighMem covers the upper part of the address space.
|
2018-04-24 02:19:23 +08:00
|
|
|
uptr max_address = GetMaxUserVirtualAddress();
|
2018-08-30 06:47:53 +08:00
|
|
|
// Adjust max address to make sure that kHighMemEnd and kHighMemStart are
|
|
|
|
// properly aligned:
|
|
|
|
max_address |= (GetMmapGranularity() << kShadowScale) - 1;
|
2018-04-24 02:19:23 +08:00
|
|
|
return max_address;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
|
2018-08-30 06:54:52 +08:00
|
|
|
__hwasan_shadow_memory_dynamic_address =
|
|
|
|
FindDynamicShadowStart(shadow_size_bytes);
|
2018-04-24 02:19:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool InitShadow() {
|
|
|
|
// Define the entire memory range.
|
|
|
|
kHighMemEnd = GetHighMemEnd();
|
|
|
|
|
|
|
|
// Determine shadow memory base offset.
|
2018-08-30 06:42:16 +08:00
|
|
|
InitializeShadowBaseAddress(MemToShadowSize(kHighMemEnd));
|
2018-04-24 02:19:23 +08:00
|
|
|
|
|
|
|
// Place the low memory first.
|
2018-08-30 06:47:53 +08:00
|
|
|
kLowMemEnd = __hwasan_shadow_memory_dynamic_address - 1;
|
|
|
|
kLowMemStart = 0;
|
2017-12-13 09:16:34 +08:00
|
|
|
|
2018-04-24 02:19:23 +08:00
|
|
|
// Define the low shadow based on the already placed low memory.
|
2018-08-30 06:42:16 +08:00
|
|
|
kLowShadowEnd = MemToShadow(kLowMemEnd);
|
2018-08-30 06:47:53 +08:00
|
|
|
kLowShadowStart = __hwasan_shadow_memory_dynamic_address;
|
2018-04-24 02:19:23 +08:00
|
|
|
|
|
|
|
// High shadow takes whatever memory is left up there (making sure it is not
|
|
|
|
// interfering with low memory in the fixed case).
|
2018-08-30 06:42:16 +08:00
|
|
|
kHighShadowEnd = MemToShadow(kHighMemEnd);
|
|
|
|
kHighShadowStart = Max(kLowMemEnd, MemToShadow(kHighShadowEnd)) + 1;
|
2018-04-24 02:19:23 +08:00
|
|
|
|
|
|
|
// High memory starts where allocated shadow allows.
|
2018-08-30 06:42:16 +08:00
|
|
|
kHighMemStart = ShadowToMem(kHighShadowStart);
|
2018-04-24 02:19:23 +08:00
|
|
|
|
|
|
|
// Check the sanity of the defined memory ranges (there might be gaps).
|
|
|
|
CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0);
|
|
|
|
CHECK_GT(kHighMemStart, kHighShadowEnd);
|
|
|
|
CHECK_GT(kHighShadowEnd, kHighShadowStart);
|
|
|
|
CHECK_GT(kHighShadowStart, kLowMemEnd);
|
|
|
|
CHECK_GT(kLowMemEnd, kLowMemStart);
|
|
|
|
CHECK_GT(kLowShadowEnd, kLowShadowStart);
|
2018-08-30 06:47:53 +08:00
|
|
|
CHECK_GT(kLowShadowStart, kLowMemEnd);
|
2018-04-24 02:19:23 +08:00
|
|
|
|
|
|
|
if (Verbosity())
|
|
|
|
PrintAddressSpaceLayout();
|
|
|
|
|
|
|
|
// Reserve shadow memory.
|
|
|
|
ReserveShadowMemoryRange(kLowShadowStart, kLowShadowEnd, "low shadow");
|
|
|
|
ReserveShadowMemoryRange(kHighShadowStart, kHighShadowEnd, "high shadow");
|
|
|
|
|
|
|
|
// Protect all the gaps.
|
|
|
|
ProtectGap(0, Min(kLowMemStart, kLowShadowStart));
|
2018-08-30 06:47:53 +08:00
|
|
|
if (kLowMemEnd + 1 < kLowShadowStart)
|
|
|
|
ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1);
|
|
|
|
if (kLowShadowEnd + 1 < kHighShadowStart)
|
|
|
|
ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1);
|
2018-04-24 02:19:23 +08:00
|
|
|
if (kHighShadowEnd + 1 < kHighMemStart)
|
|
|
|
ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1);
|
2017-12-13 09:16:34 +08:00
|
|
|
|
2017-12-09 09:31:51 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-09-25 07:03:34 +08:00
|
|
|
void InitThreads() {
|
|
|
|
CHECK(__hwasan_shadow_memory_dynamic_address);
|
|
|
|
uptr guard_page_size = GetMmapGranularity();
|
|
|
|
uptr thread_space_start =
|
|
|
|
__hwasan_shadow_memory_dynamic_address - (1ULL << kShadowBaseAlignment);
|
|
|
|
uptr thread_space_end =
|
|
|
|
__hwasan_shadow_memory_dynamic_address - guard_page_size;
|
|
|
|
ReserveShadowMemoryRange(thread_space_start, thread_space_end - 1,
|
|
|
|
"hwasan threads");
|
|
|
|
ProtectGap(thread_space_end,
|
|
|
|
__hwasan_shadow_memory_dynamic_address - thread_space_end);
|
|
|
|
InitThreadList(thread_space_start, thread_space_end - thread_space_start);
|
|
|
|
}
|
|
|
|
|
2018-08-14 03:57:11 +08:00
|
|
|
static void MadviseShadowRegion(uptr beg, uptr end) {
|
|
|
|
uptr size = end - beg + 1;
|
2019-08-06 04:54:36 +08:00
|
|
|
SetShadowRegionHugePageMode(beg, size);
|
2018-08-14 03:57:11 +08:00
|
|
|
if (common_flags()->use_madv_dontdump)
|
|
|
|
DontDumpShadowMemory(beg, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MadviseShadow() {
|
|
|
|
MadviseShadowRegion(kLowShadowStart, kLowShadowEnd);
|
|
|
|
MadviseShadowRegion(kHighShadowStart, kHighShadowEnd);
|
|
|
|
}
|
|
|
|
|
2018-01-12 06:53:30 +08:00
|
|
|
bool MemIsApp(uptr p) {
|
|
|
|
CHECK(GetTagFromPointer(p) == 0);
|
2018-04-24 02:19:23 +08:00
|
|
|
return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd);
|
2018-01-12 06:53:30 +08:00
|
|
|
}
|
|
|
|
|
2017-12-09 09:31:51 +08:00
|
|
|
static void HwasanAtExit(void) {
|
2019-01-25 10:05:25 +08:00
|
|
|
if (common_flags()->print_module_map)
|
|
|
|
DumpProcessMap();
|
2017-12-09 09:31:51 +08:00
|
|
|
if (flags()->print_stats && (flags()->atexit || hwasan_report_count > 0))
|
|
|
|
ReportStats();
|
|
|
|
if (hwasan_report_count > 0) {
|
|
|
|
// ReportAtExitStatistics();
|
|
|
|
if (common_flags()->exitcode)
|
|
|
|
internal__exit(common_flags()->exitcode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void InstallAtExitHandler() {
|
|
|
|
atexit(HwasanAtExit);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------- TSD ---------------- {{{1
|
|
|
|
|
[hwasan] Add a (almost) no-interceptor mode.
Summary:
The idea behind this change is to allow sanitization of libc. We are prototyping on Bionic,
but the tool interface will be general enough (or at least generalizable) to support any other libc.
When libc depends on libclang_rt.hwasan, the latter can not interpose libc functions.
In fact, majority of interceptors become unnecessary when libc code is instrumented.
This change gets rid of most hwasan interceptors and provides interface for libc to notify
hwasan about thread creation and destruction events. Some interceptors (pthread_create)
are kept under #ifdef to enable testing with uninstrumented libc. They are expressed in
terms of the new libc interface.
The new cmake switch, COMPILER_RT_HWASAN_WITH_INTERCEPTORS, ON by default, builds testing
version of the library with the aforementioned pthread_create interceptor.
With the OFF setting, the library becomes more of a libc plugin.
Reviewers: vitalybuka, kcc, jfb
Subscribers: srhines, kubamracek, mgorny, jfb, llvm-commits
Differential Revision: https://reviews.llvm.org/D50922
llvm-svn: 340216
2018-08-21 05:49:15 +08:00
|
|
|
extern "C" void __hwasan_thread_enter() {
|
2019-02-01 07:37:12 +08:00
|
|
|
hwasanThreadList().CreateCurrentThread()->InitRandomState();
|
[hwasan] Add a (almost) no-interceptor mode.
Summary:
The idea behind this change is to allow sanitization of libc. We are prototyping on Bionic,
but the tool interface will be general enough (or at least generalizable) to support any other libc.
When libc depends on libclang_rt.hwasan, the latter can not interpose libc functions.
In fact, majority of interceptors become unnecessary when libc code is instrumented.
This change gets rid of most hwasan interceptors and provides interface for libc to notify
hwasan about thread creation and destruction events. Some interceptors (pthread_create)
are kept under #ifdef to enable testing with uninstrumented libc. They are expressed in
terms of the new libc interface.
The new cmake switch, COMPILER_RT_HWASAN_WITH_INTERCEPTORS, ON by default, builds testing
version of the library with the aforementioned pthread_create interceptor.
With the OFF setting, the library becomes more of a libc plugin.
Reviewers: vitalybuka, kcc, jfb
Subscribers: srhines, kubamracek, mgorny, jfb, llvm-commits
Differential Revision: https://reviews.llvm.org/D50922
llvm-svn: 340216
2018-08-21 05:49:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" void __hwasan_thread_exit() {
|
2018-08-30 08:13:20 +08:00
|
|
|
Thread *t = GetCurrentThread();
|
[hwasan] Add a (almost) no-interceptor mode.
Summary:
The idea behind this change is to allow sanitization of libc. We are prototyping on Bionic,
but the tool interface will be general enough (or at least generalizable) to support any other libc.
When libc depends on libclang_rt.hwasan, the latter can not interpose libc functions.
In fact, majority of interceptors become unnecessary when libc code is instrumented.
This change gets rid of most hwasan interceptors and provides interface for libc to notify
hwasan about thread creation and destruction events. Some interceptors (pthread_create)
are kept under #ifdef to enable testing with uninstrumented libc. They are expressed in
terms of the new libc interface.
The new cmake switch, COMPILER_RT_HWASAN_WITH_INTERCEPTORS, ON by default, builds testing
version of the library with the aforementioned pthread_create interceptor.
With the OFF setting, the library becomes more of a libc plugin.
Reviewers: vitalybuka, kcc, jfb
Subscribers: srhines, kubamracek, mgorny, jfb, llvm-commits
Differential Revision: https://reviews.llvm.org/D50922
llvm-svn: 340216
2018-08-21 05:49:15 +08:00
|
|
|
// Make sure that signal handler can not see a stale current thread pointer.
|
|
|
|
atomic_signal_fence(memory_order_seq_cst);
|
2018-09-05 09:29:08 +08:00
|
|
|
if (t)
|
2018-09-25 07:03:34 +08:00
|
|
|
hwasanThreadList().ReleaseThread(t);
|
[hwasan] Add a (almost) no-interceptor mode.
Summary:
The idea behind this change is to allow sanitization of libc. We are prototyping on Bionic,
but the tool interface will be general enough (or at least generalizable) to support any other libc.
When libc depends on libclang_rt.hwasan, the latter can not interpose libc functions.
In fact, majority of interceptors become unnecessary when libc code is instrumented.
This change gets rid of most hwasan interceptors and provides interface for libc to notify
hwasan about thread creation and destruction events. Some interceptors (pthread_create)
are kept under #ifdef to enable testing with uninstrumented libc. They are expressed in
terms of the new libc interface.
The new cmake switch, COMPILER_RT_HWASAN_WITH_INTERCEPTORS, ON by default, builds testing
version of the library with the aforementioned pthread_create interceptor.
With the OFF setting, the library becomes more of a libc plugin.
Reviewers: vitalybuka, kcc, jfb
Subscribers: srhines, kubamracek, mgorny, jfb, llvm-commits
Differential Revision: https://reviews.llvm.org/D50922
llvm-svn: 340216
2018-08-21 05:49:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#if HWASAN_WITH_INTERCEPTORS
|
2017-12-09 09:31:51 +08:00
|
|
|
static pthread_key_t tsd_key;
|
|
|
|
static bool tsd_key_inited = false;
|
|
|
|
|
2018-09-25 07:03:34 +08:00
|
|
|
void HwasanTSDThreadInit() {
|
|
|
|
if (tsd_key_inited)
|
|
|
|
CHECK_EQ(0, pthread_setspecific(tsd_key,
|
|
|
|
(void *)GetPthreadDestructorIterations()));
|
|
|
|
}
|
|
|
|
|
[hwasan] Add a (almost) no-interceptor mode.
Summary:
The idea behind this change is to allow sanitization of libc. We are prototyping on Bionic,
but the tool interface will be general enough (or at least generalizable) to support any other libc.
When libc depends on libclang_rt.hwasan, the latter can not interpose libc functions.
In fact, majority of interceptors become unnecessary when libc code is instrumented.
This change gets rid of most hwasan interceptors and provides interface for libc to notify
hwasan about thread creation and destruction events. Some interceptors (pthread_create)
are kept under #ifdef to enable testing with uninstrumented libc. They are expressed in
terms of the new libc interface.
The new cmake switch, COMPILER_RT_HWASAN_WITH_INTERCEPTORS, ON by default, builds testing
version of the library with the aforementioned pthread_create interceptor.
With the OFF setting, the library becomes more of a libc plugin.
Reviewers: vitalybuka, kcc, jfb
Subscribers: srhines, kubamracek, mgorny, jfb, llvm-commits
Differential Revision: https://reviews.llvm.org/D50922
llvm-svn: 340216
2018-08-21 05:49:15 +08:00
|
|
|
void HwasanTSDDtor(void *tsd) {
|
2018-09-25 07:03:34 +08:00
|
|
|
uptr iterations = (uptr)tsd;
|
|
|
|
if (iterations > 1) {
|
|
|
|
CHECK_EQ(0, pthread_setspecific(tsd_key, (void *)(iterations - 1)));
|
[hwasan] Add a (almost) no-interceptor mode.
Summary:
The idea behind this change is to allow sanitization of libc. We are prototyping on Bionic,
but the tool interface will be general enough (or at least generalizable) to support any other libc.
When libc depends on libclang_rt.hwasan, the latter can not interpose libc functions.
In fact, majority of interceptors become unnecessary when libc code is instrumented.
This change gets rid of most hwasan interceptors and provides interface for libc to notify
hwasan about thread creation and destruction events. Some interceptors (pthread_create)
are kept under #ifdef to enable testing with uninstrumented libc. They are expressed in
terms of the new libc interface.
The new cmake switch, COMPILER_RT_HWASAN_WITH_INTERCEPTORS, ON by default, builds testing
version of the library with the aforementioned pthread_create interceptor.
With the OFF setting, the library becomes more of a libc plugin.
Reviewers: vitalybuka, kcc, jfb
Subscribers: srhines, kubamracek, mgorny, jfb, llvm-commits
Differential Revision: https://reviews.llvm.org/D50922
llvm-svn: 340216
2018-08-21 05:49:15 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
__hwasan_thread_exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HwasanTSDInit() {
|
2017-12-09 09:31:51 +08:00
|
|
|
CHECK(!tsd_key_inited);
|
|
|
|
tsd_key_inited = true;
|
[hwasan] Add a (almost) no-interceptor mode.
Summary:
The idea behind this change is to allow sanitization of libc. We are prototyping on Bionic,
but the tool interface will be general enough (or at least generalizable) to support any other libc.
When libc depends on libclang_rt.hwasan, the latter can not interpose libc functions.
In fact, majority of interceptors become unnecessary when libc code is instrumented.
This change gets rid of most hwasan interceptors and provides interface for libc to notify
hwasan about thread creation and destruction events. Some interceptors (pthread_create)
are kept under #ifdef to enable testing with uninstrumented libc. They are expressed in
terms of the new libc interface.
The new cmake switch, COMPILER_RT_HWASAN_WITH_INTERCEPTORS, ON by default, builds testing
version of the library with the aforementioned pthread_create interceptor.
With the OFF setting, the library becomes more of a libc plugin.
Reviewers: vitalybuka, kcc, jfb
Subscribers: srhines, kubamracek, mgorny, jfb, llvm-commits
Differential Revision: https://reviews.llvm.org/D50922
llvm-svn: 340216
2018-08-21 05:49:15 +08:00
|
|
|
CHECK_EQ(0, pthread_key_create(&tsd_key, HwasanTSDDtor));
|
2017-12-09 09:31:51 +08:00
|
|
|
}
|
2018-09-25 07:03:34 +08:00
|
|
|
#else
|
2018-09-25 06:50:32 +08:00
|
|
|
void HwasanTSDInit() {}
|
2018-09-25 07:03:34 +08:00
|
|
|
void HwasanTSDThreadInit() {}
|
|
|
|
#endif
|
2018-09-25 05:38:42 +08:00
|
|
|
|
2018-09-25 07:03:34 +08:00
|
|
|
#if SANITIZER_ANDROID
|
|
|
|
uptr *GetCurrentThreadLongPtr() {
|
|
|
|
return (uptr *)get_android_tls_ptr();
|
2018-09-25 06:50:32 +08:00
|
|
|
}
|
|
|
|
#else
|
2018-09-25 07:03:34 +08:00
|
|
|
uptr *GetCurrentThreadLongPtr() {
|
|
|
|
return &__hwasan_tls;
|
|
|
|
}
|
2018-09-25 06:50:32 +08:00
|
|
|
#endif
|
|
|
|
|
2018-12-13 06:10:52 +08:00
|
|
|
#if SANITIZER_ANDROID
|
|
|
|
void AndroidTestTlsSlot() {
|
|
|
|
uptr kMagicValue = 0x010203040A0B0C0D;
|
2019-02-01 07:37:12 +08:00
|
|
|
uptr *tls_ptr = GetCurrentThreadLongPtr();
|
|
|
|
uptr old_value = *tls_ptr;
|
|
|
|
*tls_ptr = kMagicValue;
|
2018-12-13 06:10:52 +08:00
|
|
|
dlerror();
|
|
|
|
if (*(uptr *)get_android_tls_ptr() != kMagicValue) {
|
|
|
|
Printf(
|
|
|
|
"ERROR: Incompatible version of Android: TLS_SLOT_SANITIZER(6) is used "
|
|
|
|
"for dlerror().\n");
|
|
|
|
Die();
|
|
|
|
}
|
2019-02-01 07:37:12 +08:00
|
|
|
*tls_ptr = old_value;
|
2018-12-13 06:10:52 +08:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
void AndroidTestTlsSlot() {}
|
|
|
|
#endif
|
|
|
|
|
2018-09-25 07:03:34 +08:00
|
|
|
Thread *GetCurrentThread() {
|
2019-01-05 03:27:04 +08:00
|
|
|
uptr *ThreadLong = GetCurrentThreadLongPtr();
|
|
|
|
#if HWASAN_WITH_INTERCEPTORS
|
|
|
|
if (!*ThreadLong)
|
|
|
|
__hwasan_thread_enter();
|
|
|
|
#endif
|
|
|
|
auto *R = (StackAllocationsRingBuffer *)ThreadLong;
|
2018-09-25 07:03:34 +08:00
|
|
|
return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
|
|
|
|
}
|
|
|
|
|
2017-12-09 09:31:51 +08:00
|
|
|
struct AccessInfo {
|
|
|
|
uptr addr;
|
|
|
|
uptr size;
|
|
|
|
bool is_store;
|
|
|
|
bool is_load;
|
2017-12-21 03:05:44 +08:00
|
|
|
bool recover;
|
2017-12-09 09:31:51 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static AccessInfo GetAccessInfo(siginfo_t *info, ucontext_t *uc) {
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
// Access type is passed in a platform dependent way (see below) and encoded
|
|
|
|
// as 0xXY, where X&1 is 1 for store, 0 for load, and X&2 is 1 if the error is
|
|
|
|
// recoverable. Valid values of Y are 0 to 4, which are interpreted as
|
|
|
|
// log2(access_size), and 0xF, which means that access size is passed via
|
|
|
|
// platform dependent register (see below).
|
|
|
|
#if defined(__aarch64__)
|
|
|
|
// Access type is encoded in BRK immediate as 0x900 + 0xXY. For Y == 0xF,
|
|
|
|
// access size is stored in X1 register. Access address is always in X0
|
|
|
|
// register.
|
2017-12-09 09:31:51 +08:00
|
|
|
uptr pc = (uptr)info->si_addr;
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
const unsigned code = ((*(u32 *)pc) >> 5) & 0xffff;
|
2018-02-22 03:52:23 +08:00
|
|
|
if ((code & 0xff00) != 0x900)
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
return AccessInfo{}; // Not ours.
|
|
|
|
|
|
|
|
const bool is_store = code & 0x10;
|
|
|
|
const bool recover = code & 0x20;
|
2018-03-24 08:40:51 +08:00
|
|
|
const uptr addr = uc->uc_mcontext.regs[0];
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
const unsigned size_log = code & 0xf;
|
2017-12-13 09:16:34 +08:00
|
|
|
if (size_log > 4 && size_log != 0xf)
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
return AccessInfo{}; // Not ours.
|
|
|
|
const uptr size = size_log == 0xf ? uc->uc_mcontext.regs[1] : 1U << size_log;
|
|
|
|
|
|
|
|
#elif defined(__x86_64__)
|
|
|
|
// Access type is encoded in the instruction following INT3 as
|
|
|
|
// NOP DWORD ptr [EAX + 0x40 + 0xXY]. For Y == 0xF, access size is stored in
|
|
|
|
// RSI register. Access address is always in RDI register.
|
|
|
|
uptr pc = (uptr)uc->uc_mcontext.gregs[REG_RIP];
|
|
|
|
uint8_t *nop = (uint8_t*)pc;
|
|
|
|
if (*nop != 0x0f || *(nop + 1) != 0x1f || *(nop + 2) != 0x40 ||
|
|
|
|
*(nop + 3) < 0x40)
|
|
|
|
return AccessInfo{}; // Not ours.
|
|
|
|
const unsigned code = *(nop + 3);
|
|
|
|
|
|
|
|
const bool is_store = code & 0x10;
|
|
|
|
const bool recover = code & 0x20;
|
|
|
|
const uptr addr = uc->uc_mcontext.gregs[REG_RDI];
|
|
|
|
const unsigned size_log = code & 0xf;
|
|
|
|
if (size_log > 4 && size_log != 0xf)
|
|
|
|
return AccessInfo{}; // Not ours.
|
|
|
|
const uptr size =
|
|
|
|
size_log == 0xf ? uc->uc_mcontext.gregs[REG_RSI] : 1U << size_log;
|
|
|
|
|
2017-12-09 09:31:51 +08:00
|
|
|
#else
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
# error Unsupported architecture
|
2017-12-09 09:31:51 +08:00
|
|
|
#endif
|
|
|
|
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
return AccessInfo{addr, size, is_store, !is_store, recover};
|
|
|
|
}
|
|
|
|
|
hwasan: Move memory access checks into small outlined functions on aarch64.
Each hwasan check requires emitting a small piece of code like this:
https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#memory-accesses
The problem with this is that these code blocks typically bloat code
size significantly.
An obvious solution is to outline these blocks of code. In fact, this
has already been implemented under the -hwasan-instrument-with-calls
flag. However, as currently implemented this has a number of problems:
- The functions use the same calling convention as regular C functions.
This means that the backend must spill all temporary registers as
required by the platform's C calling convention, even though the
check only needs two registers on the hot path.
- The functions take the address to be checked in a fixed register,
which increases register pressure.
Both of these factors can diminish the code size effect and increase
the performance hit of -hwasan-instrument-with-calls.
The solution that this patch implements is to involve the aarch64
backend in outlining the checks. An intrinsic and pseudo-instruction
are created to represent a hwasan check. The pseudo-instruction
is register allocated like any other instruction, and we allow the
register allocator to select almost any register for the address to
check. A particular combination of (register selection, type of check)
triggers the creation in the backend of a function to handle the check
for specifically that pair. The resulting functions are deduplicated by
the linker. The pseudo-instruction (really the function) is specified
to preserve all registers except for the registers that the AAPCS
specifies may be clobbered by a call.
To measure the code size and performance effect of this change, I
took a number of measurements using Chromium for Android on aarch64,
comparing a browser with inlined checks (the baseline) against a
browser with outlined checks.
Code size: Size of .text decreases from 243897420 to 171619972 bytes,
or a 30% decrease.
Performance: Using Chromium's blink_perf.layout microbenchmarks I
measured a median performance regression of 6.24%.
The fact that a perf/size tradeoff is evident here suggests that
we might want to make the new behaviour conditional on -Os/-Oz.
But for now I've enabled it unconditionally, my reasoning being that
hwasan users typically expect a relatively large perf hit, and ~6%
isn't really adding much. We may want to revisit this decision in
the future, though.
I also tried experimenting with varying the number of registers
selectable by the hwasan check pseudo-instruction (which would result
in fewer variants being created), on the hypothesis that creating
fewer variants of the function would expose another perf/size tradeoff
by reducing icache pressure from the check functions at the cost of
register pressure. Although I did observe a code size increase with
fewer registers, I did not observe a strong correlation between the
number of registers and the performance of the resulting browser on the
microbenchmarks, so I conclude that we might as well use ~all registers
to get the maximum code size improvement. My results are below:
Regs | .text size | Perf hit
-----+------------+---------
~all | 171619972 | 6.24%
16 | 171765192 | 7.03%
8 | 172917788 | 5.82%
4 | 177054016 | 6.89%
Differential Revision: https://reviews.llvm.org/D56954
llvm-svn: 351920
2019-01-23 10:20:10 +08:00
|
|
|
static void HandleTagMismatch(AccessInfo ai, uptr pc, uptr frame,
|
[HWASan] Save + print registers when tag mismatch occurs in AArch64.
Summary:
This change change the instrumentation to allow users to view the registers at the point at which tag mismatch occured. Most of the heavy lifting is done in the runtime library, where we save the registers to the stack and emit unwind information. This allows us to reduce the overhead, as very little additional work needs to be done in each __hwasan_check instance.
In this implementation, the fast path of __hwasan_check is unmodified. There are an additional 4 instructions (16B) emitted in the slow path in every __hwasan_check instance. This may increase binary size somewhat, but as most of the work is done in the runtime library, it's manageable.
The failure trace now contains a list of registers at the point of which the failure occured, in a format similar to that of Android's tombstones. It currently has the following format:
Registers where the failure occurred (pc 0x0055555561b4):
x0 0000000000000014 x1 0000007ffffff6c0 x2 1100007ffffff6d0 x3 12000056ffffe025
x4 0000007fff800000 x5 0000000000000014 x6 0000007fff800000 x7 0000000000000001
x8 12000056ffffe020 x9 0200007700000000 x10 0200007700000000 x11 0000000000000000
x12 0000007fffffdde0 x13 0000000000000000 x14 02b65b01f7a97490 x15 0000000000000000
x16 0000007fb77376b8 x17 0000000000000012 x18 0000007fb7ed6000 x19 0000005555556078
x20 0000007ffffff768 x21 0000007ffffff778 x22 0000000000000001 x23 0000000000000000
x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
x28 0000000000000000 x29 0000007ffffff6f0 x30 00000055555561b4
... and prints after the dump of memory tags around the buggy address.
Every register is saved exactly as it was at the point where the tag mismatch occurs, with the exception of x16/x17. These registers are used in the tag mismatch calculation as scratch registers during __hwasan_check, and cannot be saved without affecting the fast path. As these registers are designated as scratch registers for linking, there should be no important information in them that could aid in debugging.
Reviewers: pcc, eugenis
Reviewed By: pcc, eugenis
Subscribers: srhines, kubamracek, mgorny, javed.absar, krytarowski, kristof.beyls, hiraditya, jdoerfert, llvm-commits, #sanitizers
Tags: #sanitizers, #llvm
Differential Revision: https://reviews.llvm.org/D58857
llvm-svn: 355738
2019-03-09 05:22:35 +08:00
|
|
|
ucontext_t *uc, uptr *registers_frame = nullptr) {
|
2018-05-07 13:56:36 +08:00
|
|
|
InternalMmapVector<BufferedStackTrace> stack_buffer(1);
|
2017-12-09 09:31:51 +08:00
|
|
|
BufferedStackTrace *stack = stack_buffer.data();
|
|
|
|
stack->Reset();
|
2019-03-01 12:03:38 +08:00
|
|
|
stack->Unwind(pc, frame, uc, common_flags()->fast_unwind_on_fatal);
|
2017-12-09 09:31:51 +08:00
|
|
|
|
[HWASan] Save + print registers when tag mismatch occurs in AArch64.
Summary:
This change change the instrumentation to allow users to view the registers at the point at which tag mismatch occured. Most of the heavy lifting is done in the runtime library, where we save the registers to the stack and emit unwind information. This allows us to reduce the overhead, as very little additional work needs to be done in each __hwasan_check instance.
In this implementation, the fast path of __hwasan_check is unmodified. There are an additional 4 instructions (16B) emitted in the slow path in every __hwasan_check instance. This may increase binary size somewhat, but as most of the work is done in the runtime library, it's manageable.
The failure trace now contains a list of registers at the point of which the failure occured, in a format similar to that of Android's tombstones. It currently has the following format:
Registers where the failure occurred (pc 0x0055555561b4):
x0 0000000000000014 x1 0000007ffffff6c0 x2 1100007ffffff6d0 x3 12000056ffffe025
x4 0000007fff800000 x5 0000000000000014 x6 0000007fff800000 x7 0000000000000001
x8 12000056ffffe020 x9 0200007700000000 x10 0200007700000000 x11 0000000000000000
x12 0000007fffffdde0 x13 0000000000000000 x14 02b65b01f7a97490 x15 0000000000000000
x16 0000007fb77376b8 x17 0000000000000012 x18 0000007fb7ed6000 x19 0000005555556078
x20 0000007ffffff768 x21 0000007ffffff778 x22 0000000000000001 x23 0000000000000000
x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
x28 0000000000000000 x29 0000007ffffff6f0 x30 00000055555561b4
... and prints after the dump of memory tags around the buggy address.
Every register is saved exactly as it was at the point where the tag mismatch occurs, with the exception of x16/x17. These registers are used in the tag mismatch calculation as scratch registers during __hwasan_check, and cannot be saved without affecting the fast path. As these registers are designated as scratch registers for linking, there should be no important information in them that could aid in debugging.
Reviewers: pcc, eugenis
Reviewed By: pcc, eugenis
Subscribers: srhines, kubamracek, mgorny, javed.absar, krytarowski, kristof.beyls, hiraditya, jdoerfert, llvm-commits, #sanitizers
Tags: #sanitizers, #llvm
Differential Revision: https://reviews.llvm.org/D58857
llvm-svn: 355738
2019-03-09 05:22:35 +08:00
|
|
|
// The second stack frame contains the failure __hwasan_check function, as
|
|
|
|
// we have a stack frame for the registers saved in __hwasan_tag_mismatch that
|
|
|
|
// we wish to ignore. This (currently) only occurs on AArch64, as x64
|
|
|
|
// implementations use SIGTRAP to implement the failure, and thus do not go
|
|
|
|
// through the stack saver.
|
|
|
|
if (registers_frame && stack->trace && stack->size > 0) {
|
|
|
|
stack->trace++;
|
|
|
|
stack->size--;
|
|
|
|
}
|
|
|
|
|
2018-11-10 05:54:03 +08:00
|
|
|
bool fatal = flags()->halt_on_error || !ai.recover;
|
[HWASan] Save + print registers when tag mismatch occurs in AArch64.
Summary:
This change change the instrumentation to allow users to view the registers at the point at which tag mismatch occured. Most of the heavy lifting is done in the runtime library, where we save the registers to the stack and emit unwind information. This allows us to reduce the overhead, as very little additional work needs to be done in each __hwasan_check instance.
In this implementation, the fast path of __hwasan_check is unmodified. There are an additional 4 instructions (16B) emitted in the slow path in every __hwasan_check instance. This may increase binary size somewhat, but as most of the work is done in the runtime library, it's manageable.
The failure trace now contains a list of registers at the point of which the failure occured, in a format similar to that of Android's tombstones. It currently has the following format:
Registers where the failure occurred (pc 0x0055555561b4):
x0 0000000000000014 x1 0000007ffffff6c0 x2 1100007ffffff6d0 x3 12000056ffffe025
x4 0000007fff800000 x5 0000000000000014 x6 0000007fff800000 x7 0000000000000001
x8 12000056ffffe020 x9 0200007700000000 x10 0200007700000000 x11 0000000000000000
x12 0000007fffffdde0 x13 0000000000000000 x14 02b65b01f7a97490 x15 0000000000000000
x16 0000007fb77376b8 x17 0000000000000012 x18 0000007fb7ed6000 x19 0000005555556078
x20 0000007ffffff768 x21 0000007ffffff778 x22 0000000000000001 x23 0000000000000000
x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
x28 0000000000000000 x29 0000007ffffff6f0 x30 00000055555561b4
... and prints after the dump of memory tags around the buggy address.
Every register is saved exactly as it was at the point where the tag mismatch occurs, with the exception of x16/x17. These registers are used in the tag mismatch calculation as scratch registers during __hwasan_check, and cannot be saved without affecting the fast path. As these registers are designated as scratch registers for linking, there should be no important information in them that could aid in debugging.
Reviewers: pcc, eugenis
Reviewed By: pcc, eugenis
Subscribers: srhines, kubamracek, mgorny, javed.absar, krytarowski, kristof.beyls, hiraditya, jdoerfert, llvm-commits, #sanitizers
Tags: #sanitizers, #llvm
Differential Revision: https://reviews.llvm.org/D58857
llvm-svn: 355738
2019-03-09 05:22:35 +08:00
|
|
|
ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal,
|
|
|
|
registers_frame);
|
hwasan: Move memory access checks into small outlined functions on aarch64.
Each hwasan check requires emitting a small piece of code like this:
https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#memory-accesses
The problem with this is that these code blocks typically bloat code
size significantly.
An obvious solution is to outline these blocks of code. In fact, this
has already been implemented under the -hwasan-instrument-with-calls
flag. However, as currently implemented this has a number of problems:
- The functions use the same calling convention as regular C functions.
This means that the backend must spill all temporary registers as
required by the platform's C calling convention, even though the
check only needs two registers on the hot path.
- The functions take the address to be checked in a fixed register,
which increases register pressure.
Both of these factors can diminish the code size effect and increase
the performance hit of -hwasan-instrument-with-calls.
The solution that this patch implements is to involve the aarch64
backend in outlining the checks. An intrinsic and pseudo-instruction
are created to represent a hwasan check. The pseudo-instruction
is register allocated like any other instruction, and we allow the
register allocator to select almost any register for the address to
check. A particular combination of (register selection, type of check)
triggers the creation in the backend of a function to handle the check
for specifically that pair. The resulting functions are deduplicated by
the linker. The pseudo-instruction (really the function) is specified
to preserve all registers except for the registers that the AAPCS
specifies may be clobbered by a call.
To measure the code size and performance effect of this change, I
took a number of measurements using Chromium for Android on aarch64,
comparing a browser with inlined checks (the baseline) against a
browser with outlined checks.
Code size: Size of .text decreases from 243897420 to 171619972 bytes,
or a 30% decrease.
Performance: Using Chromium's blink_perf.layout microbenchmarks I
measured a median performance regression of 6.24%.
The fact that a perf/size tradeoff is evident here suggests that
we might want to make the new behaviour conditional on -Os/-Oz.
But for now I've enabled it unconditionally, my reasoning being that
hwasan users typically expect a relatively large perf hit, and ~6%
isn't really adding much. We may want to revisit this decision in
the future, though.
I also tried experimenting with varying the number of registers
selectable by the hwasan check pseudo-instruction (which would result
in fewer variants being created), on the hypothesis that creating
fewer variants of the function would expose another perf/size tradeoff
by reducing icache pressure from the check functions at the cost of
register pressure. Although I did observe a code size increase with
fewer registers, I did not observe a strong correlation between the
number of registers and the performance of the resulting browser on the
microbenchmarks, so I conclude that we might as well use ~all registers
to get the maximum code size improvement. My results are below:
Regs | .text size | Perf hit
-----+------------+---------
~all | 171619972 | 6.24%
16 | 171765192 | 7.03%
8 | 172917788 | 5.82%
4 | 177054016 | 6.89%
Differential Revision: https://reviews.llvm.org/D56954
llvm-svn: 351920
2019-01-23 10:20:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
|
|
|
|
AccessInfo ai = GetAccessInfo(info, uc);
|
|
|
|
if (!ai.is_store && !ai.is_load)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
SignalContext sig{info, uc};
|
|
|
|
HandleTagMismatch(ai, StackTrace::GetNextInstructionPc(sig.pc), sig.bp, uc);
|
2017-12-13 09:16:34 +08:00
|
|
|
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
#if defined(__aarch64__)
|
2017-12-13 09:16:34 +08:00
|
|
|
uc->uc_mcontext.pc += 4;
|
[HWASan] Port HWASan to Linux x86-64 (compiler-rt)
Summary:
Porting HWASan to Linux x86-64, first of the three patches, compiler-rt part.
The approach is similar to ARM case, trap signal is used to communicate
memory tag check failure. int3 instruction is used to generate a signal,
access parameters are stored in nop [eax + offset] instruction immediately
following the int3 one
Had to add HWASan init on malloc because, due to much less interceptors
defined (most other sanitizers intercept much more and get initalized
via one of those interceptors or don't care about malloc), HWASan was not
initialized yet when libstdc++ was trying to allocate memory for its own
fixed-size heap, which led to CHECK-fail in AllocateFromLocalPool.
Also added the CHECK() failure handler with more detailed message and
stack reporting.
Reviewers: eugenis
Subscribers: kubamracek, dberris, mgorny, kristof.beyls, delcypher, #sanitizers, llvm-commits
Differential Revision: https://reviews.llvm.org/D44705
llvm-svn: 328385
2018-03-24 07:38:04 +08:00
|
|
|
#elif defined(__x86_64__)
|
|
|
|
#else
|
|
|
|
# error Unsupported architecture
|
|
|
|
#endif
|
2017-12-13 09:16:34 +08:00
|
|
|
return true;
|
2017-12-09 09:31:51 +08:00
|
|
|
}
|
|
|
|
|
[HWASan] Save + print registers when tag mismatch occurs in AArch64.
Summary:
This change change the instrumentation to allow users to view the registers at the point at which tag mismatch occured. Most of the heavy lifting is done in the runtime library, where we save the registers to the stack and emit unwind information. This allows us to reduce the overhead, as very little additional work needs to be done in each __hwasan_check instance.
In this implementation, the fast path of __hwasan_check is unmodified. There are an additional 4 instructions (16B) emitted in the slow path in every __hwasan_check instance. This may increase binary size somewhat, but as most of the work is done in the runtime library, it's manageable.
The failure trace now contains a list of registers at the point of which the failure occured, in a format similar to that of Android's tombstones. It currently has the following format:
Registers where the failure occurred (pc 0x0055555561b4):
x0 0000000000000014 x1 0000007ffffff6c0 x2 1100007ffffff6d0 x3 12000056ffffe025
x4 0000007fff800000 x5 0000000000000014 x6 0000007fff800000 x7 0000000000000001
x8 12000056ffffe020 x9 0200007700000000 x10 0200007700000000 x11 0000000000000000
x12 0000007fffffdde0 x13 0000000000000000 x14 02b65b01f7a97490 x15 0000000000000000
x16 0000007fb77376b8 x17 0000000000000012 x18 0000007fb7ed6000 x19 0000005555556078
x20 0000007ffffff768 x21 0000007ffffff778 x22 0000000000000001 x23 0000000000000000
x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
x28 0000000000000000 x29 0000007ffffff6f0 x30 00000055555561b4
... and prints after the dump of memory tags around the buggy address.
Every register is saved exactly as it was at the point where the tag mismatch occurs, with the exception of x16/x17. These registers are used in the tag mismatch calculation as scratch registers during __hwasan_check, and cannot be saved without affecting the fast path. As these registers are designated as scratch registers for linking, there should be no important information in them that could aid in debugging.
Reviewers: pcc, eugenis
Reviewed By: pcc, eugenis
Subscribers: srhines, kubamracek, mgorny, javed.absar, krytarowski, kristof.beyls, hiraditya, jdoerfert, llvm-commits, #sanitizers
Tags: #sanitizers, #llvm
Differential Revision: https://reviews.llvm.org/D58857
llvm-svn: 355738
2019-03-09 05:22:35 +08:00
|
|
|
// Entry point stub for interoperability between __hwasan_tag_mismatch (ASM) and
|
|
|
|
// the rest of the mismatch handling code (C++).
|
|
|
|
extern "C" void __hwasan_tag_mismatch_stub(uptr addr, uptr access_info,
|
|
|
|
uptr *registers_frame) {
|
hwasan: Move memory access checks into small outlined functions on aarch64.
Each hwasan check requires emitting a small piece of code like this:
https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#memory-accesses
The problem with this is that these code blocks typically bloat code
size significantly.
An obvious solution is to outline these blocks of code. In fact, this
has already been implemented under the -hwasan-instrument-with-calls
flag. However, as currently implemented this has a number of problems:
- The functions use the same calling convention as regular C functions.
This means that the backend must spill all temporary registers as
required by the platform's C calling convention, even though the
check only needs two registers on the hot path.
- The functions take the address to be checked in a fixed register,
which increases register pressure.
Both of these factors can diminish the code size effect and increase
the performance hit of -hwasan-instrument-with-calls.
The solution that this patch implements is to involve the aarch64
backend in outlining the checks. An intrinsic and pseudo-instruction
are created to represent a hwasan check. The pseudo-instruction
is register allocated like any other instruction, and we allow the
register allocator to select almost any register for the address to
check. A particular combination of (register selection, type of check)
triggers the creation in the backend of a function to handle the check
for specifically that pair. The resulting functions are deduplicated by
the linker. The pseudo-instruction (really the function) is specified
to preserve all registers except for the registers that the AAPCS
specifies may be clobbered by a call.
To measure the code size and performance effect of this change, I
took a number of measurements using Chromium for Android on aarch64,
comparing a browser with inlined checks (the baseline) against a
browser with outlined checks.
Code size: Size of .text decreases from 243897420 to 171619972 bytes,
or a 30% decrease.
Performance: Using Chromium's blink_perf.layout microbenchmarks I
measured a median performance regression of 6.24%.
The fact that a perf/size tradeoff is evident here suggests that
we might want to make the new behaviour conditional on -Os/-Oz.
But for now I've enabled it unconditionally, my reasoning being that
hwasan users typically expect a relatively large perf hit, and ~6%
isn't really adding much. We may want to revisit this decision in
the future, though.
I also tried experimenting with varying the number of registers
selectable by the hwasan check pseudo-instruction (which would result
in fewer variants being created), on the hypothesis that creating
fewer variants of the function would expose another perf/size tradeoff
by reducing icache pressure from the check functions at the cost of
register pressure. Although I did observe a code size increase with
fewer registers, I did not observe a strong correlation between the
number of registers and the performance of the resulting browser on the
microbenchmarks, so I conclude that we might as well use ~all registers
to get the maximum code size improvement. My results are below:
Regs | .text size | Perf hit
-----+------------+---------
~all | 171619972 | 6.24%
16 | 171765192 | 7.03%
8 | 172917788 | 5.82%
4 | 177054016 | 6.89%
Differential Revision: https://reviews.llvm.org/D56954
llvm-svn: 351920
2019-01-23 10:20:10 +08:00
|
|
|
AccessInfo ai;
|
|
|
|
ai.is_store = access_info & 0x10;
|
|
|
|
ai.recover = false;
|
|
|
|
ai.addr = addr;
|
|
|
|
ai.size = 1 << (access_info & 0xf);
|
|
|
|
|
|
|
|
HandleTagMismatch(ai, (uptr)__builtin_return_address(0),
|
[HWASan] Save + print registers when tag mismatch occurs in AArch64.
Summary:
This change change the instrumentation to allow users to view the registers at the point at which tag mismatch occured. Most of the heavy lifting is done in the runtime library, where we save the registers to the stack and emit unwind information. This allows us to reduce the overhead, as very little additional work needs to be done in each __hwasan_check instance.
In this implementation, the fast path of __hwasan_check is unmodified. There are an additional 4 instructions (16B) emitted in the slow path in every __hwasan_check instance. This may increase binary size somewhat, but as most of the work is done in the runtime library, it's manageable.
The failure trace now contains a list of registers at the point of which the failure occured, in a format similar to that of Android's tombstones. It currently has the following format:
Registers where the failure occurred (pc 0x0055555561b4):
x0 0000000000000014 x1 0000007ffffff6c0 x2 1100007ffffff6d0 x3 12000056ffffe025
x4 0000007fff800000 x5 0000000000000014 x6 0000007fff800000 x7 0000000000000001
x8 12000056ffffe020 x9 0200007700000000 x10 0200007700000000 x11 0000000000000000
x12 0000007fffffdde0 x13 0000000000000000 x14 02b65b01f7a97490 x15 0000000000000000
x16 0000007fb77376b8 x17 0000000000000012 x18 0000007fb7ed6000 x19 0000005555556078
x20 0000007ffffff768 x21 0000007ffffff778 x22 0000000000000001 x23 0000000000000000
x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
x28 0000000000000000 x29 0000007ffffff6f0 x30 00000055555561b4
... and prints after the dump of memory tags around the buggy address.
Every register is saved exactly as it was at the point where the tag mismatch occurs, with the exception of x16/x17. These registers are used in the tag mismatch calculation as scratch registers during __hwasan_check, and cannot be saved without affecting the fast path. As these registers are designated as scratch registers for linking, there should be no important information in them that could aid in debugging.
Reviewers: pcc, eugenis
Reviewed By: pcc, eugenis
Subscribers: srhines, kubamracek, mgorny, javed.absar, krytarowski, kristof.beyls, hiraditya, jdoerfert, llvm-commits, #sanitizers
Tags: #sanitizers, #llvm
Differential Revision: https://reviews.llvm.org/D58857
llvm-svn: 355738
2019-03-09 05:22:35 +08:00
|
|
|
(uptr)__builtin_frame_address(0), nullptr, registers_frame);
|
hwasan: Move memory access checks into small outlined functions on aarch64.
Each hwasan check requires emitting a small piece of code like this:
https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#memory-accesses
The problem with this is that these code blocks typically bloat code
size significantly.
An obvious solution is to outline these blocks of code. In fact, this
has already been implemented under the -hwasan-instrument-with-calls
flag. However, as currently implemented this has a number of problems:
- The functions use the same calling convention as regular C functions.
This means that the backend must spill all temporary registers as
required by the platform's C calling convention, even though the
check only needs two registers on the hot path.
- The functions take the address to be checked in a fixed register,
which increases register pressure.
Both of these factors can diminish the code size effect and increase
the performance hit of -hwasan-instrument-with-calls.
The solution that this patch implements is to involve the aarch64
backend in outlining the checks. An intrinsic and pseudo-instruction
are created to represent a hwasan check. The pseudo-instruction
is register allocated like any other instruction, and we allow the
register allocator to select almost any register for the address to
check. A particular combination of (register selection, type of check)
triggers the creation in the backend of a function to handle the check
for specifically that pair. The resulting functions are deduplicated by
the linker. The pseudo-instruction (really the function) is specified
to preserve all registers except for the registers that the AAPCS
specifies may be clobbered by a call.
To measure the code size and performance effect of this change, I
took a number of measurements using Chromium for Android on aarch64,
comparing a browser with inlined checks (the baseline) against a
browser with outlined checks.
Code size: Size of .text decreases from 243897420 to 171619972 bytes,
or a 30% decrease.
Performance: Using Chromium's blink_perf.layout microbenchmarks I
measured a median performance regression of 6.24%.
The fact that a perf/size tradeoff is evident here suggests that
we might want to make the new behaviour conditional on -Os/-Oz.
But for now I've enabled it unconditionally, my reasoning being that
hwasan users typically expect a relatively large perf hit, and ~6%
isn't really adding much. We may want to revisit this decision in
the future, though.
I also tried experimenting with varying the number of registers
selectable by the hwasan check pseudo-instruction (which would result
in fewer variants being created), on the hypothesis that creating
fewer variants of the function would expose another perf/size tradeoff
by reducing icache pressure from the check functions at the cost of
register pressure. Although I did observe a code size increase with
fewer registers, I did not observe a strong correlation between the
number of registers and the performance of the resulting browser on the
microbenchmarks, so I conclude that we might as well use ~all registers
to get the maximum code size improvement. My results are below:
Regs | .text size | Perf hit
-----+------------+---------
~all | 171619972 | 6.24%
16 | 171765192 | 7.03%
8 | 172917788 | 5.82%
4 | 177054016 | 6.89%
Differential Revision: https://reviews.llvm.org/D56954
llvm-svn: 351920
2019-01-23 10:20:10 +08:00
|
|
|
__builtin_unreachable();
|
|
|
|
}
|
|
|
|
|
2017-12-09 09:31:51 +08:00
|
|
|
static void OnStackUnwind(const SignalContext &sig, const void *,
|
|
|
|
BufferedStackTrace *stack) {
|
2019-03-01 12:03:38 +08:00
|
|
|
stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,
|
|
|
|
common_flags()->fast_unwind_on_fatal);
|
2017-12-09 09:31:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void HwasanOnDeadlySignal(int signo, void *info, void *context) {
|
|
|
|
// Probably a tag mismatch.
|
2018-02-22 03:52:23 +08:00
|
|
|
if (signo == SIGTRAP)
|
|
|
|
if (HwasanOnSIGTRAP(signo, (siginfo_t *)info, (ucontext_t*)context))
|
2017-12-13 09:16:34 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
HandleDeadlySignal(info, context, GetTid(), &OnStackUnwind, nullptr);
|
2017-12-09 09:31:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace __hwasan
|
|
|
|
|
|
|
|
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
|