summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Collingbourne <peter@pcc.me.uk>2019-08-06 22:07:29 +0000
committerPeter Collingbourne <peter@pcc.me.uk>2019-08-06 22:07:29 +0000
commit63597da68262b255f805b9380df8d7491e248e0a (patch)
tree82aa51e6d749309b3492e005893dd6f858f89b17
parent47e8efc9d1d1d182f0ee3a662589b983f17f5c4c (diff)
downloadcompiler-rt-63597da68262b255f805b9380df8d7491e248e0a.tar.gz
hwasan: Instrument globals.
Globals are instrumented by adding a pointer tag to their symbol values and emitting metadata into a special section that allows the runtime to tag their memory when the library is loaded. Due to order of initialization issues explained in more detail in the comments, shadow initialization cannot happen during regular global initialization. Instead, the location of the global section is marked using an ELF note, and we require libc support for calling a function provided by the HWASAN runtime when libraries are loaded and unloaded. Based on ideas discussed with @evgeny777 in D56672. Differential Revision: https://reviews.llvm.org/D65770 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@368102 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/hwasan/hwasan.cpp123
-rw-r--r--lib/hwasan/hwasan.h1
-rw-r--r--lib/hwasan/hwasan_interface_internal.h9
-rw-r--r--lib/hwasan/hwasan_report.cpp25
-rw-r--r--test/hwasan/TestCases/global.c17
-rw-r--r--test/hwasan/lit.cfg.py7
6 files changed, 161 insertions, 21 deletions
diff --git a/lib/hwasan/hwasan.cpp b/lib/hwasan/hwasan.cpp
index 6f2246552..f0330247a 100644
--- a/lib/hwasan/hwasan.cpp
+++ b/lib/hwasan/hwasan.cpp
@@ -193,27 +193,12 @@ void UpdateMemoryUsage() {
void UpdateMemoryUsage() {}
#endif
-// Prepare to run instrumented code on the main thread.
-void InitInstrumentation() {
- if (hwasan_instrumentation_inited) return;
-
- if (!InitShadow()) {
- Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
- DumpProcessMap();
- Die();
- }
-
- InitThreads();
- hwasanThreadList().CreateCurrentThread();
-
- hwasan_instrumentation_inited = 1;
-}
-
} // namespace __hwasan
+using namespace __hwasan;
+
void __sanitizer::BufferedStackTrace::UnwindImpl(
uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
- using namespace __hwasan;
Thread *t = GetCurrentThread();
if (!t) {
// the thread is still being created.
@@ -231,9 +216,85 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
Unwind(max_depth, pc, 0, context, 0, 0, false);
}
-// Interface.
+struct hwasan_global {
+ s32 gv_relptr;
+ u32 info;
+};
+
+static void InitGlobals(const hwasan_global *begin, const hwasan_global *end) {
+ for (auto *desc = begin; desc != end; ++desc) {
+ uptr gv = reinterpret_cast<uptr>(desc) + desc->gv_relptr;
+ uptr size = desc->info & 0xffffff;
+ uptr full_granule_size = RoundDownTo(size, 16);
+ u8 tag = desc->info >> 24;
+ TagMemoryAligned(gv, full_granule_size, tag);
+ if (size % 16)
+ TagMemoryAligned(gv + full_granule_size, 16, size % 16);
+ }
+}
-using namespace __hwasan;
+enum { NT_LLVM_HWASAN_GLOBALS = 3 };
+
+struct hwasan_global_note {
+ s32 begin_relptr;
+ s32 end_relptr;
+};
+
+static void InitGlobalsFromPhdrs(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+ ElfW(Half) phnum) {
+ for (; phnum != 0; ++phdr, --phnum) {
+ if (phdr->p_type != PT_NOTE)
+ continue;
+ const char *note = reinterpret_cast<const char *>(base + phdr->p_vaddr);
+ const char *nend = note + phdr->p_memsz;
+ while (note < nend) {
+ auto *nhdr = reinterpret_cast<const ElfW(Nhdr) *>(note);
+ const char *name = note + sizeof(ElfW(Nhdr));
+ const char *desc = name + nhdr->n_namesz;
+ if (nhdr->n_type != NT_LLVM_HWASAN_GLOBALS ||
+ internal_strcmp(name, "LLVM") != 0) {
+ note = desc + nhdr->n_descsz;
+ continue;
+ }
+
+ auto *global_note = reinterpret_cast<const hwasan_global_note *>(desc);
+ auto *global_begin = reinterpret_cast<const hwasan_global *>(
+ note + global_note->begin_relptr);
+ auto *global_end = reinterpret_cast<const hwasan_global *>(
+ note + global_note->end_relptr);
+ InitGlobals(global_begin, global_end);
+ return;
+ }
+ }
+}
+
+static void InitLoadedGlobals() {
+ dl_iterate_phdr(
+ [](dl_phdr_info *info, size_t size, void *data) {
+ InitGlobalsFromPhdrs(info->dlpi_addr, info->dlpi_phdr,
+ info->dlpi_phnum);
+ return 0;
+ },
+ nullptr);
+}
+
+// Prepare to run instrumented code on the main thread.
+static void InitInstrumentation() {
+ if (hwasan_instrumentation_inited) return;
+
+ if (!InitShadow()) {
+ Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
+ DumpProcessMap();
+ Die();
+ }
+
+ InitThreads();
+ hwasanThreadList().CreateCurrentThread();
+
+ hwasan_instrumentation_inited = 1;
+}
+
+// Interface.
uptr __hwasan_shadow_memory_dynamic_address; // Global interface symbol.
@@ -244,6 +305,17 @@ void __hwasan_init_frames(uptr beg, uptr end) {}
void __hwasan_init_static() {
InitShadowGOT();
InitInstrumentation();
+
+ // In the non-static code path we call dl_iterate_phdr here. But at this point
+ // libc might not have been initialized enough for dl_iterate_phdr to work.
+ // Fortunately, since this is a statically linked executable we can use the
+ // linker-defined symbol __ehdr_start to find the only relevant set of phdrs.
+ extern ElfW(Ehdr) __ehdr_start;
+ InitGlobalsFromPhdrs(
+ 0,
+ reinterpret_cast<const ElfW(Phdr) *>(
+ reinterpret_cast<const char *>(&__ehdr_start) + __ehdr_start.e_phoff),
+ __ehdr_start.e_phnum);
}
void __hwasan_init() {
@@ -267,6 +339,7 @@ void __hwasan_init() {
DisableCoreDumperIfNecessary();
InitInstrumentation();
+ InitLoadedGlobals();
// Needs to be called here because flags()->random_tags might not have been
// initialized when InitInstrumentation() was called.
@@ -301,6 +374,18 @@ void __hwasan_init() {
hwasan_inited = 1;
}
+void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+ ElfW(Half) phnum) {
+ InitGlobalsFromPhdrs(base, phdr, phnum);
+}
+
+void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+ ElfW(Half) phnum) {
+ for (; phnum != 0; ++phdr, --phnum)
+ if (phdr->p_type == PT_LOAD)
+ TagMemory(base + phdr->p_vaddr, phdr->p_memsz, 0);
+}
+
void __hwasan_print_shadow(const void *p, uptr sz) {
uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
uptr shadow_first = MemToShadow(ptr_raw);
diff --git a/lib/hwasan/hwasan.h b/lib/hwasan/hwasan.h
index 465e56c3a..817cee650 100644
--- a/lib/hwasan/hwasan.h
+++ b/lib/hwasan/hwasan.h
@@ -75,7 +75,6 @@ extern int hwasan_report_count;
bool ProtectRange(uptr beg, uptr end);
bool InitShadow();
void InitThreads();
-void InitInstrumentation();
void MadviseShadow();
char *GetProcSelfMaps();
void InitializeInterceptors();
diff --git a/lib/hwasan/hwasan_interface_internal.h b/lib/hwasan/hwasan_interface_internal.h
index 1b10d76c7..ca57f0fe4 100644
--- a/lib/hwasan/hwasan_interface_internal.h
+++ b/lib/hwasan/hwasan_interface_internal.h
@@ -16,6 +16,7 @@
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include <link.h>
extern "C" {
@@ -25,6 +26,14 @@ void __hwasan_init_static();
SANITIZER_INTERFACE_ATTRIBUTE
void __hwasan_init();
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+ ElfW(Half) phnum);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
+ ElfW(Half) phnum);
+
using __sanitizer::uptr;
using __sanitizer::sptr;
using __sanitizer::uu64;
diff --git a/lib/hwasan/hwasan_report.cpp b/lib/hwasan/hwasan_report.cpp
index 346889797..19cb27554 100644
--- a/lib/hwasan/hwasan_report.cpp
+++ b/lib/hwasan/hwasan_report.cpp
@@ -278,6 +278,31 @@ void PrintAddressDescription(
Printf("%s", d.Default());
GetStackTraceFromId(chunk.GetAllocStackId()).Print();
num_descriptions_printed++;
+ } else {
+ // Check whether the address points into a loaded library. If so, this is
+ // most likely a global variable.
+ const char *module_name;
+ uptr module_address;
+ Symbolizer *sym = Symbolizer::GetOrInit();
+ if (sym->GetModuleNameAndOffsetForPC(mem, &module_name,
+ &module_address)) {
+ DataInfo info;
+ if (sym->SymbolizeData(mem, &info) && info.start) {
+ Printf(
+ "%p is located %zd bytes to the %s of %zd-byte global variable "
+ "%s [%p,%p) in %s\n",
+ untagged_addr,
+ candidate == left ? untagged_addr - (info.start + info.size)
+ : info.start - untagged_addr,
+ candidate == left ? "right" : "left", info.size, info.name,
+ info.start, info.start + info.size, module_name);
+ } else {
+ Printf("%p is located to the %s of a global variable in (%s+0x%x)\n",
+ untagged_addr, candidate == left ? "right" : "left",
+ module_name, module_address);
+ }
+ num_descriptions_printed++;
+ }
}
}
diff --git a/test/hwasan/TestCases/global.c b/test/hwasan/TestCases/global.c
new file mode 100644
index 000000000..f1aef2290
--- /dev/null
+++ b/test/hwasan/TestCases/global.c
@@ -0,0 +1,17 @@
+// RUN: %clang_hwasan %s -o %t
+// RUN: %run %t 0
+// RUN: not %run %t 1 2>&1 | FileCheck --check-prefixes=CHECK,RSYM %s
+// RUN: not %env_hwasan_opts=symbolize=0 %run %t 1 2>&1 | FileCheck --check-prefixes=CHECK,RNOSYM %s
+// RUN: not %run %t -1 2>&1 | FileCheck --check-prefixes=CHECK,LSYM %s
+// RUN: not %env_hwasan_opts=symbolize=0 %run %t -1 2>&1 | FileCheck --check-prefixes=CHECK,LNOSYM %s
+
+int x = 1;
+
+int main(int argc, char **argv) {
+ // RSYM: is located 0 bytes to the right of 4-byte global variable x {{.*}} in {{.*}}global.c.tmp
+ // RNOSYM: is located to the right of a global variable in ({{.*}}global.c.tmp+{{.*}})
+ // LSYM: is located 4 bytes to the left of 4-byte global variable x {{.*}} in {{.*}}global.c.tmp
+ // LNOSYM: is located to the left of a global variable in ({{.*}}global.c.tmp+{{.*}})
+ // CHECK-NOT: can not describe
+ (&x)[atoi(argv[1])] = 1;
+}
diff --git a/test/hwasan/lit.cfg.py b/test/hwasan/lit.cfg.py
index d4f8a46e1..f506e703b 100644
--- a/test/hwasan/lit.cfg.py
+++ b/test/hwasan/lit.cfg.py
@@ -11,7 +11,12 @@ config.test_source_root = os.path.dirname(__file__)
# Setup default compiler flags used with -fsanitize=memory option.
clang_cflags = [config.target_cflags] + config.debug_info_flags
clang_cxxflags = config.cxx_mode_flags + clang_cflags
-clang_hwasan_cflags = ["-fsanitize=hwaddress"] + clang_cflags
+clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-globals"] + clang_cflags
+if config.target_arch == 'x86_64':
+ # This does basically the same thing as tagged-globals on aarch64. Because
+ # the x86_64 implementation is for testing purposes only there is no
+ # equivalent target feature implemented on x86_64.
+ clang_hwasan_cflags += ["-mcmodel=large"]
clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags
def build_invocation(compile_flags):