[HWASan] Use page aliasing on x86_64.

Userspace page aliasing allows us to use middle pointer bits for tags
without untagging them before syscalls or accesses.  This should enable
easier experimentation with HWASan on x86_64 platforms.

Currently stack, global, and secondary heap tagging are unsupported.
Only primary heap allocations get tagged.

Note that aliasing mode will not work properly in the presence of
fork(), since heap memory will be shared between the parent and child
processes.  This mode is non-ideal; we expect Intel LAM to enable full
HWASan support on x86_64 in the future.

Reviewed By: vitalybuka, eugenis

Differential Revision: https://reviews.llvm.org/D98875
This commit is contained in:
Matt Morehouse 2021-03-24 10:04:37 -07:00
parent 5da55bfc18
commit 63f73c3eb9
33 changed files with 157 additions and 179 deletions

View File

@ -14,11 +14,12 @@
#ifndef HWASAN_H
#define HWASAN_H
#include "hwasan_flags.h"
#include "hwasan_interface_internal.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "hwasan_interface_internal.h"
#include "hwasan_flags.h"
#include "ubsan/ubsan_platform.h"
#ifndef HWASAN_CONTAINS_UBSAN
@ -35,15 +36,30 @@
typedef u8 tag_t;
#if defined(__x86_64__)
// Tags are done in middle bits using userspace aliasing.
constexpr unsigned kAddressTagShift = 39;
constexpr unsigned kTagBits = 3;
// The alias region is placed next to the shadow so the upper bits of all
// taggable addresses matches the upper bits of the shadow base. This shift
// value determines which upper bits must match. It has a floor of 44 since the
// shadow is always 8TB.
// TODO(morehouse): In alias mode we can shrink the shadow and use a
// simpler/faster shadow calculation.
constexpr unsigned kTaggableRegionCheckShift =
__sanitizer::Max(kAddressTagShift + kTagBits + 1U, 44U);
#else
// TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address
// translation and can be used to store a tag.
constexpr unsigned kAddressTagShift = 56;
constexpr unsigned kTagBits = 8;
#endif // defined(__x86_64__)
// Mask for extracting tag bits from the lower 8 bits.
constexpr uptr kTagMask = (1UL << kTagBits) - 1;
// Masks for extracting and removing tags from full pointers.
// Mask for extracting tag bits from full pointers.
constexpr uptr kAddressTagMask = kTagMask << kAddressTagShift;
// Minimal alignment of the shadow base address. Determines the space available

View File

@ -84,7 +84,8 @@ void HwasanAllocatorInit() {
atomic_store_relaxed(&hwasan_allocator_tagging_enabled,
!flags()->disable_allocator_tagging);
SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
allocator.Init(common_flags()->allocator_release_to_os_interval_ms,
kAliasRegionStart);
for (uptr i = 0; i < sizeof(tail_magic); i++)
tail_magic[i] = GetCurrentThread()->GenerateRandomTag();
}
@ -374,7 +375,7 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
// OOM error is already taken care of by HwasanAllocate.
return errno_ENOMEM;
CHECK(IsAligned((uptr)ptr, alignment));
*(void **)UntagPtr(memptr) = ptr;
*memptr = ptr;
return 0;
}

View File

@ -13,6 +13,8 @@
#ifndef HWASAN_ALLOCATOR_H
#define HWASAN_ALLOCATOR_H
#include "hwasan.h"
#include "hwasan_interface_internal.h"
#include "hwasan_poisoning.h"
#include "sanitizer_common/sanitizer_allocator.h"
#include "sanitizer_common/sanitizer_allocator_checks.h"
@ -55,7 +57,12 @@ static const uptr kMaxAllowedMallocSize = 1UL << 40; // 1T
struct AP64 {
static const uptr kSpaceBeg = ~0ULL;
#if defined(__x86_64__)
static const uptr kSpaceSize = 1ULL << kAddressTagShift;
#else
static const uptr kSpaceSize = 0x2000000000ULL;
#endif
static const uptr kMetadataSize = sizeof(Metadata);
typedef __sanitizer::VeryDenseSizeClassMap SizeClassMap;
using AddressSpaceView = LocalAddressSpaceView;
@ -103,7 +110,12 @@ typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer;
void GetAllocatorStats(AllocatorStatCounters s);
inline bool InTaggableRegion(uptr addr) {
// TODO: specialize for x86 once we use aliasing mode in the allocator.
#if defined(__x86_64__)
// Aliases are mapped next to shadow so that the upper bits match the shadow
// base.
return (addr >> kTaggableRegionCheckShift) ==
(__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift);
#endif
return true;
}

View File

@ -12,15 +12,17 @@
///
//===----------------------------------------------------------------------===//
#include "hwasan.h"
#include "hwasan_dynamic_shadow.h"
#include "hwasan_mapping.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_posix.h"
#include <elf.h>
#include <link.h>
#include "hwasan.h"
#include "hwasan_mapping.h"
#include "hwasan_thread_list.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_posix.h"
// The code in this file needs to run in an unrelocated binary. It should not
// access any external symbol, including its own non-hidden globals.
@ -117,6 +119,12 @@ namespace __hwasan {
void InitShadowGOT() {}
uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
#if defined(__x86_64__)
constexpr uptr kAliasSize = 1ULL << kAddressTagShift;
constexpr uptr kNumAliases = 1ULL << kTagBits;
return MapDynamicShadowAndAliases(shadow_size_bytes, kAliasSize, kNumAliases,
RingBufferSize());
#endif
return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment,
kHighMemEnd);
}

View File

@ -12,6 +12,8 @@
#ifndef HWASAN_FLAGS_H
#define HWASAN_FLAGS_H
#include "sanitizer_common/sanitizer_internal_defs.h"
namespace __hwasan {
struct Flags {

View File

@ -221,8 +221,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
ThreadStartArg *A = reinterpret_cast<ThreadStartArg *> (MmapOrDie(
GetPageSizeCached(), "pthread_create"));
*A = {callback, param};
int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
&HwasanThreadStartFunc, A);
int res = REAL(pthread_create)(th, attr, &HwasanThreadStartFunc, A);
return res;
}

View File

@ -76,6 +76,8 @@ uptr kHighShadowEnd;
uptr kHighMemStart;
uptr kHighMemEnd;
uptr kAliasRegionStart; // Always 0 on non-x86.
static void PrintRange(uptr start, uptr end, const char *name) {
Printf("|| [%p, %p] || %.*s ||\n", (void *)start, (void *)end, 10, name);
}
@ -123,7 +125,7 @@ void InitPrctl() {
if (internal_iserror(internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
&local_errno) &&
local_errno == EINVAL) {
#if SANITIZER_ANDROID
#if SANITIZER_ANDROID || defined(__x86_64__)
// Some older Android kernels have the tagged pointer ABI on
// unconditionally, and hence don't have the tagged-addr prctl while still
// allow the ABI.
@ -179,6 +181,18 @@ bool InitShadow() {
// High memory starts where allocated shadow allows.
kHighMemStart = ShadowToMem(kHighShadowStart);
#if defined(__x86_64__)
constexpr uptr kAliasRegionOffset = 1ULL << (kTaggableRegionCheckShift - 1);
kAliasRegionStart =
__hwasan_shadow_memory_dynamic_address + kAliasRegionOffset;
CHECK_EQ(kAliasRegionStart >> kTaggableRegionCheckShift,
__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift);
CHECK_EQ(
(kAliasRegionStart + kAliasRegionOffset - 1) >> kTaggableRegionCheckShift,
__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift);
#endif
// Check the sanity of the defined memory ranges (there might be gaps).
CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0);
CHECK_GT(kHighMemStart, kHighShadowEnd);

View File

@ -48,6 +48,8 @@ extern uptr kHighShadowEnd;
extern uptr kHighMemStart;
extern uptr kHighMemEnd;
extern uptr kAliasRegionStart;
inline uptr MemToShadow(uptr untagged_addr) {
return (untagged_addr >> kShadowScale) +
__hwasan_shadow_memory_dynamic_address;

View File

@ -24,7 +24,7 @@ using namespace __hwasan;
void *__hwasan_memset(void *block, int c, uptr size) {
CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
reinterpret_cast<uptr>(block), size);
return memset(UntagPtr(block), c, size);
return memset(block, c, size);
}
void *__hwasan_memcpy(void *to, const void *from, uptr size) {
@ -32,7 +32,7 @@ void *__hwasan_memcpy(void *to, const void *from, uptr size) {
reinterpret_cast<uptr>(to), size);
CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
reinterpret_cast<uptr>(from), size);
return memcpy(UntagPtr(to), UntagPtr(from), size);
return memcpy(to, from, size);
}
void *__hwasan_memmove(void *to, const void *from, uptr size) {

View File

@ -449,8 +449,14 @@ inline uptr Log2(uptr x) {
// Don't use std::min, std::max or std::swap, to minimize dependency
// on libstdc++.
template<class T> T Min(T a, T b) { return a < b ? a : b; }
template<class T> T Max(T a, T b) { return a > b ? a : b; }
template <class T>
constexpr T Min(T a, T b) {
return a < b ? a : b;
}
template <class T>
constexpr T Max(T a, T b) {
return a > b ? a : b;
}
template<class T> void Swap(T& a, T& b) {
T tmp = a;
a = b;

View File

@ -9,8 +9,6 @@
#include <stdio.h>
#include <stdlib.h>
#include "../utils.h"
extern void *aligned_alloc(size_t alignment, size_t size);
int main() {
@ -20,7 +18,7 @@ int main() {
// CHECK: {{#1 0x.* in main .*aligned_alloc-alignment.cpp:}}[[@LINE-3]]
// CHECK: SUMMARY: HWAddressSanitizer: invalid-aligned-alloc-alignment
untag_printf("pointer after failed aligned_alloc: %zd\n", (size_t)p);
printf("pointer after failed aligned_alloc: %zd\n", (size_t)p);
// CHECK-NULL: pointer after failed aligned_alloc: 0
return 0;

View File

@ -8,9 +8,6 @@
// A-NEXT: ---p {{.*}}shadow gap]
// A-NEXT: rw-p {{.*}}high shadow]
// B-DAG: rw-p {{.*}}SizeClassAllocator: region data]
// B-DAG: rw-p {{.*}}SizeClassAllocator: region metadata]
// B-DAG: rw-p {{.*}}SizeClassAllocator: freearray]
// B-DAG: rw-p {{.*}}SizeClassAllocator: region info]
// B-DAG: rw-p {{.*}}LargeMmapAllocator]
// B-DAG: rw-p {{.*}}stack depot]
@ -25,19 +22,17 @@
#include <pthread.h>
#include <stdlib.h>
#include "../utils.h"
void CopyFdToFd(int in_fd, int out_fd) {
const size_t kBufSize = 0x10000;
static char buf[kBufSize];
while (1) {
ssize_t got = read(in_fd, UNTAG(buf), kBufSize);
ssize_t got = read(in_fd, buf, kBufSize);
if (got > 0) {
write(out_fd, UNTAG(buf), got);
write(out_fd, buf, got);
} else if (got == 0) {
break;
} else if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR) {
untag_fprintf(stderr, "error reading file, errno %d\n", errno);
fprintf(stderr, "error reading file, errno %d\n", errno);
abort();
}
}
@ -45,7 +40,7 @@ void CopyFdToFd(int in_fd, int out_fd) {
void *ThreadFn(void *arg) {
(void)arg;
int fd = open(UNTAG("/proc/self/maps"), O_RDONLY);
int fd = open("/proc/self/maps", O_RDONLY);
CopyFdToFd(fd, 2);
close(fd);
return NULL;

View File

@ -18,8 +18,6 @@
#include <string.h>
#include <unistd.h>
#include "../utils.h"
int main(int argc, char *argv[]) {
assert(argc == 2);
const char *action = argv[1];
@ -27,15 +25,15 @@ int main(int argc, char *argv[]) {
const size_t page_size = sysconf(_SC_PAGESIZE);
void *p = nullptr;
if (!untag_strcmp(action, "m1")) {
if (!strcmp(action, "m1")) {
p = pvalloc((uintptr_t)-1);
} else if (!untag_strcmp(action, "psm1")) {
} else if (!strcmp(action, "psm1")) {
p = pvalloc((uintptr_t)-(page_size - 1));
} else {
assert(0);
}
untag_fprintf(stderr, "errno: %d\n", errno);
fprintf(stderr, "errno: %d\n", errno);
return p != nullptr;
}

View File

@ -12,8 +12,6 @@
#include <sanitizer/hwasan_interface.h>
#include "../utils.h"
const unsigned char kTag = 42;
const size_t kNumShadowPages = 256;
const size_t kNumPages = 16 * kNumShadowPages;
@ -32,13 +30,13 @@ void sync_rss() {
size_t current_rss() {
sync_rss();
int statm_fd = open(UNTAG("/proc/self/statm"), O_RDONLY);
int statm_fd = open("/proc/self/statm", O_RDONLY);
assert(statm_fd >= 0);
char buf[100];
assert(read(statm_fd, &buf, sizeof(buf)) > 0);
size_t size, rss;
assert(sscanf(buf, UNTAG("%zu %zu"), &size, &rss) == 2);
assert(sscanf(buf, "%zu %zu", &size, &rss) == 2);
close(statm_fd);
return rss;
@ -49,20 +47,20 @@ void test_rss_difference(void *p) {
size_t rss_before = current_rss();
__hwasan_tag_memory(p, 0, kMapSize);
size_t rss_after = current_rss();
untag_fprintf(stderr, "%zu -> %zu\n", rss_before, rss_after);
fprintf(stderr, "%zu -> %zu\n", rss_before, rss_after);
assert(rss_before > rss_after);
size_t diff = rss_before - rss_after;
untag_fprintf(stderr, "diff %zu\n", diff);
fprintf(stderr, "diff %zu\n", diff);
// Check that the difference is at least close to kNumShadowPages.
assert(diff > kNumShadowPages / 4 * 3);
}
int main() {
untag_fprintf(stderr, "starting rss %zu\n", current_rss());
untag_fprintf(stderr, "shadow pages: %zu\n", kNumShadowPages);
fprintf(stderr, "starting rss %zu\n", current_rss());
fprintf(stderr, "shadow pages: %zu\n", kNumShadowPages);
void *p = mmap(0, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
untag_fprintf(stderr, "p = %p\n", p);
fprintf(stderr, "p = %p\n", p);
test_rss_difference(p);
test_rss_difference(p);

View File

@ -10,12 +10,10 @@
#include <sanitizer/hwasan_interface.h>
#include "../utils.h"
pthread_barrier_t bar;
void *threadfn(void *) {
pthread_barrier_wait(UNTAG(&bar));
pthread_barrier_wait(&bar);
return nullptr;
}
@ -23,21 +21,21 @@ void start_stop_threads() {
constexpr int N = 2;
pthread_t threads[N];
pthread_barrier_init(UNTAG(&bar), nullptr, N + 1);
pthread_barrier_init(&bar, nullptr, N + 1);
for (auto &t : threads)
pthread_create(&t, nullptr, threadfn, nullptr);
pthread_barrier_wait(UNTAG(&bar));
pthread_barrier_wait(&bar);
for (auto &t : threads)
pthread_join(t, nullptr);
pthread_barrier_destroy(UNTAG(&bar));
pthread_barrier_destroy(&bar);
}
int main() {
// Cut off initial threads.
// CHECK: === test start ===
untag_fprintf(stderr, "=== test start ===\n");
fprintf(stderr, "=== test start ===\n");
// CHECK: Creating : T{{[0-9]+}} [[A:0x[0-9a-f]+]] stack:
// CHECK: Creating : T{{[0-9]+}} [[B:0x[0-9a-f]+]] stack:

View File

@ -3,6 +3,9 @@
// REQUIRES: aarch64-target-arch || x86_64-target-arch
// Aliasing mode does not support stack tagging.
// XFAIL: x86_64
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>

View File

@ -7,8 +7,6 @@
#include <stdio.h>
#include <stdlib.h>
#include "../utils.h"
int main() {
void *p = reinterpret_cast<void*>(42);
int res = posix_memalign(&p, 17, 100);
@ -17,7 +15,7 @@ int main() {
// CHECK: {{#1 0x.* in main .*posix_memalign-alignment.cpp:}}[[@LINE-3]]
// CHECK: SUMMARY: HWAddressSanitizer: invalid-posix-memalign-alignment
untag_printf("pointer after failed posix_memalign: %zd\n", (size_t)p);
printf("pointer after failed posix_memalign: %zd\n", (size_t)p);
// CHECK-NULL: pointer after failed posix_memalign: 42
return 0;

View File

@ -48,42 +48,40 @@
#include <limits>
#include <new>
#include "utils.h"
int main(int argc, char **argv) {
assert(argc == 2);
const char *action = argv[1];
untag_fprintf(stderr, "%s:\n", action);
fprintf(stderr, "%s:\n", action);
static const size_t kMaxAllowedMallocSizePlusOne = (1UL << 40) + 1;
void *x = nullptr;
if (!untag_strcmp(action, "malloc")) {
if (!strcmp(action, "malloc")) {
x = malloc(kMaxAllowedMallocSizePlusOne);
} else if (!untag_strcmp(action, "calloc")) {
} else if (!strcmp(action, "calloc")) {
x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4);
} else if (!untag_strcmp(action, "calloc-overflow")) {
} else if (!strcmp(action, "calloc-overflow")) {
volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max();
size_t kArraySize = 4096;
volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10;
x = calloc(kArraySize, kArraySize2);
} else if (!untag_strcmp(action, "realloc")) {
} else if (!strcmp(action, "realloc")) {
x = realloc(0, kMaxAllowedMallocSizePlusOne);
} else if (!untag_strcmp(action, "realloc-after-malloc")) {
} else if (!strcmp(action, "realloc-after-malloc")) {
char *t = (char*)malloc(100);
*t = 42;
x = realloc(t, kMaxAllowedMallocSizePlusOne);
assert(*t == 42);
free(t);
} else if (!untag_strcmp(action, "new")) {
} else if (!strcmp(action, "new")) {
x = operator new(kMaxAllowedMallocSizePlusOne);
} else if (!untag_strcmp(action, "new-nothrow")) {
} else if (!strcmp(action, "new-nothrow")) {
x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow);
} else {
assert(0);
}
untag_fprintf(stderr, "errno: %d\n", errno);
fprintf(stderr, "errno: %d\n", errno);
free(x);

View File

@ -15,8 +15,6 @@
#include <stdio.h>
#include <sanitizer/hwasan_interface.h>
#include "utils.h"
static volatile char sink;
int main(int argc, char **argv) {
@ -24,9 +22,21 @@ int main(int argc, char **argv) {
int offset = argc < 2 ? 40 : atoi(argv[1]);
int size = argc < 3 ? 30 : atoi(argv[2]);
char * volatile x = (char*)malloc(size);
untag_fprintf(stderr, "base: %p access: %p\n", x, &x[offset]);
fprintf(stderr, "base: %p access: %p\n", x, &x[offset]);
sink = x[offset];
#if defined(__x86_64__)
// Aliasing mode doesn't support the secondary allocator, so we fake a HWASan
// report instead of disabling the entire test.
if (size == 1000000) {
fprintf(stderr, "is a large allocated heap chunk; size: 1003520 offset: %d\n",
offset);
fprintf(stderr, "is located %s of 1000000-byte region\n",
offset == -30 ? "30 bytes to the left" : "0 bytes to the right");
return -1;
}
#endif
// CHECK40: allocated heap chunk; size: 32 offset: 8
// CHECK40: is located 10 bytes to the right of 30-byte region
//

View File

@ -8,8 +8,7 @@
#include <sanitizer/hwasan_interface.h>
int main() {
char *p = (char *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
char *p = (char *)malloc(4096);
assert(p);
__hwasan_tag_memory(p, 1, 32);
@ -26,4 +25,6 @@ int main() {
// CHECK-NEXT: {{.*}}0: 0
// CHECK-NEXT: {{.*}}0: 0
// CHECK-NEXT: {{.*}}0: 4
free(p);
}

View File

@ -8,17 +8,15 @@
#include <stdio.h>
#include "utils.h"
int main(int argc, char **argv) {
// With asan allocator this makes sure we get memory from mmap.
static const int kSize = 1 << 25;
unsigned char *x = new unsigned char[kSize];
untag_printf("-");
printf("-");
for (int i = 0; i <= 32; i++) {
untag_printf("%02x", x[i]);
printf("%02x", x[i]);
}
untag_printf("-\n");
printf("-\n");
delete [] x;
}

View File

@ -7,8 +7,6 @@
#include <sanitizer/hwasan_interface.h>
#include "utils.h"
void *BoringThread(void *arg) {
char * volatile x = (char*)malloc(10);
x[5] = 0;
@ -25,7 +23,7 @@ void *BoringThread(void *arg) {
void *UAFThread(void *arg) {
char * volatile x = (char*)malloc(10);
untag_fprintf(stderr, "ZZZ %p\n", x);
fprintf(stderr, "ZZZ %p\n", x);
free(x);
x[5] = 42;
// CHECK: ERROR: HWAddressSanitizer: tag-mismatch on address

View File

@ -12,8 +12,6 @@
#include <stdlib.h>
#include <unistd.h>
#include "utils.h"
int main() {
char Q[16] __attribute__((aligned(256)));
char P[16] __attribute__((aligned(256)));
@ -24,7 +22,7 @@ int main() {
#elif TEST_NO == 3
memcpy(Q, P, 32);
#endif
write(STDOUT_FILENO, UNTAG("recovered\n"), 10);
write(STDOUT_FILENO, "recovered\n", 10);
// WRITE: ERROR: HWAddressSanitizer: tag-mismatch on address
// WRITE: WRITE of size 32 at {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem)
// WRITE: Invalid access starting at offset [16, 32)

View File

@ -5,10 +5,8 @@
#include <sanitizer/hwasan_interface.h>
#include "utils.h"
__attribute__((no_sanitize("hwaddress"))) extern "C" void callback(const char *msg) {
untag_fprintf(stderr, "== error start\n%s\n== error end\n", msg);
fprintf(stderr, "== error start\n%s\n== error end\n", msg);
}
int main() {

View File

@ -34,11 +34,9 @@
#include <sanitizer/allocator_interface.h>
#include <sanitizer/hwasan_interface.h>
#include "utils.h"
int main(int argc, char **argv) {
assert(argc <= 3);
bool test_size_max = argc == 3 && !untag_strcmp(argv[2], "max");
bool test_size_max = argc == 3 && !strcmp(argv[2], "max");
static const size_t kMaxAllowedMallocSize = 1ULL << 40;
static const size_t kChunkHeaderSize = 16;
@ -46,26 +44,26 @@ int main(int argc, char **argv) {
size_t MallocSize = test_size_max ? std::numeric_limits<size_t>::max()
: (kMaxAllowedMallocSize + 1);
if (!untag_strcmp(argv[1], "malloc")) {
if (!strcmp(argv[1], "malloc")) {
void *p = malloc(MallocSize);
assert(!p);
} else if (!untag_strcmp(argv[1], "calloc")) {
} else if (!strcmp(argv[1], "calloc")) {
// Trigger an overflow in calloc.
size_t size = std::numeric_limits<size_t>::max();
void *p = calloc((size / 0x1000) + 1, 0x1000);
assert(!p);
} else if (!untag_strcmp(argv[1], "reallocarray")) {
} else if (!strcmp(argv[1], "reallocarray")) {
// Trigger an overflow in reallocarray.
size_t size = std::numeric_limits<size_t>::max();
void *p = __sanitizer_reallocarray(nullptr, (size / 0x1000) + 1, 0x1000);
assert(!p);
} else if (!untag_strcmp(argv[1], "new")) {
} else if (!strcmp(argv[1], "new")) {
void *p = operator new(MallocSize);
assert(!p);
} else if (!untag_strcmp(argv[1], "new-nothrow")) {
} else if (!strcmp(argv[1], "new-nothrow")) {
void *p = operator new(MallocSize, std::nothrow);
assert(!p);
} else if (!untag_strcmp(argv[1], "usable")) {
} else if (!strcmp(argv[1], "usable")) {
// Playing with the actual usable size of a chunk.
void *p = malloc(1007);
assert(p);

View File

@ -10,22 +10,20 @@
#include <stdio.h>
#include <sanitizer/hwasan_interface.h>
#include "utils.h"
static volatile char *sink;
// Overwrite the tail in a non-hwasan function so that we don't detect the
// stores as OOB.
__attribute__((no_sanitize("hwaddress"))) void overwrite_tail() {
(*UNTAG(&sink))[20] = 0x42;
(*UNTAG(&sink))[24] = 0x66;
sink[20] = 0x42;
sink[24] = 0x66;
}
int main(int argc, char **argv) {
__hwasan_enable_allocator_tagging();
char *p = (char*)malloc(20);
sink = UNTAG(p);
sink = p;
overwrite_tail();
free(p);
// CHECK: ERROR: HWAddressSanitizer: allocation-tail-overwritten; heap object [{{.*}}) of size 20

View File

@ -11,14 +11,12 @@
#include <stdio.h>
#include <sanitizer/hwasan_interface.h>
#include "utils.h"
int main() {
__hwasan_enable_allocator_tagging();
char * volatile x = (char*)malloc(10);
free(x);
__hwasan_disable_allocator_tagging();
untag_fprintf(stderr, ISREAD ? "Going to do a READ\n" : "Going to do a WRITE\n");
fprintf(stderr, ISREAD ? "Going to do a READ\n" : "Going to do a WRITE\n");
// CHECK: Going to do a [[TYPE:[A-Z]*]]
int r = 0;
if (ISREAD) r = x[5]; else x[5] = 42; // should be on the same line.

View File

@ -1,30 +0,0 @@
#pragma once
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define UNTAG(x) (typeof((x) + 0))(((uintptr_t)(x)) & 0xffffffffffffff)
__attribute__((no_sanitize("hwaddress")))
int untag_printf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int ret = vprintf(UNTAG(fmt), ap);
va_end(ap);
return ret;
}
__attribute__((no_sanitize("hwaddress")))
int untag_fprintf(FILE *stream, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int ret = vfprintf(stream, UNTAG(fmt), ap);
va_end(ap);
return ret;
}
int untag_strcmp(const char *s1, const char *s2) {
return strcmp(UNTAG(s1), UNTAG(s2));
}

View File

@ -708,7 +708,7 @@ static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
}
void HWAddressSanitizer::untagPointerOperand(Instruction *I, Value *Addr) {
if (TargetTriple.isAArch64())
if (TargetTriple.isAArch64() || TargetTriple.getArch() == Triple::x86_64)
return;
IRBuilder<> IRB(I);
@ -1004,6 +1004,7 @@ Value *HWAddressSanitizer::tagPointer(IRBuilder<> &IRB, Type *Ty,
// Remove tag from an address.
Value *HWAddressSanitizer::untagPointer(IRBuilder<> &IRB, Value *PtrLong) {
assert(!UsePageAliases);
Value *UntaggedPtrLong;
if (CompileKernel) {
// Kernel addresses have 0xFF in the most significant byte.

View File

@ -11,10 +11,7 @@ define void @atomicrmw(i64* %ptr) sanitize_hwaddress {
; CHECK: call void @__hwasan_store8(i64 %[[A]])
; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i64*
; CHECK: atomicrmw add i64* %[[UNTAGGED_PTR]], i64 1 seq_cst
; CHECK: atomicrmw add i64* %ptr, i64 1 seq_cst
; CHECK: ret void
entry:
@ -28,10 +25,7 @@ define void @cmpxchg(i64* %ptr, i64 %compare_to, i64 %new_value) sanitize_hwaddr
; CHECK: call void @__hwasan_store8(i64 %[[A]])
; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %ptr to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i64*
; CHECK: cmpxchg i64* %[[UNTAGGED_PTR]], i64 %compare_to, i64 %new_value seq_cst seq_cst
; CHECK: cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst
; CHECK: ret void
entry:

View File

@ -15,10 +15,7 @@ define i8 @test_load8(i8* %a) sanitize_hwaddress {
; ABORT: call void @__hwasan_load1(i64 %[[A]])
; RECOVER: call void @__hwasan_load1_noabort(i64 %[[A]])
; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i8*
; CHECK: %[[G:[^ ]*]] = load i8, i8* %[[UNTAGGED_PTR]], align 4
; CHECK: %[[G:[^ ]*]] = load i8, i8* %a, align 4
; CHECK: ret i8 %[[G]]
entry:
@ -33,10 +30,7 @@ define i40 @test_load40(i40* %a) sanitize_hwaddress {
; ABORT: call void @__hwasan_loadN(i64 %[[A]], i64 5)
; RECOVER: call void @__hwasan_loadN_noabort(i64 %[[A]], i64 5)
; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i40*
; CHECK: %[[B:[^ ]*]] = load i40, i40* %[[UNTAGGED_PTR]]
; CHECK: %[[B:[^ ]*]] = load i40, i40* %a
; CHECK: ret i40 %[[B]]
entry:
@ -51,10 +45,7 @@ define void @test_store8(i8* %a, i8 %b) sanitize_hwaddress {
; ABORT: call void @__hwasan_store1(i64 %[[A]])
; RECOVER: call void @__hwasan_store1_noabort(i64 %[[A]])
; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i8*
; CHECK: store i8 %b, i8* %[[UNTAGGED_PTR]], align 4
; CHECK: store i8 %b, i8* %a, align 4
; CHECK: ret void
entry:
@ -69,10 +60,7 @@ define void @test_store40(i40* %a, i40 %b) sanitize_hwaddress {
; ABORT: call void @__hwasan_storeN(i64 %[[A]], i64 5)
; RECOVER: call void @__hwasan_storeN_noabort(i64 %[[A]], i64 5)
; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i40*
; CHECK: store i40 %b, i40* %[[UNTAGGED_PTR]]
; CHECK: store i40 %b, i40* %a
; CHECK: ret void
entry:
@ -87,10 +75,7 @@ define void @test_store_unaligned(i64* %a, i64 %b) sanitize_hwaddress {
; ABORT: call void @__hwasan_storeN(i64 %[[A]], i64 8)
; RECOVER: call void @__hwasan_storeN_noabort(i64 %[[A]], i64 8)
; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i64*
; CHECK: store i64 %b, i64* %[[UNTAGGED_PTR]], align 4
; CHECK: store i64 %b, i64* %a, align 4
; CHECK: ret void
entry:

View File

@ -18,10 +18,7 @@ define i8 @test_load(i8* %a) sanitize_hwaddress {
; ABORT: call void @__hwasan_load1(i64 %[[A]])
; RECOVER: call void @__hwasan_load1_noabort(i64 %[[A]])
; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = or i64 %[[A]], -72057594037927936
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i8*
; CHECK: %[[G:[^ ]*]] = load i8, i8* %[[UNTAGGED_PTR]], align 4
; CHECK: %[[G:[^ ]*]] = load i8, i8* %a, align 4
; CHECK: ret i8 %[[G]]
entry:

View File

@ -13,10 +13,7 @@ define i8 @test_load8(i8* %a) sanitize_hwaddress {
; ABORT: call void @__hwasan_load1(i64 %[[A]])
; RECOVER: call void @__hwasan_load1_noabort(i64 %[[A]])
; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i8*
; CHECK: %[[B:[^ ]*]] = load i8, i8* %[[UNTAGGED_PTR]]
; CHECK: %[[B:[^ ]*]] = load i8, i8* %a
; CHECK: ret i8 %[[B]]
entry:
@ -31,10 +28,7 @@ define i40 @test_load40(i40* %a) sanitize_hwaddress {
; ABORT: call void @__hwasan_loadN(i64 %[[A]], i64 5)
; RECOVER: call void @__hwasan_loadN_noabort(i64 %[[A]], i64 5)
; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i40*
; CHECK: %[[B:[^ ]*]] = load i40, i40* %[[UNTAGGED_PTR]]
; CHECK: %[[B:[^ ]*]] = load i40, i40* %a
; CHECK: ret i40 %[[B]]
entry:
@ -49,10 +43,7 @@ define void @test_store8(i8* %a, i8 %b) sanitize_hwaddress {
; ABORT: call void @__hwasan_store1(i64 %[[A]])
; RECOVER: call void @__hwasan_store1_noabort(i64 %[[A]])
; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i8*
; CHECK: store i8 %b, i8* %[[UNTAGGED_PTR]]
; CHECK: store i8 %b, i8* %a
; CHECK: ret void
entry:
@ -67,10 +58,7 @@ define void @test_store40(i40* %a, i40 %b) sanitize_hwaddress {
; ABORT: call void @__hwasan_storeN(i64 %[[A]], i64 5)
; RECOVER: call void @__hwasan_storeN_noabort(i64 %[[A]], i64 5)
; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64
; CHECK: %[[UNTAGGED:[^ ]*]] = and i64 %[[A]], 72057594037927935
; CHECK: %[[UNTAGGED_PTR:[^ ]*]] = inttoptr i64 %[[UNTAGGED]] to i40*
; CHECK: store i40 %b, i40* %[[UNTAGGED_PTR]]
; CHECK: store i40 %b, i40* %a
; CHECK: ret void
entry: