Commit fb068e84 by Thomas Lively

Subzero: Improved quality of ASan error messages

Added load/store and stack/heap/global information. BUG=https://bugs.chromium.org/p/nativeclient/issues/detail?id=4374 R=kschimpf@google.com Review URL: https://codereview.chromium.org/2211733002 .
parent 59ce6153
......@@ -44,7 +44,15 @@
#define SHADOW2MEM(p) \
((uintptr_t)((char *)(p)-shadow_offset) << SHADOW_SCALE_LOG2)
#define POISON_VAL (-1)
#define STACK_POISON_VAL ((char)-1)
#define HEAP_POISON_VAL ((char)-2)
#define GLOBAL_POISON_VAL ((char)-3)
#define MEMTYPE_INDEX(x) (-1 - (x))
static const char *memtype_names[] = {"stack", "heap", "global"};
#define ACCESS_LOAD (0)
#define ACCESS_STORE (1)
static const char *access_names[] = {"load from", "store to"};
#if DEBUG
#define DUMP(args...) \
......@@ -57,8 +65,8 @@
static char *shadow_offset = NULL;
static void __asan_error(char *, int);
static void __asan_check(char *, int);
static bool __asan_check(char *, int);
static void __asan_error(char *, int, int);
static void __asan_get_redzones(char *, char **, char **);
void __asan_init(int, void **, int *);
......@@ -68,27 +76,35 @@ void *__asan_malloc(size_t);
void *__asan_calloc(size_t, size_t);
void *__asan_realloc(char *, size_t);
void __asan_free(char *);
void __asan_poison(char *, int);
void __asan_poison(char *, int, char);
void __asan_unpoison(char *, int);
static void __asan_error(char *ptr, int size) {
fprintf(stderr, "Illegal access of %d bytes at %p\n", size, ptr);
static void __asan_error(char *ptr, int size, int access) {
char *shadow_addr = MEM2SHADOW(ptr);
char shadow_val = *shadow_addr;
if (shadow_val > 0)
shadow_val = *(shadow_addr + 1);
assert(access == ACCESS_LOAD || access == ACCESS_STORE);
const char *access_name = access_names[access];
assert(shadow_val == STACK_POISON_VAL || shadow_val == HEAP_POISON_VAL ||
shadow_val == GLOBAL_POISON_VAL);
const char *memtype = memtype_names[MEMTYPE_INDEX(shadow_val)];
fprintf(stderr, "Illegal %d byte %s %s object at %p\n", size, access_name,
memtype, ptr);
abort();
}
// check only the first byte of each word unless strict
static void __asan_check(char *ptr, int size) {
static bool __asan_check(char *ptr, int size) {
assert(size == 1 || size == 2 || size == 4 || size == 8);
char *shadow_addr = (char *)MEM2SHADOW(ptr);
char shadow_val = *shadow_addr;
DUMP("check %d bytes at %p: %p + %d (%d)\n", size, ptr, shadow_addr,
(uintptr_t)ptr % SHADOW_SCALE, *shadow_addr);
(uintptr_t)ptr % SHADOW_SCALE, shadow_val);
if (size == SHADOW_SCALE) {
if (*shadow_addr != 0)
__asan_error(ptr, size);
return;
return shadow_val == 0;
}
if (*shadow_addr != 0 && (char)SHADOW_OFFSET(ptr) + size > *shadow_addr)
__asan_error(ptr, size);
return shadow_val == 0 || (char)SHADOW_OFFSET(ptr) + size <= shadow_val;
}
static void __asan_get_redzones(char *ptr, char **left, char **right) {
......@@ -103,15 +119,16 @@ static void __asan_get_redzones(char *ptr, char **left, char **right) {
void __asan_check_load(char *ptr, int size) {
// aligned single word accesses may be widened single byte accesses, but for
// all else use strict check
if (size == WORD_SIZE && (uintptr_t)ptr % WORD_SIZE == 0)
size = 1;
__asan_check(ptr, size);
int check_size =
(size == WORD_SIZE && (uintptr_t)ptr % WORD_SIZE == 0) ? 1 : size;
if (!__asan_check(ptr, check_size))
__asan_error(ptr, size, ACCESS_LOAD);
}
void __asan_check_store(char *ptr, int size) {
// stores may never be partially out of bounds so use strict check
bool strict = true;
__asan_check(ptr, size);
if (!__asan_check(ptr, size))
__asan_error(ptr, size, ACCESS_STORE);
}
void __asan_init(int n_rzs, void **rzs, int *rz_sizes) {
......@@ -138,7 +155,7 @@ void __asan_init(int n_rzs, void **rzs, int *rz_sizes) {
DUMP("poisioning %d global redzones\n", n_rzs);
for (int i = 0; i < n_rzs; i++) {
DUMP("(%d) poisoning redzone of size %d at %p\n", i, rz_sizes[i], rzs[i]);
__asan_poison(rzs[i], rz_sizes[i]);
__asan_poison(rzs[i], rz_sizes[i], GLOBAL_POISON_VAL);
}
}
......@@ -157,8 +174,8 @@ void *__asan_malloc(size_t size) {
}
void *ret = rz_left + rz_left_size;
void *rz_right = ret + size;
__asan_poison(rz_left, rz_left_size);
__asan_poison(rz_right, rz_right_size);
__asan_poison(rz_left, rz_left_size, HEAP_POISON_VAL);
__asan_poison(rz_right, rz_right_size, HEAP_POISON_VAL);
// record size and location data so we can find it again
*(void **)rz_left = rz_right;
*(size_t *)rz_right = rz_right_size;
......@@ -204,7 +221,7 @@ void __asan_free(char *ptr) {
free(rz_left);
}
void __asan_poison(char *ptr, int size) {
void __asan_poison(char *ptr, int size, char poison_val) {
char *end = ptr + size;
assert(IS_SHADOW_ALIGNED(end));
// redzones should be no greater than RZ_SIZE + RZ_SIZE-1 for alignment
......@@ -212,12 +229,11 @@ void __asan_poison(char *ptr, int size) {
DUMP("poison %d bytes at %p: %p - %p\n", size, ptr, MEM2SHADOW(ptr),
MEM2SHADOW(end));
size_t offset = SHADOW_OFFSET(ptr);
*(char *)MEM2SHADOW(ptr) = (offset == 0) ? POISON_VAL : offset;
*(char *)MEM2SHADOW(ptr) = (offset == 0) ? poison_val : offset;
ptr += SHADOW_OFFSET(size);
assert(IS_SHADOW_ALIGNED(ptr));
for (; ptr != end; ptr += SHADOW_SCALE) {
*(char *)MEM2SHADOW(ptr) = POISON_VAL;
}
int len = (end - ptr) >> SHADOW_SCALE_LOG2;
memset(MEM2SHADOW(ptr), poison_val, len);
}
void __asan_unpoison(char *ptr, int size) {
......@@ -229,7 +245,5 @@ void __asan_unpoison(char *ptr, int size) {
*(char *)MEM2SHADOW(ptr) = 0;
ptr += SHADOW_OFFSET(size);
assert(IS_SHADOW_ALIGNED(ptr));
for (; ptr != end; ptr += SHADOW_SCALE) {
*(char *)MEM2SHADOW(ptr) = 0;
}
memset(MEM2SHADOW(ptr), 0, (end - ptr) >> SHADOW_SCALE_LOG2);
}
......@@ -36,6 +36,7 @@ constexpr SizeT RzSize = 32;
constexpr const char *RzPrefix = "__$rz";
constexpr const char *RzArrayName = "__$rz_array";
constexpr const char *RzSizesName = "__$rz_sizes";
constexpr char RzStackPoison = -1;
const llvm::NaClBitcodeRecord::RecordVector RzContents =
llvm::NaClBitcodeRecord::RecordVector(RzSize, 'R');
......@@ -202,15 +203,17 @@ void ASanInstrumentation::instrumentFuncStart(LoweringContext &Context) {
constexpr SizeT NumArgs = 2;
constexpr Variable *Void = nullptr;
constexpr bool NoTailcall = false;
auto *RzSizeConst = ConstantInteger32::create(Ctx, IceType_i32, RzPadding);
auto *RzPoisonConst =
ConstantInteger32::create(Ctx, IceType_i32, RzStackPoison);
auto *Init = InstCall::create(Func, NumArgs, Void, InitFunc, NoTailcall);
Init->addArg(RzLocVar);
Init->addArg(RzSizeConst);
Init->addArg(RzPoisonConst);
auto *Destroy =
InstCall::create(Func, NumArgs, Void, DestroyFunc, NoTailcall);
Init->addArg(RzLocVar);
Destroy->addArg(RzLocVar);
auto *RzSizeConst = ConstantInteger32::create(Ctx, IceType_i32, RzPadding);
Init->addArg(RzSizeConst);
Destroy->addArg(RzSizeConst);
Cur->setDeleted();
C.insert(NewVar);
ICE_TLS_GET_FIELD(LocalDtors)->emplace_back(Destroy);
......@@ -234,14 +237,16 @@ void ASanInstrumentation::instrumentFuncStart(LoweringContext &Context) {
constexpr SizeT NumArgs = 2;
constexpr Variable *Void = nullptr;
constexpr bool NoTailcall = false;
auto *RzPoisonConst =
ConstantInteger32::create(Ctx, IceType_i32, RzStackPoison);
auto *Init = InstCall::create(Func, NumArgs, Void, InitFunc, NoTailcall);
Init->addArg(LastRz);
Init->addArg(RzAlloca->getSizeInBytes());
Init->addArg(RzPoisonConst);
auto *Destroy =
InstCall::create(Func, NumArgs, Void, DestroyFunc, NoTailcall);
Init->addArg(LastRz);
Destroy->addArg(LastRz);
Init->addArg(RzAlloca->getSizeInBytes());
Destroy->addArg(RzAlloca->getSizeInBytes());
ICE_TLS_GET_FIELD(LocalDtors)->emplace_back(Destroy);
C.insert(RzAlloca);
C.insert(Init);
......
......@@ -17,14 +17,17 @@ define internal i32 @func(i32 %arg1, i32 %arg2) {
; CHECK-NEXT: lea eax,[esp+0x10]
; CHECK-NEXT: mov DWORD PTR [esp],eax
; CHECK-NEXT: mov DWORD PTR [esp+0x4],0x20
; CHECK-NEXT: mov DWORD PTR [esp+0x8],0xffffffff
; CHECK-NEXT: __asan_poison
; CHECK-NEXT: lea eax,[esp+0x74]
; CHECK-NEXT: mov DWORD PTR [esp],eax
; CHECK-NEXT: mov DWORD PTR [esp+0x4],0x3c
; CHECK-NEXT: mov DWORD PTR [esp+0x8],0xffffffff
; CHECK-NEXT: __asan_poison
; CHECK-NEXT: lea eax,[esp+0x35]
; CHECK-NEXT: mov DWORD PTR [esp],eax
; CHECK-NEXT: mov DWORD PTR [esp+0x4],0x3b
; CHECK-NEXT: mov DWORD PTR [esp+0x8],0xffffffff
; CHECK-NEXT: __asan_poison
; CHECK-NEXT: lea eax,[esp+0x74]
; CHECK-NEXT: mov DWORD PTR [esp],eax
......
......@@ -38,9 +38,9 @@ define internal void @func() {
; DUMP-NEXT: __0:
; DUMP-NEXT: %local = alloca i8, i32 64, align 8
; DUMP-NEXT: %__$rz1 = alloca i8, i32 32, align 8
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz1, i32 32)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz1, i32 32, i32 -1)
; DUMP-NEXT: %__$rz0 = add i32 %local, 4
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz0, i32 60)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz0, i32 60, i32 -1)
; DUMP-NEXT: %heapvar = call i32 @__asan_malloc(i32 42)
; DUMP-NEXT: call void @__asan_free(i32 %heapvar)
; DUMP-NEXT: call void @__asan_unpoison(i32 %__$rz0, i32 60)
......
......@@ -2,43 +2,81 @@
; REQUIRES: no_minimal_build
; check with a one off the end local access
; check with a one off the end local load
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 2>&1 | FileCheck %s
; RUN: %t.pexe -o %t && %t 2>&1 | FileCheck --check-prefix=LOCAL-LOAD %s
; check with a many off the end local access
; check with a many off the end local load
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2>&1 | FileCheck %s
; RUN: %t.pexe -o %t && %t 1 2>&1 | FileCheck --check-prefix=LOCAL-LOAD %s
; check with a one before the front local access
; check with a one before the front local load
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 2>&1 | FileCheck %s
; RUN: %t.pexe -o %t && %t 1 2 2>&1 | FileCheck --check-prefix=LOCAL-LOAD %s
; check with a one off the end global access
; check with a one off the end global load
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 2>&1 | FileCheck %s
; RUN: %t.pexe -o %t && %t 1 2 3 2>&1 | FileCheck \
; RUN: --check-prefix=GLOBAL-LOAD %s
; check with a many off the end global access
; check with a many off the end global load
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 4 2>&1 | FileCheck %s
; RUN: %t.pexe -o %t && %t 1 2 3 4 2>&1 | FileCheck \
; RUN: --check-prefix=GLOBAL-LOAD %s
; check with a one before the front global access
; check with a one before the front global load
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 4 5 2>&1 | FileCheck %s
; RUN: %t.pexe -o %t && %t 1 2 3 4 5 2>&1 | FileCheck \
; RUN: --check-prefix=GLOBAL-LOAD %s
; check with a one off the end local store
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 4 5 6 2>&1 | FileCheck \
; RUN: --check-prefix=LOCAL-STORE %s
; check with a many off the end local store
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 4 5 6 7 2>&1 | FileCheck \
; RUN: --check-prefix=LOCAL-STORE %s
; check with a one before the front local store
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 4 5 6 7 8 2>&1 | FileCheck \
; RUN: --check-prefix=LOCAL-STORE %s
; check with a one off the end global store
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 4 5 6 7 8 9 2>&1 | FileCheck \
; RUN: --check-prefix=GLOBAL-STORE %s
; check with a many off the end global store
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 4 5 6 7 8 9 10 2>&1 | FileCheck \
; RUN: --check-prefix=GLOBAL-STORE %s
; check with a one before the front global store
; RUN: llvm-as %s -o - | pnacl-freeze > %t.pexe && %S/../../pydir/szbuild.py \
; RUN: --fsanitize-address --sz=-allow-externally-defined-symbols \
; RUN: %t.pexe -o %t && %t 1 2 3 4 5 6 7 8 9 10 11 2>&1 | FileCheck \
; RUN: --check-prefix=GLOBAL-STORE %s
declare external void @exit(i32)
; A global array
@array = internal constant [12 x i8] zeroinitializer
define void @access(i32 %is_local_i, i32 %err) {
define void @access(i32 %is_local_i, i32 %is_load_i, i32 %err) {
; get the base pointer to either the local or global array
%local = alloca i8, i32 12, align 1
%global = bitcast [12 x i8]* @array to i8*
......@@ -56,9 +94,17 @@ define void @access(i32 %is_local_i, i32 %err) {
%badaddr = add i32 %arraddr, %offset
%badptr = inttoptr i32 %badaddr to i8*
; perform the bad access
; determine load or store
%is_load = icmp ne i32 %is_load_i, 0
br i1 %is_load, label %bad_load, label %bad_store
bad_load:
%result = load i8, i8* %badptr, align 1
ret void
bad_store:
store i8 42, i8* %badptr, align 1
ret void
}
; use argc to determine which test routine to run
......@@ -66,39 +112,72 @@ define void @_start(i32 %arg) {
%argcaddr = add i32 %arg, 8
%argcptr = inttoptr i32 %argcaddr to i32*
%argc = load i32, i32* %argcptr, align 1
switch i32 %argc, label %error [i32 1, label %one_local
i32 2, label %many_local
i32 3, label %neg_local
i32 4, label %one_global
i32 5, label %many_global
i32 6, label %neg_global]
one_local:
switch i32 %argc, label %error [i32 1, label %one_local_load
i32 2, label %many_local_load
i32 3, label %neg_local_load
i32 4, label %one_global_load
i32 5, label %many_global_load
i32 6, label %neg_global_load
i32 7, label %one_local_store
i32 8, label %many_local_store
i32 9, label %neg_local_store
i32 10, label %one_global_store
i32 11, label %many_global_store
i32 12, label %neg_global_store]
one_local_load:
; Access one past the end of a local
call void @access(i32 1, i32 1, i32 0)
br label %error
many_local_load:
; Access five past the end of a local
call void @access(i32 1, i32 1, i32 4)
br label %error
neg_local_load:
; Access one before the beginning of a local
call void @access(i32 1, i32 1, i32 -1)
br label %error
one_global_load:
; Access one past the end of a global
call void @access(i32 0, i32 1, i32 0)
br label %error
many_global_load:
; Access five past the end of a global
call void @access(i32 0, i32 1, i32 4)
br label %error
neg_global_load:
; Access one before the beginning of a global
call void @access(i32 0, i32 1, i32 -1)
br label %error
one_local_store:
; Access one past the end of a local
call void @access(i32 1, i32 0)
call void @access(i32 1, i32 0, i32 0)
br label %error
many_local:
many_local_store:
; Access five past the end of a local
call void @access(i32 1, i32 4)
call void @access(i32 1, i32 0, i32 4)
br label %error
neg_local:
neg_local_store:
; Access one before the beginning of a local
call void @access(i32 1, i32 -1)
call void @access(i32 1, i32 0, i32 -1)
br label %error
one_global:
one_global_store:
; Access one past the end of a global
call void @access(i32 0, i32 0)
call void @access(i32 0, i32 0, i32 0)
br label %error
many_global:
many_global_store:
; Access five past the end of a global
call void @access(i32 0, i32 4)
call void @access(i32 0, i32 0, i32 4)
br label %error
neg_global:
neg_global_store:
; Access one before the beginning of a global
call void @access(i32 0, i32 -1)
call void @access(i32 0, i32 0, i32 -1)
br label %error
error:
call void @exit(i32 1)
unreachable
}
; CHECK: Illegal access of 1 bytes at
; LOCAL-LOAD: Illegal 1 byte load from stack object at
; LOCAL-STORE: Illegal 1 byte store to stack object at
; GLOBAL-LOAD: Illegal 1 byte load from global object at
; GLOBAL-STORE: Illegal 1 byte store to global object at
......@@ -24,17 +24,17 @@ define internal void @func() {
; DUMP-NEXT: %local4 = alloca i8, i32 128, align 8
; DUMP-NEXT: %local5 = alloca i8, i32 96, align 8
; DUMP-NEXT: %__$rz[[RZ0:[0-9]+]] = alloca i8, i32 32, align 8
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ0]], i32 32)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ0]], i32 32, i32 -1)
; DUMP-NEXT: %__$rz[[RZ1:[0-9]+]] = add i32 %local1, 4
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ1]], i32 60)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ1]], i32 60, i32 -1)
; DUMP-NEXT: %__$rz[[RZ2:[0-9]+]] = add i32 %local2, 32
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ2]], i32 32)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ2]], i32 32, i32 -1)
; DUMP-NEXT: %__$rz[[RZ3:[0-9]+]] = add i32 %local3, 13
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ3]], i32 51)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ3]], i32 51, i32 -1)
; DUMP-NEXT: %__$rz[[RZ4:[0-9]+]] = add i32 %local4, 75
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ4]], i32 53)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ4]], i32 53, i32 -1)
; DUMP-NEXT: %__$rz[[RZ5:[0-9]+]] = add i32 %local5, 64
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ5]], i32 32)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz[[RZ5]], i32 32, i32 -1)
; DUMP-NEXT: call void @__asan_unpoison(i32 %__$rz[[RZ1]], i32 60)
; DUMP-NEXT: call void @__asan_unpoison(i32 %__$rz[[RZ2]], i32 32)
; DUMP-NEXT: call void @__asan_unpoison(i32 %__$rz[[RZ3]], i32 51)
......
......@@ -22,11 +22,11 @@ no:
; DUMP-NEXT: %local1 = alloca i8, i32 64, align 8
; DUMP-NEXT: %local2 = alloca i8, i32 64, align 8
; DUMP-NEXT: %__$rz2 = alloca i8, i32 32, align 8
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz2, i32 32)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz2, i32 32, i32 -1)
; DUMP-NEXT: %__$rz0 = add i32 %local1, 4
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz0, i32 60)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz0, i32 60, i32 -1)
; DUMP-NEXT: %__$rz1 = add i32 %local2, 4
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz1, i32 60)
; DUMP-NEXT: call void @__asan_poison(i32 %__$rz1, i32 60, i32 -1)
; DUMP-NEXT: %cond = icmp ne i32 %condarg, 0
; DUMP-NEXT: br i1 %cond, label %yes, label %no
; DUMP-NEXT: yes:
......
......@@ -23,4 +23,4 @@ define void @_start(i32 %arg) {
ret void
}
; CHECK: Illegal access of 1 bytes at
; CHECK: Illegal 1 byte load from heap object at
......@@ -33,8 +33,8 @@ define internal void @no_wide_load() {
unreachable
}
; WIDE-NOT: Illegal access
; NOWIDE: Illegal access of 1 bytes at
; WIDE-NOT: Illegal
; NOWIDE: Illegal 1 byte load from stack object at
; use argc to determine which test routine to run
define void @_start(i32 %arg) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment