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