diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -42,6 +42,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Module.h" +#include "llvm/IR/NoFolder.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" @@ -579,7 +580,8 @@ UseShortGranules = ClUseShortGranules.getNumOccurrences() ? ClUseShortGranules : NewRuntime; OutlinedChecks = - TargetTriple.isAArch64() && TargetTriple.isOSBinFormatELF() && + (TargetTriple.isAArch64() || TargetTriple.isRISCV64()) && + TargetTriple.isOSBinFormatELF() && (ClInlineAllChecks.getNumOccurrences() ? !ClInlineAllChecks : !Recover); if (ClMatchAllTag.getNumOccurrences()) { @@ -599,10 +601,15 @@ if (!CompileKernel) { createHwasanCtorComdat(); + // Currently we do not instrumentation of globals for RISCV + // The reason is that the existing memory models does not allow us + // to use tagged pointers in la/lla expressions bool InstrumentGlobals = - ClGlobals.getNumOccurrences() ? ClGlobals : NewRuntime; + ClGlobals.getNumOccurrences() + ? ClGlobals + : (NewRuntime && !UsePageAliases && !TargetTriple.isRISCV64()); - if (InstrumentGlobals && !UsePageAliases) + if (InstrumentGlobals) instrumentGlobals(); bool InstrumentPersonalityFunctions = @@ -791,7 +798,8 @@ } void HWAddressSanitizer::untagPointerOperand(Instruction *I, Value *Addr) { - if (TargetTriple.isAArch64() || TargetTriple.getArch() == Triple::x86_64) + if (TargetTriple.isAArch64() || TargetTriple.getArch() == Triple::x86_64 || + TargetTriple.isRISCV64()) return; IRBuilder<> IRB(I); @@ -909,6 +917,13 @@ "{x0}", /*hasSideEffects=*/true); break; + case Triple::riscv64: + // The signal handler will find the data address in x10. + Asm = InlineAsm::get( + FunctionType::get(IRB.getVoidTy(), {PtrLong->getType()}, false), + "ebreak\naddiw x0, x0, " + itostr(0x40 + AccessInfo), "{x10}", "{x11}", + /*hasSideEffects=*/true); + break; default: report_fatal_error("unsupported architecture"); } diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/alloca-with-calls.ll b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/alloca-with-calls.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/alloca-with-calls.ll @@ -0,0 +1,23 @@ +; Test alloca instrumentation when tags are generated by HWASan function. +; +; RUN: opt < %s -passes=hwasan -hwasan-generate-tags-with-calls -S | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-linux" + +declare void @use32(i32*) + +define void @test_alloca() sanitize_hwaddress { +; CHECK-LABEL: @test_alloca( +; CHECK: %[[BC:[^ ]*]] = bitcast { i32, [12 x i8] }* %x to i32* +; CHECK: %[[T1:[^ ]*]] = call i8 @__hwasan_generate_tag() +; CHECK: %[[A:[^ ]*]] = zext i8 %[[T1]] to i64 +; CHECK: %[[B:[^ ]*]] = ptrtoint i32* %[[BC]] to i64 +; CHECK: %[[C:[^ ]*]] = shl i64 %[[A]], 56 +; CHECK: or i64 %[[B]], %[[C]] + +entry: + %x = alloca i32, align 4 + call void @use32(i32* nonnull %x) + ret void +} diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/alloca.ll b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/alloca.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/alloca.ll @@ -0,0 +1,80 @@ +; Test alloca instrumentation. +; +; RUN: opt < %s -passes=hwasan -hwasan-with-ifunc=1 -S | FileCheck %s --check-prefixes=CHECK,DYNAMIC-SHADOW,NO-UAR-TAGS +; RUN: opt < %s -passes=hwasan -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,ZERO-BASED-SHADOW,NO-UAR-TAGS +; RUN: opt < %s -passes=hwasan -hwasan-with-ifunc=1 -hwasan-uar-retag-to-zero=0 -S | FileCheck %s --check-prefixes=CHECK,DYNAMIC-SHADOW,UAR-TAGS + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-linux" + +declare void @use32(i32*) + +define void @test_alloca() sanitize_hwaddress !dbg !15 { +; CHECK-LABEL: @test_alloca( +; CHECK: %[[FP:[^ ]*]] = call i8* @llvm.frameaddress.p0i8(i32 0) +; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %[[FP]] to i64 +; CHECK: %[[B:[^ ]*]] = lshr i64 %[[A]], 20 +; CHECK: %[[BASE_TAG:[^ ]*]] = xor i64 %[[A]], %[[B]] + +; CHECK: %[[X:[^ ]*]] = alloca { i32, [12 x i8] }, align 16 +; CHECK: %[[X_BC:[^ ]*]] = bitcast { i32, [12 x i8] }* %[[X]] to i32* +; CHECK: %[[X_TAG:[^ ]*]] = xor i64 %[[BASE_TAG]], 0 +; CHECK: %[[X1:[^ ]*]] = ptrtoint i32* %[[X_BC]] to i64 +; CHECK: %[[C:[^ ]*]] = shl i64 %[[X_TAG]], 56 +; CHECK: %[[D:[^ ]*]] = or i64 %[[X1]], %[[C]] +; CHECK: %[[X_HWASAN:[^ ]*]] = inttoptr i64 %[[D]] to i32* + +; CHECK: %[[X_TAG2:[^ ]*]] = trunc i64 %[[X_TAG]] to i8 +; CHECK: %[[E:[^ ]*]] = ptrtoint i32* %[[X_BC]] to i64 +; CHECK: %[[F:[^ ]*]] = lshr i64 %[[E]], 4 +; DYNAMIC-SHADOW: %[[X_SHADOW:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %[[F]] +; ZERO-BASED-SHADOW: %[[X_SHADOW:[^ ]*]] = inttoptr i64 %[[F]] to i8* +; CHECK: %[[X_SHADOW_GEP:[^ ]*]] = getelementptr i8, i8* %[[X_SHADOW]], i32 0 +; CHECK: store i8 4, i8* %[[X_SHADOW_GEP]] +; CHECK: %[[X_I8:[^ ]*]] = bitcast i32* %[[X_BC]] to i8* +; CHECK: %[[X_I8_GEP:[^ ]*]] = getelementptr i8, i8* %[[X_I8]], i32 15 +; CHECK: store i8 %[[X_TAG2]], i8* %[[X_I8_GEP]] +; CHECK: call void @llvm.dbg.value( +; CHECK-SAME: metadata !DIArgList(i32* %[[X_BC]], i32* %[[X_BC]]) +; CHECK-SAME: metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_tag_offset, 0, DW_OP_LLVM_arg, 1, DW_OP_LLVM_tag_offset, 0, +; CHECK: call void @use32(i32* nonnull %[[X_HWASAN]]) + +; UAR-TAGS: %[[BASE_TAG_COMPL:[^ ]*]] = xor i64 %[[BASE_TAG]], 255 +; UAR-TAGS: %[[X_TAG_UAR:[^ ]*]] = trunc i64 %[[BASE_TAG_COMPL]] to i8 +; CHECK: %[[E2:[^ ]*]] = ptrtoint i32* %[[X_BC]] to i64 +; CHECK: %[[F2:[^ ]*]] = lshr i64 %[[E2]], 4 +; DYNAMIC-SHADOW: %[[X_SHADOW2:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %[[F2]] +; ZERO-BASED-SHADOW: %[[X_SHADOW2:[^ ]*]] = inttoptr i64 %[[F2]] to i8* +; NO-UAR-TAGS: call void @llvm.memset.p0i8.i64(i8* align 1 %[[X_SHADOW2]], i8 0, i64 1, i1 false) +; UAR-TAGS: call void @llvm.memset.p0i8.i64(i8* align 1 %[[X_SHADOW2]], i8 %[[X_TAG_UAR]], i64 1, i1 false) +; CHECK: ret void + + +entry: + %x = alloca i32, align 4 + call void @llvm.dbg.value(metadata !DIArgList(i32* %x, i32* %x), metadata !22, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_deref)), !dbg !21 + call void @use32(i32* nonnull %x), !dbg !23 + ret void, !dbg !24 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!llvm.ident = !{!14} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 13.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "alloca.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!14 = !{!"clang version 13.0.0"} +!15 = distinct !DISubprogram(name: "test_alloca", linkageName: "_Z11test_allocav", scope: !1, file: !1, line: 4, type: !16, scopeLine: 4, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!16 = !DISubroutineType(types: !17) +!17 = !{null} +!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 64) +!20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!21 = !DILocation(line: 0, scope: !15) +!22 = !DILocalVariable(name: "x", scope: !15, file: !1, line: 5, type: !20) +!23 = !DILocation(line: 7, column: 5, scope: !15) +!24 = !DILocation(line: 8, column: 1, scope: !15) diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/atomic.ll b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/atomic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/atomic.ll @@ -0,0 +1,30 @@ +; Test basic address sanitizer instrumentation. +; +; RUN: opt < %s -passes=hwasan -S | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-linux" + +define void @atomicrmw(i64* %ptr) sanitize_hwaddress { +; CHECK-LABEL: @atomicrmw( +; CHECK: [[PTRI8:%[^ ]*]] = bitcast i64* %ptr to i8* +; CHECK: call void @llvm.hwasan.check.memaccess.shortgranules({{.*}}, i8* [[PTRI8]], i32 19) +; CHECK: atomicrmw add i64* %ptr, i64 1 seq_cst +; CHECK: ret void + +entry: + %0 = atomicrmw add i64* %ptr, i64 1 seq_cst + ret void +} + +define void @cmpxchg(i64* %ptr, i64 %compare_to, i64 %new_value) sanitize_hwaddress { +; CHECK-LABEL: @cmpxchg( +; CHECK: [[PTRI8:%[^ ]*]] = bitcast i64* %ptr to i8* +; CHECK: call void @llvm.hwasan.check.memaccess.shortgranules({{.*}}, i8* [[PTRI8]], i32 19) +; CHECK: cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst +; CHECK: ret void + +entry: + %0 = cmpxchg i64* %ptr, i64 %compare_to, i64 %new_value seq_cst seq_cst + ret void +} diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/basic.ll b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/basic.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/basic.ll @@ -0,0 +1,409 @@ +; Test basic address sanitizer instrumentation. +; +; RUN: opt < %s -passes=hwasan -hwasan-recover=0 -hwasan-with-ifunc=1 -hwasan-with-tls=0 -S | FileCheck %s --check-prefixes=CHECK,ABORT +; RUN: opt < %s -passes=hwasan -hwasan-recover=1 -hwasan-with-ifunc=1 -hwasan-with-tls=0 -S | FileCheck %s --check-prefixes=CHECK,RECOVER,RECOVER-DYNAMIC-SHADOW +; RUN: opt < %s -passes=hwasan -hwasan-recover=0 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,ABORT +; RUN: opt < %s -passes=hwasan -hwasan-recover=1 -hwasan-mapping-offset=0 -S | FileCheck %s --check-prefixes=CHECK,RECOVER,RECOVER-ZERO-BASED-SHADOW + +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void ()* @hwasan.module_ctor to i8*)] +; CHECK: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @hwasan.module_ctor, i8* bitcast (void ()* @hwasan.module_ctor to i8*) }] + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-linux" + +define i8 @test_load8(i8* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load8( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i8* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label %[[MISMATCH:[0-9]*]], label %[[CONT:[0-9]*]], !prof {{.*}} + +; RECOVER: [[MISMATCH]]: +; RECOVER: %[[NOTSHORT:[^ ]*]] = icmp ugt i8 %[[MEMTAG]], 15 +; RECOVER: br i1 %[[NOTSHORT]], label %[[FAIL:[0-9]*]], label %[[SHORT:[0-9]*]], !prof {{.*}} + +; RECOVER: [[FAIL]]: +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 96", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; RECOVER: [[SHORT]]: +; RECOVER: %[[LOWBITS:[^ ]*]] = and i64 %[[A]], 15 +; RECOVER: %[[LOWBITS_I8:[^ ]*]] = trunc i64 %[[LOWBITS]] to i8 +; RECOVER: %[[LAST:[^ ]*]] = add i8 %[[LOWBITS_I8]], 0 +; RECOVER: %[[OOB:[^ ]*]] = icmp uge i8 %[[LAST]], %[[MEMTAG]] +; RECOVER: br i1 %[[OOB]], label %[[FAIL]], label %[[INBOUNDS:[0-9]*]], !prof {{.*}} + +; RECOVER: [[INBOUNDS]]: +; RECOVER: %[[EOG_ADDR:[^ ]*]] = or i64 %[[C]], 15 +; RECOVER: %[[EOG_PTR:[^ ]*]] = inttoptr i64 %[[EOG_ADDR]] to i8* +; RECOVER: %[[EOGTAG:[^ ]*]] = load i8, i8* %[[EOG_PTR]] +; RECOVER: %[[EOG_MISMATCH:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[EOGTAG]] +; RECOVER: br i1 %[[EOG_MISMATCH]], label %[[FAIL]], label %[[CONT1:[0-9]*]], !prof {{.*}} + +; RECOVER: [[CONT1]]: +; RECOVER: br label %[[CONT]] + +; RECOVER: [[CONT]]: + +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %a, i32 0) + +; CHECK: %[[G:[^ ]*]] = load i8, i8* %a, align 4 +; CHECK: ret i8 %[[G]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +define i16 @test_load16(i16* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load16( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i16* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label %[[MISMATCH:[0-9]*]], label %[[CONT:[0-9]*]], !prof {{.*}} + +; RECOVER: [[MISMATCH]]: +; RECOVER: %[[NOTSHORT:[^ ]*]] = icmp ugt i8 %[[MEMTAG]], 15 +; RECOVER: br i1 %[[NOTSHORT]], label %[[FAIL:[0-9]*]], label %[[SHORT:[0-9]*]], !prof {{.*}} + +; RECOVER: [[FAIL]]: +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 97", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; RECOVER: [[SHORT]]: +; RECOVER: %[[LOWBITS:[^ ]*]] = and i64 %[[A]], 15 +; RECOVER: %[[LOWBITS_I8:[^ ]*]] = trunc i64 %[[LOWBITS]] to i8 +; RECOVER: %[[LAST:[^ ]*]] = add i8 %[[LOWBITS_I8]], 1 +; RECOVER: %[[OOB:[^ ]*]] = icmp uge i8 %[[LAST]], %[[MEMTAG]] +; RECOVER: br i1 %[[OOB]], label %[[FAIL]], label %[[INBOUNDS:[0-9]*]], !prof {{.*}} + +; RECOVER: [[INBOUNDS]]: +; RECOVER: %[[EOG_ADDR:[^ ]*]] = or i64 %[[C]], 15 +; RECOVER: %[[EOG_PTR:[^ ]*]] = inttoptr i64 %[[EOG_ADDR]] to i8* +; RECOVER: %[[EOGTAG:[^ ]*]] = load i8, i8* %[[EOG_PTR]] +; RECOVER: %[[EOG_MISMATCH:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[EOGTAG]] +; RECOVER: br i1 %[[EOG_MISMATCH]], label %[[FAIL]], label %[[CONT1:[0-9]*]], !prof {{.*}} + +; RECOVER: [[CONT1]]: +; RECOVER: br label %[[CONT]] + +; RECOVER: [[CONT]]: + +; ABORT: %[[A:[^ ]*]] = bitcast i16* %a to i8* +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %[[A]], i32 1) + +; CHECK: %[[G:[^ ]*]] = load i16, i16* %a, align 4 +; CHECK: ret i16 %[[G]] + +entry: + %b = load i16, i16* %a, align 4 + ret i16 %b +} + +define i32 @test_load32(i32* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load32( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i32* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}} + +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 98", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; ABORT: %[[A:[^ ]*]] = bitcast i32* %a to i8* +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %[[A]], i32 2) + +; CHECK: %[[G:[^ ]*]] = load i32, i32* %a, align 4 +; CHECK: ret i32 %[[G]] + +entry: + %b = load i32, i32* %a, align 4 + ret i32 %b +} + +define i64 @test_load64(i64* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load64( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i64* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}} + +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 99", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; ABORT: %[[A:[^ ]*]] = bitcast i64* %a to i8* +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %[[A]], i32 3) + +; CHECK: %[[G:[^ ]*]] = load i64, i64* %a, align 8 +; CHECK: ret i64 %[[G]] + +entry: + %b = load i64, i64* %a, align 8 + ret i64 %b +} + +define i128 @test_load128(i128* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load128( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i128* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}} + +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 100", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; ABORT: %[[A:[^ ]*]] = bitcast i128* %a to i8* +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %[[A]], i32 4) + +; CHECK: %[[G:[^ ]*]] = load i128, i128* %a, align 16 +; CHECK: ret i128 %[[G]] + +entry: + %b = load i128, i128* %a, align 16 + ret i128 %b +} + +define i40 @test_load40(i40* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load40( +; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64 +; 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: ret i40 %[[B]] + +entry: + %b = load i40, i40* %a, align 4 + ret i40 %b +} + +define void @test_store8(i8* %a, i8 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store8( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i8* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}} + +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 112", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %a, i32 16) + +; CHECK: store i8 %b, i8* %a, align 4 +; CHECK: ret void + +entry: + store i8 %b, i8* %a, align 4 + ret void +} + +define void @test_store16(i16* %a, i16 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store16( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i16* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}} + +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 113", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; ABORT: %[[A:[^ ]*]] = bitcast i16* %a to i8* +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %[[A]], i32 17) + +; CHECK: store i16 %b, i16* %a, align 4 +; CHECK: ret void + +entry: + store i16 %b, i16* %a, align 4 + ret void +} + +define void @test_store32(i32* %a, i32 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store32( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i32* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}} + +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 114", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; ABORT: %[[A:[^ ]*]] = bitcast i32* %a to i8* +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %[[A]], i32 18) + +; CHECK: store i32 %b, i32* %a, align 4 +; CHECK: ret void + +entry: + store i32 %b, i32* %a, align 4 + ret void +} + +define void @test_store64(i64* %a, i64 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store64( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i64* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}} + +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 115", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; ABORT: %[[A:[^ ]*]] = bitcast i64* %a to i8* +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %[[A]], i32 19) + +; CHECK: store i64 %b, i64* %a, align 8 +; CHECK: ret void + +entry: + store i64 %b, i64* %a, align 8 + ret void +} + +define void @test_store128(i128* %a, i128 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store128( +; RECOVER: %[[A:[^ ]*]] = ptrtoint i128* %a to i64 +; RECOVER: %[[B:[^ ]*]] = lshr i64 %[[A]], 56 +; RECOVER: %[[PTRTAG:[^ ]*]] = trunc i64 %[[B]] to i8 +; RECOVER: %[[C:[^ ]*]] = and i64 %[[A]], 72057594037927935 +; RECOVER: %[[D:[^ ]*]] = lshr i64 %[[C]], 4 +; RECOVER-DYNAMIC-SHADOW: %[[E:[^ ]*]] = getelementptr i8, i8* %.hwasan.shadow, i64 %4 +; RECOVER-ZERO-BASED-SHADOW: %[[E:[^ ]*]] = inttoptr i64 %[[D]] to i8* +; RECOVER: %[[MEMTAG:[^ ]*]] = load i8, i8* %[[E]] +; RECOVER: %[[F:[^ ]*]] = icmp ne i8 %[[PTRTAG]], %[[MEMTAG]] +; RECOVER: br i1 %[[F]], label {{.*}}, label {{.*}}, !prof {{.*}} + +; RECOVER: call void asm sideeffect alignstack "ebreak\0Aaddiw x0, x0, 116", "{x10}"(i64 %[[A]]) +; RECOVER: br label + +; ABORT: %[[A:[^ ]*]] = bitcast i128* %a to i8* +; ABORT: call void @llvm.hwasan.check.memaccess.shortgranules(i8* %.hwasan.shadow, i8* %[[A]], i32 20) + +; CHECK: store i128 %b, i128* %a, align 16 +; CHECK: ret void + +entry: + store i128 %b, i128* %a, align 16 + ret void +} + +define void @test_store40(i40* %a, i40 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store40( +; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64 +; 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: ret void + +entry: + store i40 %b, i40* %a, align 4 + ret void +} + +define void @test_store_unaligned(i64* %a, i64 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store_unaligned( +; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64 +; 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: ret void + +entry: + store i64 %b, i64* %a, align 4 + ret void +} + +define i8 @test_load_noattr(i8* %a) { +; CHECK-LABEL: @test_load_noattr( +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a +; CHECK-NEXT: ret i8 %[[B]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +define i8 @test_load_notmyattr(i8* %a) sanitize_address { +; CHECK-LABEL: @test_load_notmyattr( +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a +; CHECK-NEXT: ret i8 %[[B]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +define i8 @test_load_addrspace(i8 addrspace(256)* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load_addrspace( +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8 addrspace(256)* %a +; CHECK-NEXT: ret i8 %[[B]] + +entry: + %b = load i8, i8 addrspace(256)* %a, align 4 + ret i8 %b +} + +; CHECK: declare void @__hwasan_init() + +; CHECK: define internal void @hwasan.module_ctor() #[[#ATTR:]] comdat { +; CHECK-NEXT: call void @__hwasan_init() +; CHECK-NEXT: ret void +; CHECK-NEXT: } + +; CHECK: attributes #[[#ATTR]] = { nounwind } diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/exception-lifetime.ll b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/exception-lifetime.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/exception-lifetime.ll @@ -0,0 +1,60 @@ +; Test allocas with multiple lifetime ends, as frequently seen for exception +; handling. +; +; RUN: opt -passes=hwasan -hwasan-use-after-scope -S -o - %s | FileCheck %s --check-prefix=CHECK + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-linux" + +declare void @mayFail(i32* %x) sanitize_hwaddress +declare void @onExcept(i32* %x) sanitize_hwaddress + +declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) nounwind +declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) nounwind +declare i32 @__gxx_personality_v0(...) + +define void @test() sanitize_hwaddress personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +entry: + %x = alloca i32, align 8 + %exn.slot = alloca i8*, align 8 + %ehselector.slot = alloca i32, align 4 + %0 = bitcast i32* %x to i8* + call void @llvm.lifetime.start.p0i8(i64 8, i8* %0) + invoke void @mayFail(i32* %x) to label %invoke.cont unwind label %lpad +; CHECK: [[CAST:%.*]] = bitcast { i32, [12 x i8] }* %x to i32* +; CHECK: [[TMP1:%.*]] = bitcast i32* {{.*}}[[CAST]] to i8* + +invoke.cont: ; preds = %entry +; CHECK: invoke.cont: +; CHECK: call void @llvm.memset.p0i8.i64(i8* align 1 %{{.*}}, i8 0, i64 1, i1 false) +; CHECK: call void @llvm.lifetime.end.p0i8(i64 16, i8* {{.*}}[[TMP1]]) +; CHECK: ret void + + %1 = bitcast i32* %x to i8* + call void @llvm.lifetime.end.p0i8(i64 8, i8* %1) + ret void + +lpad: ; preds = %entry +; CHECK: lpad +; CHECK: call void @llvm.memset.p0i8.i64(i8* align 1 %{{.*}}, i8 0, i64 1, i1 false) +; CHECK: call void @llvm.lifetime.end.p0i8(i64 16, i8* {{.*}}[[TMP1]]) +; CHECK: br label %eh.resume + + %2 = landingpad { i8*, i32 } + cleanup + %3 = extractvalue { i8*, i32 } %2, 0 + store i8* %3, i8** %exn.slot, align 8 + %4 = extractvalue { i8*, i32 } %2, 1 + store i32 %4, i32* %ehselector.slot, align 4 + call void @onExcept(i32* %x) #18 + %5 = bitcast i32* %x to i8* + call void @llvm.lifetime.end.p0i8(i64 8, i8* %5) + br label %eh.resume + +eh.resume: ; preds = %lpad + %exn = load i8*, i8** %exn.slot, align 8 + %sel = load i32, i32* %ehselector.slot, align 4 + %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0 + %lpad.val1 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1 + resume { i8*, i32 } %lpad.val1 +} diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/stack-safety-analysis.ll b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/stack-safety-analysis.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/stack-safety-analysis.ll @@ -0,0 +1,315 @@ +; RUN: opt -passes=hwasan -hwasan-instrument-with-calls -hwasan-use-stack-safety=1 -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=SAFETY,CHECK +; RUN: opt -passes=hwasan -hwasan-instrument-with-calls -hwasan-use-stack-safety=0 -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=NOSAFETY,CHECK +; RUN: opt -passes=hwasan -hwasan-instrument-with-calls -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=SAFETY,CHECK +; RUN: opt -passes=hwasan -hwasan-instrument-stack=0 -hwasan-instrument-with-calls -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=NOSTACK,CHECK + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-linux" + +; Check a safe alloca to ensure it does not get a tag. +define i32 @test_simple(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_simple + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_store + %buf.sroa.0 = alloca i8, align 4 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %buf.sroa.0) + store volatile i8 0, i8* %buf.sroa.0, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %buf.sroa.0) + ret i32 0 +} + +; Check a non-safe alloca to ensure it gets a tag. +define i32 @test_use(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_use + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_store + %buf.sroa.0 = alloca i8, align 4 + call void @use(i8* nonnull %buf.sroa.0) + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %buf.sroa.0) + store volatile i8 0, i8* %buf.sroa.0, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %buf.sroa.0) + ret i32 0 +} + +; Check an alloca with in range GEP to ensure it does not get a tag or check. +define i32 @test_in_range(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_in_range + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_store + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 0 + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %ptr) + store volatile i8 0, i8* %ptr, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %ptr) + ret i32 0 +} + +; Check an alloca with in range GEP to ensure it does not get a tag or check. +define i32 @test_in_range2(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_in_range2 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_store + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + store volatile i8 0, i8* %ptr, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + ret i32 0 +} + +define i32 @test_in_range3(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_in_range3 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memset + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_memset + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memset + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.memset.p0i8.i32(i8* %ptr, i8 0, i32 1, i1 true) + ret i32 0 +} + +define i32 @test_in_range4(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_in_range4 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %ptr, i32 1, i1 true) + ret i32 0 +} + +define i32 @test_in_range5(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_in_range5 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + %buf.sroa.1 = alloca [10 x i8], align 4 + %ptr1 = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %y = bitcast [10 x i8]* %buf.sroa.1 to i8* + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %ptr1, i32 1, i1 true) + ret i32 0 +} + +; Check an alloca with out of range GEP to ensure it gets a tag and check. +define i32 @test_out_of_range(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_out_of_range + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_store + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 10 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + store volatile i8 0, i8* %ptr, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + ret i32 0 +} + +define i32 @test_out_of_range3(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_out_of_range3 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memset + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_memset + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memset + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.memset.p0i8.i32(i8* %ptr, i8 0, i32 2, i1 true) + ret i32 0 +} + +define i32 @test_out_of_range4(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_out_of_range4 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %ptr, i32 2, i1 true) + ret i32 0 +} + +define i32 @test_out_of_range5(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_out_of_range5 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + %buf.sroa.1 = alloca [10 x i8], align 4 + %ptr1 = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %y = bitcast [10 x i8]* %buf.sroa.1 to i8* + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %x) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %x) + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %y) + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %ptr1, i32 1, i1 true) + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %y) + ret i32 0 +} + +; Check an alloca with potentially out of range GEP to ensure it gets a tag and +; check. +define i32 @test_potentially_out_of_range(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_potentially_out_of_range + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_store + %buf.sroa.0 = alloca [10 x i8], align 4 + %off = call i32 @getoffset() + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 %off + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %ptr) + store volatile i8 0, i8* %ptr, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %ptr) + ret i32 0 +} + +define i32 @test_potentially_out_of_range2(i8* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_potentially_out_of_range2 + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_memmove + ; SAFETY-NOT: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_memmove + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK: call {{.*}}__hwasan_memmove + %buf.sroa.0 = alloca [10 x i8], align 4 + %ptr = getelementptr [10 x i8], [10 x i8]* %buf.sroa.0, i32 0, i32 9 + %x = bitcast [10 x i8]* %buf.sroa.0 to i8* + call void @llvm.memmove.p0i8.p0i8.i32(i8* %ptr, i8* %a, i32 1, i1 true) + ret i32 0 +} +; Check an alloca with potentially out of range GEP to ensure it gets a tag and +; check. +define i32 @test_unclear(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_unclear + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK: call {{.*}}__hwasan_store + %buf.sroa.0 = alloca i8, align 4 + %ptr = call i8* @getptr(i8* %buf.sroa.0) + call void @llvm.lifetime.start.p0i8(i64 10, i8* nonnull %ptr) + store volatile i8 0, i8* %ptr, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 10, i8* nonnull %ptr) + ret i32 0 +} + +define i32 @test_select(i8* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_select + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK: call {{.*}}__hwasan_store + %x = call i8* @getptr(i8* %a) + %buf.sroa.0 = alloca i8, align 4 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %buf.sroa.0) + %c = call i1 @cond() + %ptr = select i1 %c, i8* %x, i8* %buf.sroa.0 + store volatile i8 0, i8* %ptr, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %buf.sroa.0) + ret i32 0 +} + +; Check whether we see through the returns attribute of functions. +define i32 @test_retptr(i32* %a) sanitize_hwaddress { +entry: + ; CHECK-LABEL: @test_retptr + ; NOSAFETY: call {{.*}}__hwasan_generate_tag + ; NOSAFETY: call {{.*}}__hwasan_store + ; SAFETY: call {{.*}}__hwasan_generate_tag + ; SAFETY-NOT: call {{.*}}__hwasan_store + ; NOSTACK-NOT: call {{.*}}__hwasan_generate_tag + ; NOSTACK-NOT: call {{.*}}__hwasan_store + %buf.sroa.0 = alloca i8, align 4 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %buf.sroa.0) + %ptr = call i8* @retptr(i8* %buf.sroa.0) + store volatile i8 0, i8* %ptr, align 4, !tbaa !8 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %buf.sroa.0) + ret i32 0 +} + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) + +declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i1) +declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1) +declare void @llvm.memmove.p0i8.p0i8.i32(i8*, i8*, i32, i1) + +declare i1 @cond() +declare void @use(i8* nocapture) +declare i32 @getoffset() +declare i8* @getptr(i8* nocapture) +declare i8* @retptr(i8* returned) + +!8 = !{!9, !9, i64 0} +!9 = !{!"omnipotent char", !10, i64 0} +!10 = !{!"Simple C/C++ TBAA"} diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/use-after-scope-setjmp.ll b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/use-after-scope-setjmp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/use-after-scope-setjmp.ll @@ -0,0 +1,43 @@ +; RUN: opt -passes=hwasan -hwasan-use-stack-safety=0 -hwasan-use-after-scope -S < %s | FileCheck %s +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-linux" + +@stackbuf = dso_local local_unnamed_addr global i8* null, align 8 +@jbuf = dso_local global [32 x i64] zeroinitializer, align 8 + +declare void @may_jump() + +define dso_local noundef i1 @_Z6targetv() sanitize_hwaddress { +entry: + %buf = alloca [4096 x i8], align 1 + %call = call i32 @setjmp(i64* noundef getelementptr inbounds ([32 x i64], [32 x i64]* @jbuf, i64 0, i64 0)) + switch i32 %call, label %while.body [ + i32 1, label %return + i32 2, label %sw.bb1 + ] + +sw.bb1: ; preds = %entry + br label %return + +while.body: ; preds = %entry + %0 = getelementptr inbounds [4096 x i8], [4096 x i8]* %buf, i64 0, i64 0 + call void @llvm.lifetime.start.p0i8(i64 4096, i8* nonnull %0) #10 + store i8* %0, i8** @stackbuf, align 8 + ; may_jump may call longjmp, going back to the switch (and then the return), + ; bypassing the lifetime.end. This is why we need to untag on the return, + ; rather than the lifetime.end. + call void @may_jump() + call void @llvm.lifetime.end.p0i8(i64 4096, i8* nonnull %0) #10 + br label %return + +; CHECK-LABEL: return: +; CHECK: void @llvm.memset.p0i8.i64({{.*}}, i8 0, i64 256, i1 false) +return: ; preds = %entry, %while.body, %sw.bb1 + %retval.0 = phi i1 [ true, %while.body ], [ true, %sw.bb1 ], [ false, %entry ] + ret i1 %retval.0 +} + +declare i32 @setjmp(i64* noundef) returns_twice + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/with-calls.ll b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/with-calls.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/RISCV/with-calls.ll @@ -0,0 +1,203 @@ +; Test basic address sanitizer instrumentation. +; +; RUN: opt < %s -passes=hwasan -hwasan-instrument-with-calls -S | FileCheck %s --check-prefixes=CHECK,ABORT +; RUN: opt < %s -passes=hwasan -hwasan-instrument-with-calls -hwasan-recover=1 -S | FileCheck %s --check-prefixes=CHECK,RECOVER + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "riscv64-unknown-linux" + +define i8 @test_load8(i8* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load8( +; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64 +; ABORT: call void @__hwasan_load1(i64 %[[A]]) +; RECOVER: call void @__hwasan_load1_noabort(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i8, i8* %a +; CHECK: ret i8 %[[B]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +define i16 @test_load16(i16* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load16( +; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64 +; ABORT: call void @__hwasan_load2(i64 %[[A]]) +; RECOVER: call void @__hwasan_load2_noabort(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i16, i16* %a +; CHECK: ret i16 %[[B]] + +entry: + %b = load i16, i16* %a, align 4 + ret i16 %b +} + +define i32 @test_load32(i32* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load32( +; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64 +; ABORT: call void @__hwasan_load4(i64 %[[A]]) +; RECOVER: call void @__hwasan_load4_noabort(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i32, i32* %a +; CHECK: ret i32 %[[B]] + +entry: + %b = load i32, i32* %a, align 4 + ret i32 %b +} + +define i64 @test_load64(i64* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load64( +; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64 +; ABORT: call void @__hwasan_load8(i64 %[[A]]) +; RECOVER: call void @__hwasan_load8_noabort(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i64, i64* %a +; CHECK: ret i64 %[[B]] + +entry: + %b = load i64, i64* %a, align 8 + ret i64 %b +} + +define i128 @test_load128(i128* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load128( +; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64 +; ABORT: call void @__hwasan_load16(i64 %[[A]]) +; RECOVER: call void @__hwasan_load16_noabort(i64 %[[A]]) +; CHECK: %[[B:[^ ]*]] = load i128, i128* %a +; CHECK: ret i128 %[[B]] + +entry: + %b = load i128, i128* %a, align 16 + ret i128 %b +} + +define i40 @test_load40(i40* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load40( +; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64 +; 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: ret i40 %[[B]] + +entry: + %b = load i40, i40* %a, align 4 + ret i40 %b +} + +define void @test_store8(i8* %a, i8 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store8( +; CHECK: %[[A:[^ ]*]] = ptrtoint i8* %a to i64 +; ABORT: call void @__hwasan_store1(i64 %[[A]]) +; RECOVER: call void @__hwasan_store1_noabort(i64 %[[A]]) +; CHECK: store i8 %b, i8* %a +; CHECK: ret void + +entry: + store i8 %b, i8* %a, align 4 + ret void +} + +define void @test_store16(i16* %a, i16 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store16( +; CHECK: %[[A:[^ ]*]] = ptrtoint i16* %a to i64 +; ABORT: call void @__hwasan_store2(i64 %[[A]]) +; RECOVER: call void @__hwasan_store2_noabort(i64 %[[A]]) +; CHECK: store i16 %b, i16* %a +; CHECK: ret void + +entry: + store i16 %b, i16* %a, align 4 + ret void +} + +define void @test_store32(i32* %a, i32 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store32( +; CHECK: %[[A:[^ ]*]] = ptrtoint i32* %a to i64 +; ABORT: call void @__hwasan_store4(i64 %[[A]]) +; RECOVER: call void @__hwasan_store4_noabort(i64 %[[A]]) +; CHECK: store i32 %b, i32* %a +; CHECK: ret void + +entry: + store i32 %b, i32* %a, align 4 + ret void +} + +define void @test_store64(i64* %a, i64 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store64( +; CHECK: %[[A:[^ ]*]] = ptrtoint i64* %a to i64 +; ABORT: call void @__hwasan_store8(i64 %[[A]]) +; RECOVER: call void @__hwasan_store8_noabort(i64 %[[A]]) +; CHECK: store i64 %b, i64* %a +; CHECK: ret void + +entry: + store i64 %b, i64* %a, align 8 + ret void +} + +define void @test_store128(i128* %a, i128 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store128( +; CHECK: %[[A:[^ ]*]] = ptrtoint i128* %a to i64 +; ABORT: call void @__hwasan_store16(i64 %[[A]]) +; RECOVER: call void @__hwasan_store16_noabort(i64 %[[A]]) +; CHECK: store i128 %b, i128* %a +; CHECK: ret void + +entry: + store i128 %b, i128* %a, align 16 + ret void +} + +define void @test_store40(i40* %a, i40 %b) sanitize_hwaddress { +; CHECK-LABEL: @test_store40( +; CHECK: %[[A:[^ ]*]] = ptrtoint i40* %a to i64 +; 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: ret void + +entry: + store i40 %b, i40* %a, align 4 + ret void +} + +define i8 @test_load_noattr(i8* %a) { +; CHECK-LABEL: @test_load_noattr( +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a +; CHECK-NEXT: ret i8 %[[B]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +define i8 @test_load_notmyattr(i8* %a) sanitize_address { +; CHECK-LABEL: @test_load_notmyattr( +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8* %a +; CHECK-NEXT: ret i8 %[[B]] + +entry: + %b = load i8, i8* %a, align 4 + ret i8 %b +} + +define i8 @test_load_addrspace(i8 addrspace(256)* %a) sanitize_hwaddress { +; CHECK-LABEL: @test_load_addrspace( +; CHECK-NEXT: entry: +; CHECK-NEXT: %[[B:[^ ]*]] = load i8, i8 addrspace(256)* %a +; CHECK-NEXT: ret i8 %[[B]] + +entry: + %b = load i8, i8 addrspace(256)* %a, align 4 + ret i8 %b +} + +; CHECK: declare void @__hwasan_init() + +; CHECK: define internal void @hwasan.module_ctor() #[[#]] comdat { +; CHECK-NEXT: call void @__hwasan_init() +; CHECK-NEXT: ret void +; CHECK-NEXT: }