From 294ed9bff3fd0d23d1b97c8a4be07b9168e69f69 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Thu, 30 Nov 2023 20:14:00 +0000 Subject: [PATCH 1/6] [hwasan] Add fixed_shadow_base flag 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. --- compiler-rt/lib/hwasan/hwasan_flags.inc | 7 +++++++ compiler-rt/lib/hwasan/hwasan_linux.cpp | 10 ++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler-rt/lib/hwasan/hwasan_flags.inc b/compiler-rt/lib/hwasan/hwasan_flags.inc index 978fa46b705cb9..bb224fbb8eaa8b 100644 --- a/compiler-rt/lib/hwasan/hwasan_flags.inc +++ b/compiler-rt/lib/hwasan/hwasan_flags.inc @@ -84,3 +84,10 @@ HWASAN_FLAG(bool, malloc_bisect_dump, false, // are untagged before the call. HWASAN_FLAG(bool, fail_without_syscall_abi, true, "Exit if fail to request relaxed syscall ABI.") + +HWASAN_FLAG( + uptr, fixed_shadow_base, 0, + "If non-zero, HWASan will attempt to allocate the shadow at this address, " + "instead of choosing one dynamically." + "Tip: this can be combined with the compiler option, " + "-hwasan-mapping-offset, to optimize the instrumentation.") diff --git a/compiler-rt/lib/hwasan/hwasan_linux.cpp b/compiler-rt/lib/hwasan/hwasan_linux.cpp index 81226da976d116..e7cf36ef3161cb 100644 --- a/compiler-rt/lib/hwasan/hwasan_linux.cpp +++ b/compiler-rt/lib/hwasan/hwasan_linux.cpp @@ -106,8 +106,14 @@ static uptr GetHighMemEnd() { } static void InitializeShadowBaseAddress(uptr shadow_size_bytes) { - __hwasan_shadow_memory_dynamic_address = - FindDynamicShadowStart(shadow_size_bytes); + // NULL is generally address zero, so it is not a valid location for the + // shadow. + if (flags()->fixed_shadow_base != 0) { + __hwasan_shadow_memory_dynamic_address = flags()->fixed_shadow_base; + } else { + __hwasan_shadow_memory_dynamic_address = + FindDynamicShadowStart(shadow_size_bytes); + } } static void MaybeDieIfNoTaggingAbi(const char *message) { From 48eb300c1a0ee2233f6c2e2e0a3899f591befa94 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Thu, 30 Nov 2023 20:56:08 +0000 Subject: [PATCH 2/6] Change "non-fixed shadow" magic value from 0 to -1, per offline discussion with Vitaly --- compiler-rt/lib/hwasan/hwasan_flags.inc | 4 ++-- compiler-rt/lib/hwasan/hwasan_linux.cpp | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler-rt/lib/hwasan/hwasan_flags.inc b/compiler-rt/lib/hwasan/hwasan_flags.inc index bb224fbb8eaa8b..058a0457b9e7f6 100644 --- a/compiler-rt/lib/hwasan/hwasan_flags.inc +++ b/compiler-rt/lib/hwasan/hwasan_flags.inc @@ -86,8 +86,8 @@ HWASAN_FLAG(bool, fail_without_syscall_abi, true, "Exit if fail to request relaxed syscall ABI.") HWASAN_FLAG( - uptr, fixed_shadow_base, 0, - "If non-zero, HWASan will attempt to allocate the shadow at this address, " + uptr, fixed_shadow_base, -1, + "If not -1, HWASan will attempt to allocate the shadow at this address, " "instead of choosing one dynamically." "Tip: this can be combined with the compiler option, " "-hwasan-mapping-offset, to optimize the instrumentation.") diff --git a/compiler-rt/lib/hwasan/hwasan_linux.cpp b/compiler-rt/lib/hwasan/hwasan_linux.cpp index e7cf36ef3161cb..f01fa427641347 100644 --- a/compiler-rt/lib/hwasan/hwasan_linux.cpp +++ b/compiler-rt/lib/hwasan/hwasan_linux.cpp @@ -106,9 +106,7 @@ static uptr GetHighMemEnd() { } static void InitializeShadowBaseAddress(uptr shadow_size_bytes) { - // NULL is generally address zero, so it is not a valid location for the - // shadow. - if (flags()->fixed_shadow_base != 0) { + if (flags()->fixed_shadow_base != (uptr)-1) { __hwasan_shadow_memory_dynamic_address = flags()->fixed_shadow_base; } else { __hwasan_shadow_memory_dynamic_address = From 0896aa86ba9de0f1b681c3e4565cfe597294a0a7 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Tue, 5 Dec 2023 22:25:11 +0000 Subject: [PATCH 3/6] Add test --- .../hwasan/TestCases/Linux/fixed-shadow.c | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c diff --git a/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c b/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c new file mode 100644 index 00000000000000..9ff57f27de4e17 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c @@ -0,0 +1,62 @@ +// Test fixed shadow base functionality. +// +// Default compiler instrumentation works with any shadow base (dynamic or fixed). +// RUN: %clang_hwasan %s -o %t && %run %t +// RUN: %clang_hwasan %s -o %t && HWASAN_OPTIONS=fixed_shadow_base=263878495698944 %run %t +// RUN: %clang_hwasan %s -o %t && HWASAN_OPTIONS=fixed_shadow_base=4398046511104 %run %t +// +// If -hwasan-mapping-offset is set, then the fixed_shadow_base needs to match. +// RUN: %clang_hwasan %s -mllvm -hwasan-mapping-offset=263878495698944 -o %t && HWASAN_OPTIONS=fixed_shadow_base=263878495698944 %run %t +// RUN: %clang_hwasan %s -mllvm -hwasan-mapping-offset=4398046511104 -o %t && HWASAN_OPTIONS=fixed_shadow_base=4398046511104 %run %t +// RUN: %clang_hwasan %s -mllvm -hwasan-mapping-offset=263878495698944 -o %t && HWASAN_OPTIONS=fixed_shadow_base=4398046511104 not %run %t +// RUN: %clang_hwasan %s -mllvm -hwasan-mapping-offset=4398046511104 -o %t && HWASAN_OPTIONS=fixed_shadow_base=263878495698944 not %run %t +// +// Note: if fixed_shadow_base is not set, compiler-rt will dynamically choose a +// shadow base, which has a tiny but non-zero probability of matching the +// compiler instrumentation. To avoid test flake, we do not test this case. +// +// Assume 48-bit VMA +// REQUIRES: aarch64-target-arch +// +// UNSUPPORTED: android + +#include +#include +#include +#include +#include +#include + +int main() { + __hwasan_enable_allocator_tagging(); + + void** mmaps [256]; + // 48-bit VMA + for (int i = 0; i < 256; i++) { + unsigned long long addr = (i * (1ULL << 40)); + + void *p = mmap ((void*)addr, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + mmaps [i] = p; + } + + int failures = 0; + for (int i = 0; i < 256; i++) { + if (mmaps [i] == MAP_FAILED) { + failures++; + } else { + printf ("%d %p\n", i, mmaps [i]); + munmap (mmaps [i], 4096); + } + } + + // We expect roughly 17 failures: + // - the page at address zero + // - 16 failures because the shadow memory takes up 1/16th of the address space + // We could also get unlucky e.g., if libraries or binaries are loaded into the + // exact addresses where we tried to map. + // To avoid test flake, we allow some margin of error. + printf ("Failed: %d\n", failures); + assert (failures < 32); + return 0; +} From 2c9a4b43a927b6d20d9eacd3888103d80dfff382 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Tue, 5 Dec 2023 22:26:39 +0000 Subject: [PATCH 4/6] clang-format test --- .../hwasan/TestCases/Linux/fixed-shadow.c | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c b/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c index 9ff57f27de4e17..154ef5cd44e6c0 100644 --- a/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c +++ b/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c @@ -13,41 +13,41 @@ // // Note: if fixed_shadow_base is not set, compiler-rt will dynamically choose a // shadow base, which has a tiny but non-zero probability of matching the -// compiler instrumentation. To avoid test flake, we do not test this case. +// compiler instrumentation. To avoid test flake, we do not test this case. // // Assume 48-bit VMA // REQUIRES: aarch64-target-arch // // UNSUPPORTED: android -#include -#include -#include -#include -#include +#include #include +#include +#include +#include +#include -int main() { +int main() { __hwasan_enable_allocator_tagging(); - void** mmaps [256]; + void **mmaps[256]; // 48-bit VMA - for (int i = 0; i < 256; i++) { - unsigned long long addr = (i * (1ULL << 40)); + for (int i = 0; i < 256; i++) { + unsigned long long addr = (i * (1ULL << 40)); - void *p = mmap ((void*)addr, 4096, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - mmaps [i] = p; + void *p = mmap((void *)addr, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + mmaps[i] = p; } int failures = 0; - for (int i = 0; i < 256; i++) { - if (mmaps [i] == MAP_FAILED) { - failures++; - } else { - printf ("%d %p\n", i, mmaps [i]); - munmap (mmaps [i], 4096); - } + for (int i = 0; i < 256; i++) { + if (mmaps[i] == MAP_FAILED) { + failures++; + } else { + printf("%d %p\n", i, mmaps[i]); + munmap(mmaps[i], 4096); + } } // We expect roughly 17 failures: @@ -56,7 +56,7 @@ int main() { // We could also get unlucky e.g., if libraries or binaries are loaded into the // exact addresses where we tried to map. // To avoid test flake, we allow some margin of error. - printf ("Failed: %d\n", failures); - assert (failures < 32); + printf("Failed: %d\n", failures); + assert(failures < 32); return 0; } From fc08ffd538af4f20ea79b842ea695726389ba739 Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Wed, 6 Dec 2023 21:22:27 +0000 Subject: [PATCH 5/6] Remove MAP_FIXED from mmap in test --- .../test/hwasan/TestCases/Linux/fixed-shadow.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c b/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c index 154ef5cd44e6c0..eee4a492213209 100644 --- a/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c +++ b/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c @@ -30,14 +30,26 @@ int main() { __hwasan_enable_allocator_tagging(); + // We test that the compiler instrumentation is able to access shadow memory + // for many different addresses. If we only test a small number of addresses, + // it might work by chance even if the shadow base does not match between the + // compiler instrumentation and compiler-rt. void **mmaps[256]; // 48-bit VMA for (int i = 0; i < 256; i++) { unsigned long long addr = (i * (1ULL << 40)); void *p = mmap((void *)addr, 4096, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - mmaps[i] = p; + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + // We don't use MAP_FIXED, to avoid overwriting critical memory. + // However, if we don't get allocated the requested address, it + // isn't a useful test. + if ((unsigned long long)p != addr) { + munmap(p, 4096); + mmaps[i] = MAP_FAILED; + } else { + mmaps[i] = p; + } } int failures = 0; @@ -57,6 +69,6 @@ int main() { // exact addresses where we tried to map. // To avoid test flake, we allow some margin of error. printf("Failed: %d\n", failures); - assert(failures < 32); + assert(failures < 48); return 0; } From 7005753b2769d06160178d028f1d3b443e1dca9e Mon Sep 17 00:00:00 2001 From: Thurston Dang Date: Thu, 7 Dec 2023 17:43:55 +0000 Subject: [PATCH 6/6] Add 'REQUIRES: clang' to testcase --- compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c b/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c index eee4a492213209..4ff1d3e64c1d0e 100644 --- a/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c +++ b/compiler-rt/test/hwasan/TestCases/Linux/fixed-shadow.c @@ -18,6 +18,8 @@ // Assume 48-bit VMA // REQUIRES: aarch64-target-arch // +// REQUIRES: Clang +// // UNSUPPORTED: android #include