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 @@ -62,6 +62,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,7 +28,7 @@ static Allocator allocator; static AllocatorCache fallback_allocator_cache; static SpinMutex fallback_mutex; -static atomic_uint8_t hwasan_allocator_tagging_enabled; +atomic_uint8_t hwasan_allocator_tagging_enabled; static constexpr tag_t kFallbackAllocTag = 0xBB & kTagMask; static constexpr tag_t kFallbackFreeTag = 0xBC; 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,78 @@ 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) + +// The main purpose of the mmap interceptor is to prevent the user from +// allocating on top of shadow pages. +// +// For compatibility reasons, it does not use tagging, except for +// MAP_FIXED. +template +static void *mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T length, + int prot, int flags, int fd, OFF64_T offset) { + // For MAP_FIXED, we use the requested tag (implicit in the address + // parameter) because the user might be trying, for example, to map a + // region adjacent to an existing tagged region (*) and expecting to be + // able to access both regions using either pointer. + // + // For non-MAP_FIXED, the user can't expect anything about the pointer, so + // we choose to always use the zero tag. This is convenient because the + // shadow pages are zero initialized. + // + // (*) e.g., 1) secondary allocator returns tagged region + // 2) user mmaps (MAP_FIXED) next to the tagged region + tag_t tag = 0; + if(addr) { + if (flags & map_fixed) tag = GetTagFromPointer(reinterpret_cast(addr)); + + 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, else fail. + 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 (tag != 0) { + if(atomic_load_relaxed(&hwasan_allocator_tagging_enabled)) { + res = (void *)TagMemory((uptr)res, rounded_length, tag); + } else { + // User requested MAP_FIXED with a pointer that was tagged non-zero, but + // somehow tagging is not enabled. This shouldn't happen. + return (void *)-1; + } + } + + 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" @@ -160,6 +232,27 @@ int pthread_attr_getdetachstate(void *attr, int *v); } +// Call munmap with the untagged pointer, and clear the shadow memory +// +// N.B> We don't care if an attacker unmaps shadow memory. Since mmap and the +// secondary allocator will not allow users to map inside the shadow memory +// region, at most that will allow the attacker to crash the program. +INTERCEPTOR(int, munmap, void *addr, size_t length) { + int tag = 0; + if(addr) { + tag = GetTagFromPointer(reinterpret_cast(addr)); + addr = UntagPtr(addr); + } + + int result = REAL(munmap)(addr, length); + if (addr && (result == 0) && (tag != 0)) { + // This might get reused as a thread stack. + TagMemory(reinterpret_cast(addr), length, 0); + } + + return result; +} + INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*callback)(void *), void *param) { EnsureMainThreadIDIsCorrect(); @@ -414,12 +507,14 @@ static int inited = 0; CHECK_EQ(inited, 0); - (void)(InitializeCommonInterceptors); + InitializeCommonInterceptors(); + (void)(read_iovec); (void)(write_iovec); # if HWASAN_WITH_INTERCEPTORS # if defined(__linux__) + INTERCEPT_FUNCTION(munmap); INTERCEPT_FUNCTION(__libc_longjmp); INTERCEPT_FUNCTION(longjmp); INTERCEPT_FUNCTION(siglongjmp); 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 @@ -728,8 +728,8 @@ #undef SANITIZER_INTERCEPT_GETLOADAVG #define SANITIZER_INTERCEPT_GETLOADAVG 0 -#undef SANITIZER_INTERCEPT_MMAP -#define SANITIZER_INTERCEPT_MMAP 0 +// #undef SANITIZER_INTERCEPT_MMAP +// #define SANITIZER_INTERCEPT_MMAP 0 #undef SANITIZER_INTERCEPT_MMAP64 #define SANITIZER_INTERCEPT_MMAP64 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