forked from OSchip/llvm-project
Revert "[HWASan] Use page aliasing on x86_64."
This reverts commit 63f73c3eb9
due to
breakage on aarch64 without TBI.
This commit is contained in:
parent
6869e6c1e7
commit
c8ef98e5de
|
@ -14,12 +14,11 @@
|
|||
#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
|
||||
|
@ -36,30 +35,15 @@
|
|||
|
||||
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;
|
||||
|
||||
// Mask for extracting tag bits from full pointers.
|
||||
// Masks for extracting and removing tags from full pointers.
|
||||
constexpr uptr kAddressTagMask = kTagMask << kAddressTagShift;
|
||||
|
||||
// Minimal alignment of the shadow base address. Determines the space available
|
||||
|
|
|
@ -84,8 +84,7 @@ 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,
|
||||
kAliasRegionStart);
|
||||
allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
|
||||
for (uptr i = 0; i < sizeof(tail_magic); i++)
|
||||
tail_magic[i] = GetCurrentThread()->GenerateRandomTag();
|
||||
}
|
||||
|
@ -375,7 +374,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));
|
||||
*memptr = ptr;
|
||||
*(void **)UntagPtr(memptr) = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
#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"
|
||||
|
@ -57,12 +55,7 @@ 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;
|
||||
|
@ -110,12 +103,7 @@ typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer;
|
|||
void GetAllocatorStats(AllocatorStatCounters s);
|
||||
|
||||
inline bool InTaggableRegion(uptr addr) {
|
||||
#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
|
||||
// TODO: specialize for x86 once we use aliasing mode in the allocator.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,17 +12,15 @@
|
|||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#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.
|
||||
|
||||
|
@ -119,12 +117,6 @@ 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);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#ifndef HWASAN_FLAGS_H
|
||||
#define HWASAN_FLAGS_H
|
||||
|
||||
#include "sanitizer_common/sanitizer_internal_defs.h"
|
||||
|
||||
namespace __hwasan {
|
||||
|
||||
struct Flags {
|
||||
|
|
|
@ -221,7 +221,8 @@ 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)(th, attr, &HwasanThreadStartFunc, A);
|
||||
int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
|
||||
&HwasanThreadStartFunc, A);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -76,8 +76,6 @@ 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);
|
||||
}
|
||||
|
@ -125,7 +123,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 || defined(__x86_64__)
|
||||
#if SANITIZER_ANDROID
|
||||
// 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.
|
||||
|
@ -181,18 +179,6 @@ 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);
|
||||
|
|
|
@ -48,8 +48,6 @@ 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;
|
||||
|
|
|
@ -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(block, c, size);
|
||||
return memset(UntagPtr(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(to, from, size);
|
||||
return memcpy(UntagPtr(to), UntagPtr(from), size);
|
||||
}
|
||||
|
||||
void *__hwasan_memmove(void *to, const void *from, uptr size) {
|
||||
|
|
|
@ -449,14 +449,8 @@ inline uptr Log2(uptr x) {
|
|||
|
||||
// Don't use std::min, std::max or std::swap, to minimize dependency
|
||||
// on libstdc++.
|
||||
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> 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> void Swap(T& a, T& b) {
|
||||
T tmp = a;
|
||||
a = b;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
extern void *aligned_alloc(size_t alignment, size_t size);
|
||||
|
||||
int main() {
|
||||
|
@ -18,7 +20,7 @@ int main() {
|
|||
// CHECK: {{#1 0x.* in main .*aligned_alloc-alignment.cpp:}}[[@LINE-3]]
|
||||
// CHECK: SUMMARY: HWAddressSanitizer: invalid-aligned-alloc-alignment
|
||||
|
||||
printf("pointer after failed aligned_alloc: %zd\n", (size_t)p);
|
||||
untag_printf("pointer after failed aligned_alloc: %zd\n", (size_t)p);
|
||||
// CHECK-NULL: pointer after failed aligned_alloc: 0
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
// 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]
|
||||
|
@ -22,17 +25,19 @@
|
|||
#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, buf, kBufSize);
|
||||
ssize_t got = read(in_fd, UNTAG(buf), kBufSize);
|
||||
if (got > 0) {
|
||||
write(out_fd, buf, got);
|
||||
write(out_fd, UNTAG(buf), got);
|
||||
} else if (got == 0) {
|
||||
break;
|
||||
} else if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR) {
|
||||
fprintf(stderr, "error reading file, errno %d\n", errno);
|
||||
untag_fprintf(stderr, "error reading file, errno %d\n", errno);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +45,7 @@ void CopyFdToFd(int in_fd, int out_fd) {
|
|||
|
||||
void *ThreadFn(void *arg) {
|
||||
(void)arg;
|
||||
int fd = open("/proc/self/maps", O_RDONLY);
|
||||
int fd = open(UNTAG("/proc/self/maps"), O_RDONLY);
|
||||
CopyFdToFd(fd, 2);
|
||||
close(fd);
|
||||
return NULL;
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
assert(argc == 2);
|
||||
const char *action = argv[1];
|
||||
|
@ -25,15 +27,15 @@ int main(int argc, char *argv[]) {
|
|||
const size_t page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
void *p = nullptr;
|
||||
if (!strcmp(action, "m1")) {
|
||||
if (!untag_strcmp(action, "m1")) {
|
||||
p = pvalloc((uintptr_t)-1);
|
||||
} else if (!strcmp(action, "psm1")) {
|
||||
} else if (!untag_strcmp(action, "psm1")) {
|
||||
p = pvalloc((uintptr_t)-(page_size - 1));
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
fprintf(stderr, "errno: %d\n", errno);
|
||||
untag_fprintf(stderr, "errno: %d\n", errno);
|
||||
|
||||
return p != nullptr;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#include <sanitizer/hwasan_interface.h>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
const unsigned char kTag = 42;
|
||||
const size_t kNumShadowPages = 256;
|
||||
const size_t kNumPages = 16 * kNumShadowPages;
|
||||
|
@ -30,13 +32,13 @@ void sync_rss() {
|
|||
|
||||
size_t current_rss() {
|
||||
sync_rss();
|
||||
int statm_fd = open("/proc/self/statm", O_RDONLY);
|
||||
int statm_fd = open(UNTAG("/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, "%zu %zu", &size, &rss) == 2);
|
||||
assert(sscanf(buf, UNTAG("%zu %zu"), &size, &rss) == 2);
|
||||
|
||||
close(statm_fd);
|
||||
return rss;
|
||||
|
@ -47,20 +49,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();
|
||||
fprintf(stderr, "%zu -> %zu\n", rss_before, rss_after);
|
||||
untag_fprintf(stderr, "%zu -> %zu\n", rss_before, rss_after);
|
||||
assert(rss_before > rss_after);
|
||||
size_t diff = rss_before - rss_after;
|
||||
fprintf(stderr, "diff %zu\n", diff);
|
||||
untag_fprintf(stderr, "diff %zu\n", diff);
|
||||
// Check that the difference is at least close to kNumShadowPages.
|
||||
assert(diff > kNumShadowPages / 4 * 3);
|
||||
}
|
||||
|
||||
int main() {
|
||||
fprintf(stderr, "starting rss %zu\n", current_rss());
|
||||
fprintf(stderr, "shadow pages: %zu\n", kNumShadowPages);
|
||||
untag_fprintf(stderr, "starting rss %zu\n", current_rss());
|
||||
untag_fprintf(stderr, "shadow pages: %zu\n", kNumShadowPages);
|
||||
|
||||
void *p = mmap(0, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
|
||||
fprintf(stderr, "p = %p\n", p);
|
||||
untag_fprintf(stderr, "p = %p\n", p);
|
||||
|
||||
test_rss_difference(p);
|
||||
test_rss_difference(p);
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
|
||||
#include <sanitizer/hwasan_interface.h>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
pthread_barrier_t bar;
|
||||
|
||||
void *threadfn(void *) {
|
||||
pthread_barrier_wait(&bar);
|
||||
pthread_barrier_wait(UNTAG(&bar));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -21,21 +23,21 @@ void start_stop_threads() {
|
|||
constexpr int N = 2;
|
||||
pthread_t threads[N];
|
||||
|
||||
pthread_barrier_init(&bar, nullptr, N + 1);
|
||||
pthread_barrier_init(UNTAG(&bar), nullptr, N + 1);
|
||||
for (auto &t : threads)
|
||||
pthread_create(&t, nullptr, threadfn, nullptr);
|
||||
|
||||
pthread_barrier_wait(&bar);
|
||||
pthread_barrier_wait(UNTAG(&bar));
|
||||
|
||||
for (auto &t : threads)
|
||||
pthread_join(t, nullptr);
|
||||
pthread_barrier_destroy(&bar);
|
||||
pthread_barrier_destroy(UNTAG(&bar));
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Cut off initial threads.
|
||||
// CHECK: === test start ===
|
||||
fprintf(stderr, "=== test start ===\n");
|
||||
untag_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:
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
// 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>
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../utils.h"
|
||||
|
||||
int main() {
|
||||
void *p = reinterpret_cast<void*>(42);
|
||||
int res = posix_memalign(&p, 17, 100);
|
||||
|
@ -15,7 +17,7 @@ int main() {
|
|||
// CHECK: {{#1 0x.* in main .*posix_memalign-alignment.cpp:}}[[@LINE-3]]
|
||||
// CHECK: SUMMARY: HWAddressSanitizer: invalid-posix-memalign-alignment
|
||||
|
||||
printf("pointer after failed posix_memalign: %zd\n", (size_t)p);
|
||||
untag_printf("pointer after failed posix_memalign: %zd\n", (size_t)p);
|
||||
// CHECK-NULL: pointer after failed posix_memalign: 42
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -48,40 +48,42 @@
|
|||
#include <limits>
|
||||
#include <new>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
assert(argc == 2);
|
||||
const char *action = argv[1];
|
||||
fprintf(stderr, "%s:\n", action);
|
||||
untag_fprintf(stderr, "%s:\n", action);
|
||||
|
||||
static const size_t kMaxAllowedMallocSizePlusOne = (1UL << 40) + 1;
|
||||
|
||||
void *x = nullptr;
|
||||
if (!strcmp(action, "malloc")) {
|
||||
if (!untag_strcmp(action, "malloc")) {
|
||||
x = malloc(kMaxAllowedMallocSizePlusOne);
|
||||
} else if (!strcmp(action, "calloc")) {
|
||||
} else if (!untag_strcmp(action, "calloc")) {
|
||||
x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4);
|
||||
} else if (!strcmp(action, "calloc-overflow")) {
|
||||
} else if (!untag_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 (!strcmp(action, "realloc")) {
|
||||
} else if (!untag_strcmp(action, "realloc")) {
|
||||
x = realloc(0, kMaxAllowedMallocSizePlusOne);
|
||||
} else if (!strcmp(action, "realloc-after-malloc")) {
|
||||
} else if (!untag_strcmp(action, "realloc-after-malloc")) {
|
||||
char *t = (char*)malloc(100);
|
||||
*t = 42;
|
||||
x = realloc(t, kMaxAllowedMallocSizePlusOne);
|
||||
assert(*t == 42);
|
||||
free(t);
|
||||
} else if (!strcmp(action, "new")) {
|
||||
} else if (!untag_strcmp(action, "new")) {
|
||||
x = operator new(kMaxAllowedMallocSizePlusOne);
|
||||
} else if (!strcmp(action, "new-nothrow")) {
|
||||
} else if (!untag_strcmp(action, "new-nothrow")) {
|
||||
x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
fprintf(stderr, "errno: %d\n", errno);
|
||||
untag_fprintf(stderr, "errno: %d\n", errno);
|
||||
|
||||
free(x);
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <stdio.h>
|
||||
#include <sanitizer/hwasan_interface.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
static volatile char sink;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
@ -22,21 +24,9 @@ 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);
|
||||
fprintf(stderr, "base: %p access: %p\n", x, &x[offset]);
|
||||
untag_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
|
||||
//
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#include <sanitizer/hwasan_interface.h>
|
||||
|
||||
int main() {
|
||||
char *p = (char *)malloc(4096);
|
||||
char *p = (char *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
|
||||
assert(p);
|
||||
|
||||
__hwasan_tag_memory(p, 1, 32);
|
||||
|
@ -25,6 +26,4 @@ int main() {
|
|||
// CHECK-NEXT: {{.*}}0: 0
|
||||
// CHECK-NEXT: {{.*}}0: 0
|
||||
// CHECK-NEXT: {{.*}}0: 4
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
|
|
@ -8,15 +8,17 @@
|
|||
|
||||
#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];
|
||||
printf("-");
|
||||
untag_printf("-");
|
||||
for (int i = 0; i <= 32; i++) {
|
||||
printf("%02x", x[i]);
|
||||
untag_printf("%02x", x[i]);
|
||||
}
|
||||
printf("-\n");
|
||||
untag_printf("-\n");
|
||||
delete [] x;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <sanitizer/hwasan_interface.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
void *BoringThread(void *arg) {
|
||||
char * volatile x = (char*)malloc(10);
|
||||
x[5] = 0;
|
||||
|
@ -23,7 +25,7 @@ void *BoringThread(void *arg) {
|
|||
|
||||
void *UAFThread(void *arg) {
|
||||
char * volatile x = (char*)malloc(10);
|
||||
fprintf(stderr, "ZZZ %p\n", x);
|
||||
untag_fprintf(stderr, "ZZZ %p\n", x);
|
||||
free(x);
|
||||
x[5] = 42;
|
||||
// CHECK: ERROR: HWAddressSanitizer: tag-mismatch on address
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
int main() {
|
||||
char Q[16] __attribute__((aligned(256)));
|
||||
char P[16] __attribute__((aligned(256)));
|
||||
|
@ -22,7 +24,7 @@ int main() {
|
|||
#elif TEST_NO == 3
|
||||
memcpy(Q, P, 32);
|
||||
#endif
|
||||
write(STDOUT_FILENO, "recovered\n", 10);
|
||||
write(STDOUT_FILENO, UNTAG("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)
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
|
||||
#include <sanitizer/hwasan_interface.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
__attribute__((no_sanitize("hwaddress"))) extern "C" void callback(const char *msg) {
|
||||
fprintf(stderr, "== error start\n%s\n== error end\n", msg);
|
||||
untag_fprintf(stderr, "== error start\n%s\n== error end\n", msg);
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
|
|
@ -34,9 +34,11 @@
|
|||
#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 && !strcmp(argv[2], "max");
|
||||
bool test_size_max = argc == 3 && !untag_strcmp(argv[2], "max");
|
||||
|
||||
static const size_t kMaxAllowedMallocSize = 1ULL << 40;
|
||||
static const size_t kChunkHeaderSize = 16;
|
||||
|
@ -44,26 +46,26 @@ int main(int argc, char **argv) {
|
|||
size_t MallocSize = test_size_max ? std::numeric_limits<size_t>::max()
|
||||
: (kMaxAllowedMallocSize + 1);
|
||||
|
||||
if (!strcmp(argv[1], "malloc")) {
|
||||
if (!untag_strcmp(argv[1], "malloc")) {
|
||||
void *p = malloc(MallocSize);
|
||||
assert(!p);
|
||||
} else if (!strcmp(argv[1], "calloc")) {
|
||||
} else if (!untag_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 (!strcmp(argv[1], "reallocarray")) {
|
||||
} else if (!untag_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 (!strcmp(argv[1], "new")) {
|
||||
} else if (!untag_strcmp(argv[1], "new")) {
|
||||
void *p = operator new(MallocSize);
|
||||
assert(!p);
|
||||
} else if (!strcmp(argv[1], "new-nothrow")) {
|
||||
} else if (!untag_strcmp(argv[1], "new-nothrow")) {
|
||||
void *p = operator new(MallocSize, std::nothrow);
|
||||
assert(!p);
|
||||
} else if (!strcmp(argv[1], "usable")) {
|
||||
} else if (!untag_strcmp(argv[1], "usable")) {
|
||||
// Playing with the actual usable size of a chunk.
|
||||
void *p = malloc(1007);
|
||||
assert(p);
|
||||
|
|
|
@ -10,20 +10,22 @@
|
|||
#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() {
|
||||
sink[20] = 0x42;
|
||||
sink[24] = 0x66;
|
||||
(*UNTAG(&sink))[20] = 0x42;
|
||||
(*UNTAG(&sink))[24] = 0x66;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
__hwasan_enable_allocator_tagging();
|
||||
|
||||
char *p = (char*)malloc(20);
|
||||
sink = p;
|
||||
sink = UNTAG(p);
|
||||
overwrite_tail();
|
||||
free(p);
|
||||
// CHECK: ERROR: HWAddressSanitizer: allocation-tail-overwritten; heap object [{{.*}}) of size 20
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
#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();
|
||||
fprintf(stderr, ISREAD ? "Going to do a READ\n" : "Going to do a WRITE\n");
|
||||
untag_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.
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#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));
|
||||
}
|
|
@ -708,7 +708,7 @@ static size_t TypeSizeToSizeIndex(uint32_t TypeSize) {
|
|||
}
|
||||
|
||||
void HWAddressSanitizer::untagPointerOperand(Instruction *I, Value *Addr) {
|
||||
if (TargetTriple.isAArch64() || TargetTriple.getArch() == Triple::x86_64)
|
||||
if (TargetTriple.isAArch64())
|
||||
return;
|
||||
|
||||
IRBuilder<> IRB(I);
|
||||
|
@ -1004,7 +1004,6 @@ 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.
|
||||
|
|
|
@ -11,7 +11,10 @@ define void @atomicrmw(i64* %ptr) sanitize_hwaddress {
|
|||
|
||||
; CHECK: call void @__hwasan_store8(i64 %[[A]])
|
||||
|
||||
; CHECK: atomicrmw add i64* %ptr, i64 1 seq_cst
|
||||
; 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: ret void
|
||||
|
||||
entry:
|
||||
|
@ -25,7 +28,10 @@ define void @cmpxchg(i64* %ptr, i64 %compare_to, i64 %new_value) sanitize_hwaddr
|
|||
|
||||
; CHECK: call void @__hwasan_store8(i64 %[[A]])
|
||||
|
||||
; CHECK: cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst
|
||||
; 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: ret void
|
||||
|
||||
entry:
|
||||
|
|
|
@ -15,7 +15,10 @@ define i8 @test_load8(i8* %a) sanitize_hwaddress {
|
|||
; ABORT: call void @__hwasan_load1(i64 %[[A]])
|
||||
; RECOVER: call void @__hwasan_load1_noabort(i64 %[[A]])
|
||||
|
||||
; CHECK: %[[G:[^ ]*]] = load i8, i8* %a, align 4
|
||||
; 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: ret i8 %[[G]]
|
||||
|
||||
entry:
|
||||
|
@ -30,7 +33,10 @@ 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: %[[B:[^ ]*]] = load i40, i40* %a
|
||||
; 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: ret i40 %[[B]]
|
||||
|
||||
entry:
|
||||
|
@ -45,7 +51,10 @@ 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: store i8 %b, i8* %a, align 4
|
||||
; 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: ret void
|
||||
|
||||
entry:
|
||||
|
@ -60,7 +69,10 @@ 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: store i40 %b, i40* %a
|
||||
; 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: ret void
|
||||
|
||||
entry:
|
||||
|
@ -75,7 +87,10 @@ 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: store i64 %b, i64* %a, align 4
|
||||
; 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: ret void
|
||||
|
||||
entry:
|
||||
|
|
|
@ -18,7 +18,10 @@ define i8 @test_load(i8* %a) sanitize_hwaddress {
|
|||
; ABORT: call void @__hwasan_load1(i64 %[[A]])
|
||||
; RECOVER: call void @__hwasan_load1_noabort(i64 %[[A]])
|
||||
|
||||
; CHECK: %[[G:[^ ]*]] = load i8, i8* %a, align 4
|
||||
; 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: ret i8 %[[G]]
|
||||
|
||||
entry:
|
||||
|
|
|
@ -13,7 +13,10 @@ define i8 @test_load8(i8* %a) sanitize_hwaddress {
|
|||
; ABORT: call void @__hwasan_load1(i64 %[[A]])
|
||||
; RECOVER: call void @__hwasan_load1_noabort(i64 %[[A]])
|
||||
|
||||
; CHECK: %[[B:[^ ]*]] = load i8, i8* %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: ret i8 %[[B]]
|
||||
|
||||
entry:
|
||||
|
@ -28,7 +31,10 @@ 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: %[[B:[^ ]*]] = load i40, i40* %a
|
||||
; 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: ret i40 %[[B]]
|
||||
|
||||
entry:
|
||||
|
@ -43,7 +49,10 @@ 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: store i8 %b, i8* %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: ret void
|
||||
|
||||
entry:
|
||||
|
@ -58,7 +67,10 @@ 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: store i40 %b, i40* %a
|
||||
; 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: ret void
|
||||
|
||||
entry:
|
||||
|
|
Loading…
Reference in New Issue