Skip to content

Commit ea991a1

Browse files
authored
[hwasan] Add fixed_shadow_base flag (#73980)
When set to non-zero, the HWASan runtime will map the shadow base at the specified constant address. This is particularly useful in conjunction with the existing compiler option 'hwasan-mapping-offset', which bakes a hardcoded constant address into the instrumentation. --------- Co-authored-by: Thurston Dang <[email protected]>
1 parent fb2b907 commit ea991a1

File tree

3 files changed

+89
-2
lines changed

3 files changed

+89
-2
lines changed

compiler-rt/lib/hwasan/hwasan_flags.inc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,10 @@ HWASAN_FLAG(bool, malloc_bisect_dump, false,
8484
// are untagged before the call.
8585
HWASAN_FLAG(bool, fail_without_syscall_abi, true,
8686
"Exit if fail to request relaxed syscall ABI.")
87+
88+
HWASAN_FLAG(
89+
uptr, fixed_shadow_base, -1,
90+
"If not -1, HWASan will attempt to allocate the shadow at this address, "
91+
"instead of choosing one dynamically."
92+
"Tip: this can be combined with the compiler option, "
93+
"-hwasan-mapping-offset, to optimize the instrumentation.")

compiler-rt/lib/hwasan/hwasan_linux.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,12 @@ static uptr GetHighMemEnd() {
106106
}
107107

108108
static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
109-
__hwasan_shadow_memory_dynamic_address =
110-
FindDynamicShadowStart(shadow_size_bytes);
109+
if (flags()->fixed_shadow_base != (uptr)-1) {
110+
__hwasan_shadow_memory_dynamic_address = flags()->fixed_shadow_base;
111+
} else {
112+
__hwasan_shadow_memory_dynamic_address =
113+
FindDynamicShadowStart(shadow_size_bytes);
114+
}
111115
}
112116

113117
static void MaybeDieIfNoTaggingAbi(const char *message) {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Test fixed shadow base functionality.
2+
//
3+
// Default compiler instrumentation works with any shadow base (dynamic or fixed).
4+
// RUN: %clang_hwasan %s -o %t && %run %t
5+
// RUN: %clang_hwasan %s -o %t && HWASAN_OPTIONS=fixed_shadow_base=263878495698944 %run %t
6+
// RUN: %clang_hwasan %s -o %t && HWASAN_OPTIONS=fixed_shadow_base=4398046511104 %run %t
7+
//
8+
// If -hwasan-mapping-offset is set, then the fixed_shadow_base needs to match.
9+
// RUN: %clang_hwasan %s -mllvm -hwasan-mapping-offset=263878495698944 -o %t && HWASAN_OPTIONS=fixed_shadow_base=263878495698944 %run %t
10+
// RUN: %clang_hwasan %s -mllvm -hwasan-mapping-offset=4398046511104 -o %t && HWASAN_OPTIONS=fixed_shadow_base=4398046511104 %run %t
11+
// RUN: %clang_hwasan %s -mllvm -hwasan-mapping-offset=263878495698944 -o %t && HWASAN_OPTIONS=fixed_shadow_base=4398046511104 not %run %t
12+
// RUN: %clang_hwasan %s -mllvm -hwasan-mapping-offset=4398046511104 -o %t && HWASAN_OPTIONS=fixed_shadow_base=263878495698944 not %run %t
13+
//
14+
// Note: if fixed_shadow_base is not set, compiler-rt will dynamically choose a
15+
// shadow base, which has a tiny but non-zero probability of matching the
16+
// compiler instrumentation. To avoid test flake, we do not test this case.
17+
//
18+
// Assume 48-bit VMA
19+
// REQUIRES: aarch64-target-arch
20+
//
21+
// REQUIRES: Clang
22+
//
23+
// UNSUPPORTED: android
24+
25+
#include <assert.h>
26+
#include <sanitizer/allocator_interface.h>
27+
#include <sanitizer/hwasan_interface.h>
28+
#include <stdio.h>
29+
#include <stdlib.h>
30+
#include <sys/mman.h>
31+
32+
int main() {
33+
__hwasan_enable_allocator_tagging();
34+
35+
// We test that the compiler instrumentation is able to access shadow memory
36+
// for many different addresses. If we only test a small number of addresses,
37+
// it might work by chance even if the shadow base does not match between the
38+
// compiler instrumentation and compiler-rt.
39+
void **mmaps[256];
40+
// 48-bit VMA
41+
for (int i = 0; i < 256; i++) {
42+
unsigned long long addr = (i * (1ULL << 40));
43+
44+
void *p = mmap((void *)addr, 4096, PROT_READ | PROT_WRITE,
45+
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
46+
// We don't use MAP_FIXED, to avoid overwriting critical memory.
47+
// However, if we don't get allocated the requested address, it
48+
// isn't a useful test.
49+
if ((unsigned long long)p != addr) {
50+
munmap(p, 4096);
51+
mmaps[i] = MAP_FAILED;
52+
} else {
53+
mmaps[i] = p;
54+
}
55+
}
56+
57+
int failures = 0;
58+
for (int i = 0; i < 256; i++) {
59+
if (mmaps[i] == MAP_FAILED) {
60+
failures++;
61+
} else {
62+
printf("%d %p\n", i, mmaps[i]);
63+
munmap(mmaps[i], 4096);
64+
}
65+
}
66+
67+
// We expect roughly 17 failures:
68+
// - the page at address zero
69+
// - 16 failures because the shadow memory takes up 1/16th of the address space
70+
// We could also get unlucky e.g., if libraries or binaries are loaded into the
71+
// exact addresses where we tried to map.
72+
// To avoid test flake, we allow some margin of error.
73+
printf("Failed: %d\n", failures);
74+
assert(failures < 48);
75+
return 0;
76+
}

0 commit comments

Comments
 (0)