diff --git a/compiler-rt/lib/hwasan/hwasan.h b/compiler-rt/lib/hwasan/hwasan.h --- a/compiler-rt/lib/hwasan/hwasan.h +++ b/compiler-rt/lib/hwasan/hwasan.h @@ -163,4 +163,22 @@ typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1]; #endif // HWASAN_WITH_INTERCEPTORS && __aarch64__ +// Callback function type for the globals iteration functions below. Arguments +// include the fully-relocated pointer to the global, the size and tag of said +// global, and the data argument passed to the iteration function that +// dispatches this callback. Returns true if the iteration should stop, false +// otherwise. +typedef bool (*hwasan_globals_cb)(uptr global_ptr, uptr global_size, + u8 global_tag, void *data); + +// Walk through all loaded DSO's, and call `cb()` for each HWASan global. +// This function returns the return value of the last `cb()` invocation. +bool IterateHwasanGlobals(hwasan_globals_cb cb, void *data); +// Walk through the specific DSO (as specified by the base, phdr, and phnum), +// calling `cb()` for each HWASan global. Returns the return value of the last +// `cb()` invocation. +bool IterateHwasanGlobalsInObject(ElfW(Addr) base, const ElfW(Phdr) * phdr, + ElfW(Half) phnum, hwasan_globals_cb cb, + void *data); + #endif // HWASAN_H diff --git a/compiler-rt/lib/hwasan/hwasan.cpp b/compiler-rt/lib/hwasan/hwasan.cpp --- a/compiler-rt/lib/hwasan/hwasan.cpp +++ b/compiler-rt/lib/hwasan/hwasan.cpp @@ -199,18 +199,6 @@ u32 info; }; -static void InitGlobals(const hwasan_global *begin, const hwasan_global *end) { - for (auto *desc = begin; desc != end; ++desc) { - uptr gv = reinterpret_cast(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); - } -} - enum { NT_LLVM_HWASAN_GLOBALS = 3 }; struct hwasan_global_note { @@ -244,17 +232,24 @@ } } -static void InitGlobalsFromPhdrs(ElfW(Addr) base, const ElfW(Phdr) * phdr, - ElfW(Half) phnum) { +bool IterateHwasanGlobalsInObject(ElfW(Addr) base, const ElfW(Phdr) * phdr, + ElfW(Half) phnum, hwasan_globals_cb cb, + void *data) { + // Read the phdrs from this DSO. for (unsigned i = 0; i != phnum; ++i) { if (phdr[i].p_type != PT_NOTE) continue; + const char *note = reinterpret_cast(base + phdr[i].p_vaddr); const char *nend = note + phdr[i].p_memsz; + + // Traverse all the notes until we find a HWASan note. while (note < nend) { auto *nhdr = reinterpret_cast(note); const char *name = note + sizeof(ElfW(Nhdr)); const char *desc = name + RoundUpTo(nhdr->n_namesz, 4); + + // Discard non-HWASan-Globals notes. if (nhdr->n_type != NT_LLVM_HWASAN_GLOBALS || internal_strcmp(name, "LLVM") != 0) { note = desc + RoundUpTo(nhdr->n_descsz, 4); @@ -266,24 +261,53 @@ CheckCodeModel(base, phdr, phnum); auto *global_note = reinterpret_cast(desc); - auto *global_begin = reinterpret_cast( + auto *globals_begin = reinterpret_cast( note + global_note->begin_relptr); - auto *global_end = reinterpret_cast( + auto *globals_end = reinterpret_cast( note + global_note->end_relptr); - InitGlobals(global_begin, global_end); - return; + + for (auto *global = globals_begin; global != globals_end; ++global) { + uptr addr = reinterpret_cast(global) + global->gv_relptr; + uptr size = global->info & 0xffffff; + u8 tag = global->info >> 24; + if (cb(addr, size, tag, data)) + return true; + } + break; } } + + return false; +} + +bool IterateHwasanGlobals(hwasan_globals_cb cb, void *data) { + struct HwasanGlobalsCbArgumentWrapper { + hwasan_globals_cb cb; + void *data; + }; + HwasanGlobalsCbArgumentWrapper args{.cb = cb, .data = data}; + return dl_iterate_phdr( + [](dl_phdr_info *info, size_t /* size */, void *data) -> int { + auto *args = + reinterpret_cast(data); + return IterateHwasanGlobalsInObject( + info->dlpi_addr, info->dlpi_phdr, info->dlpi_phnum, args->cb, + args->data); + }, + &args) == 0; +} + +static bool InitializeSingleGlobal(uptr addr, uptr size, u8 tag, + void * /* data */) { + uptr full_granule_size = RoundDownTo(size, 16); + TagMemoryAligned(addr, full_granule_size, tag); + if (size % 16) + TagMemoryAligned(addr + full_granule_size, 16, size % 16); + return false; } 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); + IterateHwasanGlobals(InitializeSingleGlobal, nullptr); } // Prepare to run instrumented code on the main thread. @@ -321,11 +345,11 @@ // 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, + IterateHwasanGlobalsInObject( + /* base */ 0, reinterpret_cast( reinterpret_cast(&__ehdr_start) + __ehdr_start.e_phoff), - __ehdr_start.e_phnum); + __ehdr_start.e_phnum, InitializeSingleGlobal, /* data */ nullptr); } void __hwasan_init() { @@ -384,7 +408,8 @@ void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr, ElfW(Half) phnum) { - InitGlobalsFromPhdrs(base, phdr, phnum); + IterateHwasanGlobalsInObject(base, phdr, phnum, InitializeSingleGlobal, + /* data */ nullptr); } void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr, diff --git a/compiler-rt/lib/hwasan/hwasan_report.cpp b/compiler-rt/lib/hwasan/hwasan_report.cpp --- a/compiler-rt/lib/hwasan/hwasan_report.cpp +++ b/compiler-rt/lib/hwasan/hwasan_report.cpp @@ -26,6 +26,8 @@ #include "sanitizer_common/sanitizer_stacktrace_printer.h" #include "sanitizer_common/sanitizer_symbolizer.h" +#include + using namespace __sanitizer; namespace __hwasan { @@ -243,6 +245,45 @@ return tag == inline_tag; } +// HWASan globals store the size of the global in the descriptor. In cases where +// we don't have a binary with symbols, we can't grab the size of the global +// from the debug info - but we might be able to retrieve it from the phdr. +// Returns zero if the lookup failed. +static uptr GetGlobalSizeFromDescriptor(uptr ptr) { + uptr global_size = 0u; + + struct GlobalSizeWalkArgWrapper { + uptr ptr; + uptr *global_size; + }; + + GlobalSizeWalkArgWrapper args{.ptr = ptr, .global_size = &global_size}; + + // Find the ELF object that this global resides in. + Dl_info info; + dladdr(reinterpret_cast(ptr), &info); + auto *ehdr = reinterpret_cast(info.dli_fbase); + auto *phdr = reinterpret_cast(reinterpret_cast(ehdr) + + ehdr->e_phoff); + + // Walk all globals in this ELF object, looking for the one we're interested + // in. Once we find it, we can stop iterating and return the size of the + // global we're interested in. + IterateHwasanGlobalsInObject( + reinterpret_cast(ehdr), phdr, ehdr->e_phnum, + [](uptr global_addr, uptr global_size, u8 /* tag */, void *data) -> bool { + auto *args = reinterpret_cast(data); + if (global_addr <= args->ptr && args->ptr < global_addr + global_size) { + *args->global_size = global_size; + return true; + } + return false; + }, + &args); + + return global_size; +} + void PrintAddressDescription( uptr tagged_addr, uptr access_size, StackAllocationsRingBuffer *current_stack_allocations) { @@ -319,9 +360,18 @@ 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); + uptr size = GetGlobalSizeFromDescriptor(mem); + if (size == 0) + 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); + else + Printf( + "%p is located to the %s of a %zd-byte global variable in " + "(%s+0x%x)\n", + untagged_addr, candidate == left ? "right" : "left", size, + module_name, module_address); } num_descriptions_printed++; } diff --git a/compiler-rt/test/hwasan/TestCases/global.c b/compiler-rt/test/hwasan/TestCases/global.c --- a/compiler-rt/test/hwasan/TestCases/global.c +++ b/compiler-rt/test/hwasan/TestCases/global.c @@ -9,9 +9,9 @@ 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+{{.*}}) + // RNOSYM: is located to the right of a 4-byte 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+{{.*}}) + // LNOSYM: is located to the left of a 4-byte global variable in ({{.*}}global.c.tmp+{{.*}}) // CHECK-NOT: can not describe (&x)[atoi(argv[1])] = 1; }