Skip to content

Commit 0930643

Browse files
committed
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 llvm-svn: 368102
1 parent b3292a8 commit 0930643

File tree

11 files changed

+429
-31
lines changed

11 files changed

+429
-31
lines changed

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,11 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
10131013
CmdArgs.push_back(Args.MakeArgString("hwasan-abi=" + HwasanAbi));
10141014
}
10151015

1016+
if (Sanitizers.has(SanitizerKind::HWAddress)) {
1017+
CmdArgs.push_back("-target-feature");
1018+
CmdArgs.push_back("+tagged-globals");
1019+
}
1020+
10161021
// MSan: Workaround for PR16386.
10171022
// ASan: This is mainly to help LSan with cases such as
10181023
// https://github.com/google/sanitizers/issues/373

clang/test/Driver/fsanitize.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,9 @@
843843
// RUN: %clang -target x86_64-linux-gnu -fsanitize=hwaddress -fsanitize-hwaddress-abi=platform %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-HWASAN-PLATFORM-ABI
844844
// RUN: %clang -target x86_64-linux-gnu -fsanitize=hwaddress -fsanitize-hwaddress-abi=foo %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-HWASAN-FOO-ABI
845845
// CHECK-HWASAN-INTERCEPTOR-ABI: "-default-function-attr" "hwasan-abi=interceptor"
846+
// CHECK-HWASAN-INTERCEPTOR-ABI: "-target-feature" "+tagged-globals"
846847
// CHECK-HWASAN-PLATFORM-ABI: "-default-function-attr" "hwasan-abi=platform"
848+
// CHECK-HWASAN-PLATFORM-ABI: "-target-feature" "+tagged-globals"
847849
// CHECK-HWASAN-FOO-ABI: error: invalid value 'foo' in '-fsanitize-hwaddress-abi=foo'
848850

849851
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address,pointer-compare,pointer-subtract %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-POINTER-ALL

compiler-rt/lib/hwasan/hwasan.cpp

Lines changed: 104 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -193,27 +193,12 @@ void UpdateMemoryUsage() {
193193
void UpdateMemoryUsage() {}
194194
#endif
195195

196-
// Prepare to run instrumented code on the main thread.
197-
void InitInstrumentation() {
198-
if (hwasan_instrumentation_inited) return;
199-
200-
if (!InitShadow()) {
201-
Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
202-
DumpProcessMap();
203-
Die();
204-
}
205-
206-
InitThreads();
207-
hwasanThreadList().CreateCurrentThread();
208-
209-
hwasan_instrumentation_inited = 1;
210-
}
211-
212196
} // namespace __hwasan
213197

198+
using namespace __hwasan;
199+
214200
void __sanitizer::BufferedStackTrace::UnwindImpl(
215201
uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
216-
using namespace __hwasan;
217202
Thread *t = GetCurrentThread();
218203
if (!t) {
219204
// the thread is still being created.
@@ -231,9 +216,85 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
231216
Unwind(max_depth, pc, 0, context, 0, 0, false);
232217
}
233218

234-
// Interface.
219+
struct hwasan_global {
220+
s32 gv_relptr;
221+
u32 info;
222+
};
223+
224+
static void InitGlobals(const hwasan_global *begin, const hwasan_global *end) {
225+
for (auto *desc = begin; desc != end; ++desc) {
226+
uptr gv = reinterpret_cast<uptr>(desc) + desc->gv_relptr;
227+
uptr size = desc->info & 0xffffff;
228+
uptr full_granule_size = RoundDownTo(size, 16);
229+
u8 tag = desc->info >> 24;
230+
TagMemoryAligned(gv, full_granule_size, tag);
231+
if (size % 16)
232+
TagMemoryAligned(gv + full_granule_size, 16, size % 16);
233+
}
234+
}
235235

236-
using namespace __hwasan;
236+
enum { NT_LLVM_HWASAN_GLOBALS = 3 };
237+
238+
struct hwasan_global_note {
239+
s32 begin_relptr;
240+
s32 end_relptr;
241+
};
242+
243+
static void InitGlobalsFromPhdrs(ElfW(Addr) base, const ElfW(Phdr) * phdr,
244+
ElfW(Half) phnum) {
245+
for (; phnum != 0; ++phdr, --phnum) {
246+
if (phdr->p_type != PT_NOTE)
247+
continue;
248+
const char *note = reinterpret_cast<const char *>(base + phdr->p_vaddr);
249+
const char *nend = note + phdr->p_memsz;
250+
while (note < nend) {
251+
auto *nhdr = reinterpret_cast<const ElfW(Nhdr) *>(note);
252+
const char *name = note + sizeof(ElfW(Nhdr));
253+
const char *desc = name + nhdr->n_namesz;
254+
if (nhdr->n_type != NT_LLVM_HWASAN_GLOBALS ||
255+
internal_strcmp(name, "LLVM") != 0) {
256+
note = desc + nhdr->n_descsz;
257+
continue;
258+
}
259+
260+
auto *global_note = reinterpret_cast<const hwasan_global_note *>(desc);
261+
auto *global_begin = reinterpret_cast<const hwasan_global *>(
262+
note + global_note->begin_relptr);
263+
auto *global_end = reinterpret_cast<const hwasan_global *>(
264+
note + global_note->end_relptr);
265+
InitGlobals(global_begin, global_end);
266+
return;
267+
}
268+
}
269+
}
270+
271+
static void InitLoadedGlobals() {
272+
dl_iterate_phdr(
273+
[](dl_phdr_info *info, size_t size, void *data) {
274+
InitGlobalsFromPhdrs(info->dlpi_addr, info->dlpi_phdr,
275+
info->dlpi_phnum);
276+
return 0;
277+
},
278+
nullptr);
279+
}
280+
281+
// Prepare to run instrumented code on the main thread.
282+
static void InitInstrumentation() {
283+
if (hwasan_instrumentation_inited) return;
284+
285+
if (!InitShadow()) {
286+
Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
287+
DumpProcessMap();
288+
Die();
289+
}
290+
291+
InitThreads();
292+
hwasanThreadList().CreateCurrentThread();
293+
294+
hwasan_instrumentation_inited = 1;
295+
}
296+
297+
// Interface.
237298

238299
uptr __hwasan_shadow_memory_dynamic_address; // Global interface symbol.
239300

@@ -244,6 +305,17 @@ void __hwasan_init_frames(uptr beg, uptr end) {}
244305
void __hwasan_init_static() {
245306
InitShadowGOT();
246307
InitInstrumentation();
308+
309+
// In the non-static code path we call dl_iterate_phdr here. But at this point
310+
// libc might not have been initialized enough for dl_iterate_phdr to work.
311+
// Fortunately, since this is a statically linked executable we can use the
312+
// linker-defined symbol __ehdr_start to find the only relevant set of phdrs.
313+
extern ElfW(Ehdr) __ehdr_start;
314+
InitGlobalsFromPhdrs(
315+
0,
316+
reinterpret_cast<const ElfW(Phdr) *>(
317+
reinterpret_cast<const char *>(&__ehdr_start) + __ehdr_start.e_phoff),
318+
__ehdr_start.e_phnum);
247319
}
248320

249321
void __hwasan_init() {
@@ -267,6 +339,7 @@ void __hwasan_init() {
267339
DisableCoreDumperIfNecessary();
268340

269341
InitInstrumentation();
342+
InitLoadedGlobals();
270343

271344
// Needs to be called here because flags()->random_tags might not have been
272345
// initialized when InitInstrumentation() was called.
@@ -301,6 +374,18 @@ void __hwasan_init() {
301374
hwasan_inited = 1;
302375
}
303376

377+
void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
378+
ElfW(Half) phnum) {
379+
InitGlobalsFromPhdrs(base, phdr, phnum);
380+
}
381+
382+
void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
383+
ElfW(Half) phnum) {
384+
for (; phnum != 0; ++phdr, --phnum)
385+
if (phdr->p_type == PT_LOAD)
386+
TagMemory(base + phdr->p_vaddr, phdr->p_memsz, 0);
387+
}
388+
304389
void __hwasan_print_shadow(const void *p, uptr sz) {
305390
uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
306391
uptr shadow_first = MemToShadow(ptr_raw);

compiler-rt/lib/hwasan/hwasan.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ extern int hwasan_report_count;
7575
bool ProtectRange(uptr beg, uptr end);
7676
bool InitShadow();
7777
void InitThreads();
78-
void InitInstrumentation();
7978
void MadviseShadow();
8079
char *GetProcSelfMaps();
8180
void InitializeInterceptors();

compiler-rt/lib/hwasan/hwasan_interface_internal.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "sanitizer_common/sanitizer_internal_defs.h"
1818
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
19+
#include <link.h>
1920

2021
extern "C" {
2122

@@ -25,6 +26,14 @@ void __hwasan_init_static();
2526
SANITIZER_INTERFACE_ATTRIBUTE
2627
void __hwasan_init();
2728

29+
SANITIZER_INTERFACE_ATTRIBUTE
30+
void __hwasan_library_loaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
31+
ElfW(Half) phnum);
32+
33+
SANITIZER_INTERFACE_ATTRIBUTE
34+
void __hwasan_library_unloaded(ElfW(Addr) base, const ElfW(Phdr) * phdr,
35+
ElfW(Half) phnum);
36+
2837
using __sanitizer::uptr;
2938
using __sanitizer::sptr;
3039
using __sanitizer::uu64;

compiler-rt/lib/hwasan/hwasan_report.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,31 @@ void PrintAddressDescription(
278278
Printf("%s", d.Default());
279279
GetStackTraceFromId(chunk.GetAllocStackId()).Print();
280280
num_descriptions_printed++;
281+
} else {
282+
// Check whether the address points into a loaded library. If so, this is
283+
// most likely a global variable.
284+
const char *module_name;
285+
uptr module_address;
286+
Symbolizer *sym = Symbolizer::GetOrInit();
287+
if (sym->GetModuleNameAndOffsetForPC(mem, &module_name,
288+
&module_address)) {
289+
DataInfo info;
290+
if (sym->SymbolizeData(mem, &info) && info.start) {
291+
Printf(
292+
"%p is located %zd bytes to the %s of %zd-byte global variable "
293+
"%s [%p,%p) in %s\n",
294+
untagged_addr,
295+
candidate == left ? untagged_addr - (info.start + info.size)
296+
: info.start - untagged_addr,
297+
candidate == left ? "right" : "left", info.size, info.name,
298+
info.start, info.start + info.size, module_name);
299+
} else {
300+
Printf("%p is located to the %s of a global variable in (%s+0x%x)\n",
301+
untagged_addr, candidate == left ? "right" : "left",
302+
module_name, module_address);
303+
}
304+
num_descriptions_printed++;
305+
}
281306
}
282307
}
283308

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// RUN: %clang_hwasan %s -o %t
2+
// RUN: %run %t 0
3+
// RUN: not %run %t 1 2>&1 | FileCheck --check-prefixes=CHECK,RSYM %s
4+
// RUN: not %env_hwasan_opts=symbolize=0 %run %t 1 2>&1 | FileCheck --check-prefixes=CHECK,RNOSYM %s
5+
// RUN: not %run %t -1 2>&1 | FileCheck --check-prefixes=CHECK,LSYM %s
6+
// RUN: not %env_hwasan_opts=symbolize=0 %run %t -1 2>&1 | FileCheck --check-prefixes=CHECK,LNOSYM %s
7+
8+
int x = 1;
9+
10+
int main(int argc, char **argv) {
11+
// RSYM: is located 0 bytes to the right of 4-byte global variable x {{.*}} in {{.*}}global.c.tmp
12+
// RNOSYM: is located to the right of a global variable in ({{.*}}global.c.tmp+{{.*}})
13+
// LSYM: is located 4 bytes to the left of 4-byte global variable x {{.*}} in {{.*}}global.c.tmp
14+
// LNOSYM: is located to the left of a global variable in ({{.*}}global.c.tmp+{{.*}})
15+
// CHECK-NOT: can not describe
16+
(&x)[atoi(argv[1])] = 1;
17+
}

compiler-rt/test/hwasan/lit.cfg.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
1111
# Setup default compiler flags used with -fsanitize=memory option.
1212
clang_cflags = [config.target_cflags] + config.debug_info_flags
1313
clang_cxxflags = config.cxx_mode_flags + clang_cflags
14-
clang_hwasan_cflags = ["-fsanitize=hwaddress"] + clang_cflags
14+
clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-globals"] + clang_cflags
15+
if config.target_arch == 'x86_64':
16+
# This does basically the same thing as tagged-globals on aarch64. Because
17+
# the x86_64 implementation is for testing purposes only there is no
18+
# equivalent target feature implemented on x86_64.
19+
clang_hwasan_cflags += ["-mcmodel=large"]
1520
clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags
1621

1722
def build_invocation(compile_flags):

llvm/include/llvm/BinaryFormat/ELF.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,6 +1416,11 @@ enum : unsigned {
14161416
NT_SIGINFO = 0x53494749,
14171417
};
14181418

1419+
// LLVM-specific notes.
1420+
enum {
1421+
NT_LLVM_HWASAN_GLOBALS = 3,
1422+
};
1423+
14191424
// GNU note types
14201425
enum {
14211426
NT_GNU_ABI_TAG = 1,

0 commit comments

Comments
 (0)