diff --git a/compiler-rt/lib/hwasan/hwasan_allocator.h b/compiler-rt/lib/hwasan/hwasan_allocator.h --- a/compiler-rt/lib/hwasan/hwasan_allocator.h +++ b/compiler-rt/lib/hwasan/hwasan_allocator.h @@ -29,6 +29,9 @@ # error Unsupported platform #endif +static constexpr tag_t kFallbackAllocTag = 0xBB & kTagMask; +static constexpr tag_t kFallbackFreeTag = 0xBC; + namespace __hwasan { struct Metadata { @@ -62,6 +65,8 @@ } }; +extern atomic_uint8_t hwasan_allocator_tagging_enabled; + static const uptr kMaxAllowedMallocSize = 1UL << 40; // 1T struct AP64 { diff --git a/compiler-rt/lib/hwasan/hwasan_allocator.cpp b/compiler-rt/lib/hwasan/hwasan_allocator.cpp --- a/compiler-rt/lib/hwasan/hwasan_allocator.cpp +++ b/compiler-rt/lib/hwasan/hwasan_allocator.cpp @@ -28,10 +28,7 @@ static Allocator allocator; static AllocatorCache fallback_allocator_cache; static SpinMutex fallback_mutex; -static atomic_uint8_t hwasan_allocator_tagging_enabled; - -static constexpr tag_t kFallbackAllocTag = 0xBB & kTagMask; -static constexpr tag_t kFallbackFreeTag = 0xBC; +atomic_uint8_t hwasan_allocator_tagging_enabled; enum { // Either just allocated by underlying allocator, but AsanChunk is not yet diff --git a/compiler-rt/lib/hwasan/hwasan_interceptors.h b/compiler-rt/lib/hwasan/hwasan_interceptors.h new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/hwasan/hwasan_interceptors.h @@ -0,0 +1,40 @@ +#ifndef HWASAN_INTERCEPTORS_H +#define HWASAN_INTERCEPTORS_H + +#include "sanitizer_common/sanitizer_platform.h" + +// Copied from ASan + +// There is no general interception at all on Fuchsia. +// Only the functions in asan_interceptors_memintrinsics.h are +// really defined to replace libc functions. +#if !SANITIZER_FUCHSIA + +# if !SANITIZER_APPLE +# define HWASAN_INTERCEPT_FUNC(name) \ + do { \ + if (!INTERCEPT_FUNCTION(name)) \ + VReport(1, "HWAddressSanitizer: failed to intercept '%s'\n", #name); \ + } while (0) +# define HWASAN_INTERCEPT_FUNC_VER(name, ver) \ + do { \ + if (!INTERCEPT_FUNCTION_VER(name, ver)) \ + VReport(1, "HWAddressSanitizer: failed to intercept '%s@@%s'\n", \ + #name, ver); \ + } while (0) +# define HWASAN_INTERCEPT_FUNC_VER_UNVERSIONED_FALLBACK(name, ver) \ + do { \ + if (!INTERCEPT_FUNCTION_VER(name, ver) && !INTERCEPT_FUNCTION(name)) \ + VReport( \ + 1, "HWAddressSanitizer: failed to intercept '%s@@%s' or '%s'\n", \ + #name, ver, #name); \ + } while (0) + +# else +// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. +# define HWASAN_INTERCEPT_FUNC(name) +# endif // SANITIZER_APPLE + +#endif // !SANITIZER_FUCHSIA + +#endif // HWASAN_INTERCEPTORS_H diff --git a/compiler-rt/lib/hwasan/hwasan_interceptors.cpp b/compiler-rt/lib/hwasan/hwasan_interceptors.cpp --- a/compiler-rt/lib/hwasan/hwasan_interceptors.cpp +++ b/compiler-rt/lib/hwasan/hwasan_interceptors.cpp @@ -15,11 +15,14 @@ //===----------------------------------------------------------------------===// #include "hwasan.h" +#include "hwasan_allocator.h" #include "hwasan_checks.h" +#include "hwasan_interceptors.h" #include "hwasan_platform_interceptors.h" #include "hwasan_thread.h" #include "hwasan_thread_list.h" #include "interception/interception.h" +#include "sanitizer_common/sanitizer_errno.h" #include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_stackdepot.h" @@ -133,9 +136,56 @@ do { \ } while (false) -#define COMMON_INTERCEPT_FUNCTION(name) \ - do { \ - (void)(name); \ +#define COMMON_INTERCEPT_FUNCTION(name) HWASAN_INTERCEPT_FUNC(name) + +#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!hwasan_inited) + +template +static void *mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T length, + int prot, int flags, int fd, OFF64_T offset) { + if(addr) { + // A common idiom is to use a (tagged) pointer previously returned by + // mmap, as the parameter to another mmap call. + addr = UntagPtr(addr); + } + SIZE_T rounded_length = RoundUpTo(length, GetPageSize()); + void *end_addr = (char *)addr + (rounded_length - 1); + if (addr && (!MemIsApp(reinterpret_cast(addr)) || !MemIsApp(reinterpret_cast(end_addr)))) { + // User requested an address that is incompatible with HWASan's + // memory layout. Use a different address if allowed. + if (flags & map_fixed) { + errno = errno_EINVAL; + return (void *)-1; + } else { + addr = nullptr; + } + } + void *res = real_mmap(addr, length, prot, flags, fd, offset); + if (res != (void *)-1) { + void *end_res = (char *)res + (rounded_length - 1); + if (!MemIsApp(reinterpret_cast(res)) || !MemIsApp(reinterpret_cast(end_res))) { + // Application has attempted to map more memory than is supported by + // HWASan. Act as if we ran out of memory. + internal_munmap(res, length); + errno = errno_ENOMEM; + return (void *)-1; + } + } + + // Tagging + if(atomic_load_relaxed(&hwasan_allocator_tagging_enabled)) { + Thread *t = GetCurrentThread(); + tag_t tag = t ? t->GenerateRandomTag() : kFallbackAllocTag; + res = (void *)TagMemory((uptr)res, rounded_length, tag); + } + return res; +} + +#define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags, fd, \ + offset) \ + do { \ + (void)(ctx); \ + return mmap_interceptor(REAL(mmap), addr, sz, prot, flags, fd, off); \ } while (false) #include "sanitizer_common/sanitizer_common_interceptors.inc" @@ -414,7 +464,8 @@ static int inited = 0; CHECK_EQ(inited, 0); - (void)(InitializeCommonInterceptors); + InitializeCommonInterceptors(); + (void)(read_iovec); (void)(write_iovec); diff --git a/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h b/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h --- a/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h +++ b/compiler-rt/lib/hwasan/hwasan_platform_interceptors.h @@ -3,6 +3,18 @@ #include "sanitizer_common/sanitizer_platform_interceptors.h" +// This file cancels out most of the sanitizer_common interception, thus +// allowing HWASan to selectively reuse some of the interceptors. +// +// To re-enable sanitizer_common's interception of a function, comment out +// the corresponding '#undef SANITIZER_INTERCEPT_fn' and +// '#define SANITIZER_INTERCEPT_fn 0': +// - We prefer to comment out rather than delete the lines, to show that +// it is deliberate, rather than an accidental omission. +// - We do not use '#define SANITIZE_INTERCEPT_fn 1', because +// interception is usually conditional (e.g., based on SI_POSIX); we let +// the condition in sanitizers_platform_interceptors.h take effect. + // cat sanitizer_platform_interceptors.h | grep '^#define SANITIZER_INTERCEPT' | cut -d ' ' -f 1,2 | sed -r 's/^#define/#undef/' #undef SANITIZER_INTERCEPT_STRLEN #undef SANITIZER_INTERCEPT_STRNLEN @@ -241,7 +253,7 @@ #undef SANITIZER_INTERCEPT_UTMP #undef SANITIZER_INTERCEPT_UTMPX #undef SANITIZER_INTERCEPT_GETLOADAVG -#undef SANITIZER_INTERCEPT_MMAP +// #undef SANITIZER_INTERCEPT_MMAP #undef SANITIZER_INTERCEPT_MMAP64 #undef SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO #undef SANITIZER_INTERCEPT_MEMALIGN @@ -570,7 +582,7 @@ #define SANITIZER_INTERCEPT_UTMP 0 #define SANITIZER_INTERCEPT_UTMPX 0 #define SANITIZER_INTERCEPT_GETLOADAVG 0 -#define SANITIZER_INTERCEPT_MMAP 0 +// #define SANITIZER_INTERCEPT_MMAP 0 #define SANITIZER_INTERCEPT_MMAP64 0 #define SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO 0 #define SANITIZER_INTERCEPT_MEMALIGN 0 diff --git a/compiler-rt/test/hwasan/TestCases/mmap-test.c b/compiler-rt/test/hwasan/TestCases/mmap-test.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/mmap-test.c @@ -0,0 +1,56 @@ +// Check that mmap returns tagged addresses. +// RUN: %clang_hwasan %s -o %t +// RUN: %run %t + +// REQUIRES: pointer-tagging + +#include +#include +#include +#include +#include +#include + +#define NUM_TRIALS 1024 +#define PAGE_SIZE 4096 + +int main() { + __hwasan_enable_allocator_tagging(); + + long *p = (long*) mmap (NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + assert (p != MAP_FAILED); + printf ("%p\n", p); + + p[0] = MAGIC; + + munmap (p, PAGE_SIZE); + + // We know that p is a valid address for mmap. We try to map again at the same address, + // and check that we (almost always) get a different address. + int sameAddresses = 0; + for (int i = 0; i < NUM_TRIALS; i++) { + long *q = (long*) mmap (p, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + assert (q != MAP_FAILED); + + printf ("%p\n", q); + + if (p == q) { + sameAddresses ++; + } + + munmap (q, PAGE_SIZE); + } + + printf ("Same address: %d\n", sameAddresses); + + // We have at least 16 tags, which means the probability p of randomly getting the + // same tagged address is <= 1/16. The number of trials n is 1024. + // The expected number of times we get the same tagged address is np = 64 (by the + // binomial distribution), and the standard deviation is sqrt(npq) = ~8. That means + // the chance of getting the same address more than 500 times is practically zero. + if (sameAddresses > (NUM_TRIALS * 0.5)) { + return 1; + } + + return 0; +} diff --git a/compiler-rt/test/sanitizer_common/TestCases/Posix/mmap_write_exec.cpp b/compiler-rt/test/sanitizer_common/TestCases/Posix/mmap_write_exec.cpp --- a/compiler-rt/test/sanitizer_common/TestCases/Posix/mmap_write_exec.cpp +++ b/compiler-rt/test/sanitizer_common/TestCases/Posix/mmap_write_exec.cpp @@ -7,9 +7,6 @@ // TODO: Fix option on Android, it hangs there for unknown reasons. // XFAIL: android -// FIXME: Implement. -// XFAIL: hwasan - #if defined(__APPLE__) #include #endif