diff --git a/compiler-rt/test/hwasan/TestCases/stack-uas.c b/compiler-rt/test/hwasan/TestCases/stack-uas.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/stack-uas.c @@ -0,0 +1,68 @@ +// Tests use-after-scope detection and reporting. +// RUN: %clang_hwasan -mllvm -hwasan-use-after-scope -g %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_hwasan -mllvm -hwasan-use-after-scope -g %s -o %t && not %env_hwasan_opts=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM + +// RUN: %clang_hwasan -mllvm -hwasan-use-after-scope=false -g %s -o %t && %run %t 2>&1 +// Use after scope is turned off by default. +// RUN: %clang_hwasan -g %s -o %t && %run %t 2>&1 + +// RUN: %clang_hwasan -fexperimental-new-pass-manager -mllvm -hwasan-use-after-scope -g %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_hwasan -fno-experimental-new-pass-manager -mllvm -hwasan-use-after-scope -g %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: stable-runtime + +// Stack histories currently are not recorded on x86. +// XFAIL: x86_64 + +void USE(void *x) { // pretend_to_do_something(void *x) + __asm__ __volatile__("" + : + : "r"(x) + : "memory"); +} + +__attribute__((noinline)) void Unrelated1() { + int A[2]; + USE(&A[0]); +} +__attribute__((noinline)) void Unrelated2() { + int BB[3]; + USE(&BB[0]); +} +__attribute__((noinline)) void Unrelated3() { + int CCC[4]; + USE(&CCC[0]); +} + +__attribute__((noinline)) char buggy() { + char *volatile p; + { + char zzz[0x1000]; + p = zzz; + } + return *p; +} + +int main() { + Unrelated1(); + Unrelated2(); + Unrelated3(); + char p = buggy(); + return p; + // CHECK: READ of size 1 at + // CHECK: #0 {{.*}} in buggy{{.*}}stack-uas.c:[[@LINE-10]] + // CHECK: Cause: stack tag-mismatch + // CHECK: is located in stack of thread + // CHECK: Potentially referenced stack objects: + // CHECK-NEXT: zzz in buggy {{.*}}stack-uas.c:[[@LINE-17]] + // CHECK-NEXT: Memory tags around the buggy address + + // NOSYM: Previously allocated frames: + // NOSYM-NEXT: record_addr:0x{{.*}} record:0x{{.*}} ({{.*}}/stack-uas.c.tmp+0x{{.*}}){{$}} + // NOSYM-NEXT: record_addr:0x{{.*}} record:0x{{.*}} ({{.*}}/stack-uas.c.tmp+0x{{.*}}){{$}} + // NOSYM-NEXT: record_addr:0x{{.*}} record:0x{{.*}} ({{.*}}/stack-uas.c.tmp+0x{{.*}}){{$}} + // NOSYM-NEXT: record_addr:0x{{.*}} record:0x{{.*}} ({{.*}}/stack-uas.c.tmp+0x{{.*}}){{$}} + // NOSYM-NEXT: Memory tags around the buggy address + + // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in buggy +} diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-capture.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-capture.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-capture.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-capture.cpp @@ -1,8 +1,8 @@ -// RUN: %clangxx_asan %stdcxx11 -O1 -fsanitize-address-use-after-scope %s -o %t && \ -// RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan -// XFAIL: * +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope --std=c++11 -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: aarch64-target-arch #include @@ -12,8 +12,9 @@ int x = 0; f = [&x]() __attribute__((noinline)) { return x; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in {{.*}}use-after-scope-capture.cpp:[[@LINE-2]] + // CHECK: Cause: stack tag-mismatch }; } return f(); // BOOM diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-dtor-order.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-dtor-order.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-dtor-order.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-dtor-order.cpp @@ -1,8 +1,10 @@ -// RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && \ +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime #include @@ -10,7 +12,7 @@ explicit IntHolder(int *val = 0) : val_(val) {} __attribute__((noinline)) ~IntHolder() { printf("Value: %d\n", *val_); // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in IntHolder::~IntHolder{{.*}}.cpp:[[@LINE-2]] } void set(int *val) { val_ = val; } diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-goto.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-goto.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-goto.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-goto.cpp @@ -1,10 +1,12 @@ -// RUN: %clangxx_asan -O0 -fsanitize-address-use-after-scope %s -o %t && %run %t +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O0 %s -o %t && %run %t // Function jumps over variable initialization making lifetime analysis // ambiguous. Asan should ignore such variable and program must not fail. -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime #include diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-if.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-if.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-if.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-if.cpp @@ -1,8 +1,10 @@ -// RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && \ +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime int *p; bool b = true; @@ -13,6 +15,7 @@ p = x + 1; } return *p; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in main {{.*}}.cpp:[[@LINE-2]] + // CHECK: Cause: stack tag-mismatch } diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-inlined.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-inlined.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-inlined.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-inlined.cpp @@ -1,15 +1,16 @@ +// This is the ASAN test of the same name ported to HWAsan. + // Test with "-O2" only to make sure inlining (leading to use-after-scope) // happens. "always_inline" is not enough, as Clang doesn't emit // llvm.lifetime intrinsics at -O0. // -// RUN: %clangxx_asan -O2 -fsanitize-address-use-after-scope %s -o %t && \ +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O2 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * -int *arr; +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime +int *arr; __attribute__((always_inline)) void inlined(int arg) { int x[5]; for (int i = 0; i < arg; i++) @@ -20,12 +21,6 @@ int main(int argc, char *argv[]) { inlined(argc); return arr[argc - 1]; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope - // CHECK: READ of size 4 at 0x{{.*}} thread T0 - // CHECK: #0 0x{{.*}} in main - // CHECK: {{.*}}use-after-scope-inlined.cpp:[[@LINE-4]] - // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset [[OFFSET:[^ ]*]] in frame - // CHECK: {{.*}} in main - // CHECK: This frame has - // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i' (line [[@LINE-15]]) + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch + // CHECK: Cause: stack tag-mismatch } diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-loop-bug.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-loop-bug.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-loop-bug.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-loop-bug.cpp @@ -1,8 +1,10 @@ -// RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && \ +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime volatile int *p; @@ -13,8 +15,7 @@ p = x + i; } return *p; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope-loop-bug.cpp:[[@LINE-2]] - // CHECK: Address 0x{{.*}} is located in stack of thread T{{.*}} at offset [[OFFSET:[^ ]+]] in frame - // {{\[}}[[OFFSET]], {{[0-9]+}}) 'x' + // CHECK: Cause: stack tag-mismatch } diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-loop-removed.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-loop-removed.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-loop-removed.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-loop-removed.cpp @@ -1,8 +1,10 @@ -// RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && \ +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime #include @@ -14,8 +16,7 @@ p = &x; } return *p; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope-loop-removed.cpp:[[@LINE-2]] - // CHECK: Address 0x{{.*}} is located in stack of thread T{{.*}} at offset [[OFFSET:[^ ]+]] in frame - // {{\[}}[[OFFSET]], {{[0-9]+}}) 'x' + // CHECK: Cause: stack tag-mismatch } diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-loop.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-loop.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-loop.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-loop.cpp @@ -1,8 +1,10 @@ -// RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && \ +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime int *p[3]; @@ -12,6 +14,7 @@ p[i] = &x; } return **p; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in main {{.*}}.cpp:[[@LINE-2]] + // CHECK: Cause: stack tag-mismatch } diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-nobug.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-nobug.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-nobug.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-nobug.cpp @@ -1,7 +1,9 @@ -// RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && %run %t -// -// Not expected to work yet with HWAsan. -// XFAIL: * +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O1 %s -o %t && %run %t + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime #include #include diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-temp.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-temp.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-temp.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-temp.cpp @@ -1,8 +1,10 @@ -// RUN: %clangxx_asan %stdcxx11 -O1 -fsanitize-address-use-after-scope %s -o %t && \ +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -std=c++11 -O1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime struct IntHolder { int val; @@ -17,7 +19,8 @@ int main(int argc, char *argv[]) { save({argc}); int x = saved->val; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope-temp.cpp:[[@LINE-2]] + // CHECK: Cause: stack tag-mismatch return x; } diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-temp2.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-temp2.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-temp2.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-temp2.cpp @@ -1,8 +1,10 @@ -// RUN: %clangxx_asan %stdcxx11 -O1 -fsanitize-address-use-after-scope %s -o %t && \ +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -std=c++11 -O1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime struct IntHolder { __attribute__((noinline)) const IntHolder &Self() const { @@ -16,7 +18,8 @@ int main(int argc, char *argv[]) { saved = &IntHolder().Self(); int x = saved->val; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope-temp2.cpp:[[@LINE-2]] + // CHECK: Cause: stack tag-mismatch return x; } diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope-types.cpp @@ -1,18 +1,21 @@ -// RUN: %clangxx_asan %stdcxx11 -O0 -fsanitize-address-use-after-scope %s -o %t +// This is the ASAN test of the same name ported to HWAsan. + +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -std=c++11 -O0 %s -o %t // RUN: not %run %t 0 2>&1 | FileCheck %s // RUN: not %run %t 1 2>&1 | FileCheck %s // RUN: not %run %t 2 2>&1 | FileCheck %s // RUN: not %run %t 3 2>&1 | FileCheck %s // RUN: not %run %t 4 2>&1 | FileCheck %s // RUN: not %run %t 5 2>&1 | FileCheck %s -// RUN: not %run %t 6 2>&1 | FileCheck %s +// The std::vector case is broken because of limited lifetime tracking. +// TODO(fmayer): Fix and enable. // RUN: not %run %t 7 2>&1 | FileCheck %s // RUN: not %run %t 8 2>&1 | FileCheck %s // RUN: not %run %t 9 2>&1 | FileCheck %s // RUN: not %run %t 10 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime #include #include @@ -46,10 +49,9 @@ } ptr.Access(); - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #{{[0-9]+}} 0x{{.*}} in {{(void )?test.*\((void)?\) .*}}use-after-scope-types.cpp - // CHECK: Address 0x{{.*}} is located in stack of thread T{{.*}} at offset [[OFFSET:[^ ]+]] in frame - // {{\[}}[[OFFSET]], {{[0-9]+}}) 'x' + // CHECK: Cause: stack tag-mismatch } int main(int argc, char **argv) { diff --git a/compiler-rt/test/hwasan/TestCases/use-after-scope.cpp b/compiler-rt/test/hwasan/TestCases/use-after-scope.cpp --- a/compiler-rt/test/hwasan/TestCases/use-after-scope.cpp +++ b/compiler-rt/test/hwasan/TestCases/use-after-scope.cpp @@ -1,12 +1,10 @@ -// RUN: %clangxx_asan -O1 -fsanitize-address-use-after-scope %s -o %t && \ -// RUN: not %run %t 2>&1 | FileCheck %s +// This is the ASAN test of the same name ported to HWAsan. -// -fsanitize-address-use-after-scope is now on by default: -// RUN: %clangxx_asan -O1 %s -o %t && \ +// RUN: %clangxx_hwasan -mllvm -hwasan-use-after-scope -O1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// -// Not expected to work yet with HWAsan. -// XFAIL: * + +// REQUIRES: aarch64-target-arch +// REQUIRES: stable-runtime volatile int *p = 0; @@ -16,9 +14,8 @@ p = &x; } *p = 5; // BOOM - // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope.cpp:[[@LINE-2]] - // CHECK: Address 0x{{.*}} is located in stack of thread T{{.*}} at offset [[OFFSET:[^ ]+]] in frame - // {{\[}}[[OFFSET]], {{[0-9]+}}) 'x' + // CHECK: Cause: stack tag-mismatch return 0; } 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 @@ -17,7 +17,9 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" +#include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/StackSafetyAnalysis.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" @@ -26,6 +28,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InlineAsm.h" @@ -41,6 +44,7 @@ #include "llvm/IR/Value.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" +#include "llvm/PassRegistry.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -115,6 +119,11 @@ cl::Hidden, cl::desc("Use Stack Safety analysis results"), cl::Optional); +static cl::opt + ClUseAfterScope("hwasan-use-after-scope", + cl::desc("detect use after scope within function"), + cl::Hidden, cl::init(false)); + static cl::opt ClUARRetagToZero( "hwasan-uar-retag-to-zero", cl::desc("Clear alloca tags before returning from the function to allow " @@ -220,9 +229,21 @@ return shouldInstrumentStack(TargetTriple) && mightUseStackSafetyAnalysis(DisableOptimization); } + +bool shouldDetectUseAfterScope(const Triple &TargetTriple) { + return ClUseAfterScope && shouldInstrumentStack(TargetTriple); +} + /// An instrumentation pass implementing detection of addressability bugs /// using tagged pointers. class HWAddressSanitizer { +private: + struct AllocaInfo { + AllocaInst *AI; + SmallVector LifetimeStart; + SmallVector LifetimeEnd; + }; + public: HWAddressSanitizer(Module &M, bool CompileKernel, bool Recover, const StackSafetyGlobalInfo *SSI) @@ -237,7 +258,9 @@ void setSSI(const StackSafetyGlobalInfo *S) { SSI = S; } - bool sanitizeFunction(Function &F); + bool sanitizeFunction(Function &F, + llvm::function_ref GetDT, + llvm::function_ref GetPDT); void initializeModule(); void createHwasanCtorComdat(); @@ -260,13 +283,16 @@ Instruction *I, SmallVectorImpl &Interesting); bool isInterestingAlloca(const AllocaInst &AI); - bool tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag, size_t Size); + void tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag, size_t Size); Value *tagPointer(IRBuilder<> &IRB, Type *Ty, Value *PtrLong, Value *Tag); Value *untagPointer(IRBuilder<> &IRB, Value *PtrLong); bool instrumentStack( - SmallVectorImpl &Allocas, + MapVector &AllocasToInstrument, + SmallVector &UnrecognizedLifetimes, DenseMap> &AllocaDbgMap, - SmallVectorImpl &RetVec, Value *StackTag); + SmallVectorImpl &RetVec, Value *StackTag, + llvm::function_ref GetDT, + llvm::function_ref GetPDT); Value *readRegister(IRBuilder<> &IRB, StringRef Name); bool instrumentLandingPads(SmallVectorImpl &RetVec); Value *getNextTagWithCall(IRBuilder<> &IRB); @@ -315,6 +341,7 @@ void init(Triple &TargetTriple, bool InstrumentWithCalls); unsigned getObjectAlignment() const { return 1U << Scale; } }; + ShadowMapping Mapping; Type *VoidTy = Type::getVoidTy(M.getContext()); @@ -331,6 +358,7 @@ bool InstrumentLandingPads; bool InstrumentWithCalls; bool InstrumentStack; + bool DetectUseAfterScope; bool UsePageAliases; bool HasMatchAllTag = false; @@ -377,14 +405,21 @@ } bool runOnFunction(Function &F) override { - if (shouldUseStackSafetyAnalysis(Triple(F.getParent()->getTargetTriple()), - DisableOptimization)) { + auto TargetTriple = Triple(F.getParent()->getTargetTriple()); + if (shouldUseStackSafetyAnalysis(TargetTriple, DisableOptimization)) { // We cannot call getAnalysis in doInitialization, that would cause a // crash as the required analyses are not initialized yet. HWASan->setSSI( &getAnalysis().getResult()); } - return HWASan->sanitizeFunction(F); + return HWASan->sanitizeFunction( + F, + [&]() -> const DominatorTree & { + return getAnalysis().getDomTree(); + }, + [&]() -> const PostDominatorTree & { + return getAnalysis().getPostDomTree(); + }); } bool doFinalization(Module &M) override { @@ -399,6 +434,8 @@ // This is so we don't need to plumb TargetTriple all the way to here. if (mightUseStackSafetyAnalysis(DisableOptimization)) AU.addRequired(); + AU.addRequired(); + AU.addRequired(); } private: @@ -417,6 +454,8 @@ "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, false) INITIALIZE_PASS_DEPENDENCY(StackSafetyGlobalInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(PostDominatorTreeWrapperPass) INITIALIZE_PASS_END( HWAddressSanitizerLegacyPass, "hwasan", "HWAddressSanitizer: detect memory bugs using tagged addressing.", false, @@ -438,13 +477,23 @@ PreservedAnalyses HWAddressSanitizerPass::run(Module &M, ModuleAnalysisManager &MAM) { const StackSafetyGlobalInfo *SSI = nullptr; - if (shouldUseStackSafetyAnalysis(llvm::Triple(M.getTargetTriple()), - DisableOptimization)) + auto TargetTriple = llvm::Triple(M.getTargetTriple()); + if (shouldUseStackSafetyAnalysis(TargetTriple, DisableOptimization)) SSI = &MAM.getResult(M); + HWAddressSanitizer HWASan(M, CompileKernel, Recover, SSI); bool Modified = false; - for (Function &F : M) - Modified |= HWASan.sanitizeFunction(F); + auto &FAM = MAM.getResult(M).getManager(); + for (Function &F : M) { + Modified |= HWASan.sanitizeFunction( + F, + [&]() -> const DominatorTree & { + return FAM.getResult(F); + }, + [&]() -> const PostDominatorTree & { + return FAM.getResult(F); + }); + } if (Modified) return PreservedAnalyses::none(); return PreservedAnalyses::all(); @@ -566,6 +615,7 @@ UsePageAliases = shouldUsePageAliases(TargetTriple); InstrumentWithCalls = shouldInstrumentWithCalls(TargetTriple); InstrumentStack = shouldInstrumentStack(TargetTriple); + DetectUseAfterScope = shouldDetectUseAfterScope(TargetTriple); PointerTagShift = IsX86_64 ? 57 : 56; TagMaskByte = IsX86_64 ? 0x3F : 0xFF; @@ -968,7 +1018,7 @@ return SizeInBytes * ArraySize; } -bool HWAddressSanitizer::tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag, +void HWAddressSanitizer::tagAlloca(IRBuilder<> &IRB, AllocaInst *AI, Value *Tag, size_t Size) { size_t AlignedSize = alignTo(Size, Mapping.getObjectAlignment()); if (!UseShortGranules) @@ -999,7 +1049,6 @@ AlignedSize - 1)); } } - return true; } unsigned HWAddressSanitizer::retagMask(unsigned AllocaNo) { @@ -1232,16 +1281,23 @@ } bool HWAddressSanitizer::instrumentStack( - SmallVectorImpl &Allocas, + MapVector &AllocasToInstrument, + SmallVector &UnrecognizedLifetimes, DenseMap> &AllocaDbgMap, - SmallVectorImpl &RetVec, Value *StackTag) { + SmallVectorImpl &RetVec, Value *StackTag, + llvm::function_ref GetDT, + llvm::function_ref GetPDT) { // Ideally, we want to calculate tagged stack base pointer, and rewrite all // alloca addresses using that. Unfortunately, offsets are not known yet // (unless we use ASan-style mega-alloca). Instead we keep the base tag in a // temp, shift-OR it into each alloca address and xor with the retag mask. // This generates one extra instruction per alloca use. - for (unsigned N = 0; N < Allocas.size(); ++N) { - auto *AI = Allocas[N]; + unsigned int I = 0; + + for (auto &KV : AllocasToInstrument) { + auto N = I++; + auto *AI = KV.first; + AllocaInfo &Info = KV.second; IRBuilder<> IRB(AI->getNextNode()); // Replace uses of the alloca with tagged address. @@ -1268,17 +1324,39 @@ } size_t Size = getAllocaSizeInBytes(*AI); - tagAlloca(IRB, AI, Tag, Size); - - for (auto RI : RetVec) { - IRB.SetInsertPoint(RI); - - // Re-tag alloca memory with the special UAR tag. - Value *Tag = getUARTag(IRB, StackTag); - tagAlloca(IRB, AI, Tag, alignTo(Size, Mapping.getObjectAlignment())); + size_t AlignedSize = alignTo(Size, Mapping.getObjectAlignment()); + bool StandardLifetime = UnrecognizedLifetimes.empty() && + Info.LifetimeStart.size() == 1 && + Info.LifetimeEnd.size() == 1; + if (DetectUseAfterScope && StandardLifetime) { + IntrinsicInst *Start = Info.LifetimeStart[0]; + IntrinsicInst *End = Info.LifetimeEnd[0]; + IRB.SetInsertPoint(Start->getNextNode()); + auto TagEnd = [&](Instruction *Node) { + IRB.SetInsertPoint(Node); + Value *UARTag = getUARTag(IRB, StackTag); + tagAlloca(IRB, AI, UARTag, AlignedSize); + }; + tagAlloca(IRB, AI, Tag, Size); + if (!forAllReachableExits(GetDT(), GetPDT(), Start, End, RetVec, TagEnd)) + End->eraseFromParent(); + } else { + tagAlloca(IRB, AI, Tag, Size); + for (auto *RI : RetVec) { + IRB.SetInsertPoint(RI); + Value *UARTag = getUARTag(IRB, StackTag); + tagAlloca(IRB, AI, UARTag, AlignedSize); + } + if (!StandardLifetime) { + for (auto &II : Info.LifetimeStart) + II->eraseFromParent(); + for (auto &II : Info.LifetimeEnd) + II->eraseFromParent(); + } } } - + for (auto &I : UnrecognizedLifetimes) + I->eraseFromParent(); return true; } @@ -1300,7 +1378,9 @@ !(SSI && SSI->isSafe(AI)); } -bool HWAddressSanitizer::sanitizeFunction(Function &F) { +bool HWAddressSanitizer::sanitizeFunction( + Function &F, llvm::function_ref GetDT, + llvm::function_ref GetPDT) { if (&F == HwasanCtorFunction) return false; @@ -1311,18 +1391,36 @@ SmallVector OperandsToInstrument; SmallVector IntrinToInstrument; - SmallVector AllocasToInstrument; + MapVector AllocasToInstrument; SmallVector RetVec; SmallVector LandingPadVec; + SmallVector UnrecognizedLifetimes; DenseMap> AllocaDbgMap; for (auto &BB : F) { for (auto &Inst : BB) { - if (InstrumentStack) + if (InstrumentStack) { if (AllocaInst *AI = dyn_cast(&Inst)) { if (isInterestingAlloca(*AI)) - AllocasToInstrument.push_back(AI); + AllocasToInstrument.insert({AI, {}}); + continue; + } + auto *II = dyn_cast(&Inst); + if (II && (II->getIntrinsicID() == Intrinsic::lifetime_start || + II->getIntrinsicID() == Intrinsic::lifetime_end)) { + AllocaInst *AI = findAllocaForValue(II->getArgOperand(1)); + if (!AI) { + UnrecognizedLifetimes.push_back(&Inst); + continue; + } + if (!isInterestingAlloca(*AI)) + continue; + if (II->getIntrinsicID() == Intrinsic::lifetime_start) + AllocasToInstrument[AI].LifetimeStart.push_back(II); + else + AllocasToInstrument[AI].LifetimeEnd.push_back(II); continue; } + } if (isa(Inst) || isa(Inst) || isa(Inst)) @@ -1377,13 +1475,15 @@ if (!AllocasToInstrument.empty()) { Value *StackTag = ClGenerateTagsWithCalls ? nullptr : getStackBaseTag(EntryIRB); - instrumentStack(AllocasToInstrument, AllocaDbgMap, RetVec, StackTag); + instrumentStack(AllocasToInstrument, UnrecognizedLifetimes, AllocaDbgMap, + RetVec, StackTag, GetDT, GetPDT); } // Pad and align each of the allocas that we instrumented to stop small // uninteresting allocas from hiding in instrumented alloca's padding and so // that we have enough space to store real tags for short granules. DenseMap AllocaToPaddedAllocaMap; - for (AllocaInst *AI : AllocasToInstrument) { + for (auto &KV : AllocasToInstrument) { + AllocaInst *AI = KV.first; uint64_t Size = getAllocaSizeInBytes(*AI); uint64_t AlignedSize = alignTo(Size, Mapping.getObjectAlignment()); AI->setAlignment( diff --git a/llvm/test/Instrumentation/HWAddressSanitizer/use-after-scope.ll b/llvm/test/Instrumentation/HWAddressSanitizer/use-after-scope.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/HWAddressSanitizer/use-after-scope.ll @@ -0,0 +1,190 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -hwasan -hwasan-use-after-scope=1 -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=SCOPE +; RUN: opt -hwasan -hwasan-use-after-scope=0 -hwasan-generate-tags-with-calls -S < %s | FileCheck %s --check-prefixes=NOSCOPE + +; ModuleID = 'use-after-scope.c' +source_filename = "use-after-scope.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define dso_local i32 @standard_lifetime() local_unnamed_addr sanitize_hwaddress { +; SCOPE-LABEL: @standard_lifetime( +; SCOPE-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call i8* asm "", "=r,0"(i8* null) +; SCOPE-NEXT: [[TMP1:%.*]] = alloca { i8, [15 x i8] }, align 16 +; SCOPE-NEXT: [[TMP2:%.*]] = bitcast { i8, [15 x i8] }* [[TMP1]] to i8* +; SCOPE-NEXT: [[TMP3:%.*]] = call i8 @__hwasan_generate_tag() +; SCOPE-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i64 +; SCOPE-NEXT: [[TMP5:%.*]] = ptrtoint i8* [[TMP2]] to i64 +; SCOPE-NEXT: [[TMP6:%.*]] = shl i64 [[TMP4]], 57 +; SCOPE-NEXT: [[TMP7:%.*]] = or i64 [[TMP5]], [[TMP6]] +; SCOPE-NEXT: [[ALLOCA_0_HWASAN:%.*]] = inttoptr i64 [[TMP7]] to i8* +; SCOPE-NEXT: br label [[TMP8:%.*]] +; SCOPE: 8: +; SCOPE-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull [[ALLOCA_0_HWASAN]]) +; SCOPE-NEXT: [[TMP9:%.*]] = trunc i64 [[TMP4]] to i8 +; SCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 [[TMP9]], i64 16) +; SCOPE-NEXT: [[TMP10:%.*]] = tail call i1 (...) @cond() +; SCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 0, i64 16) +; SCOPE-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull [[ALLOCA_0_HWASAN]]) +; SCOPE-NEXT: br i1 [[TMP10]], label [[TMP11:%.*]], label [[TMP8]] +; SCOPE: 11: +; SCOPE-NEXT: call void @use(i8* nonnull [[ALLOCA_0_HWASAN]]) +; SCOPE-NEXT: ret i32 0 +; +; NOSCOPE-LABEL: @standard_lifetime( +; NOSCOPE-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call i8* asm "", "=r,0"(i8* null) +; NOSCOPE-NEXT: [[TMP1:%.*]] = alloca { i8, [15 x i8] }, align 16 +; NOSCOPE-NEXT: [[TMP2:%.*]] = bitcast { i8, [15 x i8] }* [[TMP1]] to i8* +; NOSCOPE-NEXT: [[TMP3:%.*]] = call i8 @__hwasan_generate_tag() +; NOSCOPE-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i64 +; NOSCOPE-NEXT: [[TMP5:%.*]] = ptrtoint i8* [[TMP2]] to i64 +; NOSCOPE-NEXT: [[TMP6:%.*]] = shl i64 [[TMP4]], 57 +; NOSCOPE-NEXT: [[TMP7:%.*]] = or i64 [[TMP5]], [[TMP6]] +; NOSCOPE-NEXT: [[ALLOCA_0_HWASAN:%.*]] = inttoptr i64 [[TMP7]] to i8* +; NOSCOPE-NEXT: [[TMP8:%.*]] = trunc i64 [[TMP4]] to i8 +; NOSCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 [[TMP8]], i64 16) +; NOSCOPE-NEXT: br label [[TMP9:%.*]] +; NOSCOPE: 9: +; NOSCOPE-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull [[ALLOCA_0_HWASAN]]) +; NOSCOPE-NEXT: [[TMP10:%.*]] = tail call i1 (...) @cond() +; NOSCOPE-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull [[ALLOCA_0_HWASAN]]) +; NOSCOPE-NEXT: br i1 [[TMP10]], label [[TMP11:%.*]], label [[TMP9]] +; NOSCOPE: 11: +; NOSCOPE-NEXT: call void @use(i8* nonnull [[ALLOCA_0_HWASAN]]) +; NOSCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 0, i64 16) +; NOSCOPE-NEXT: ret i32 0 +; + %1 = alloca i8, align 1 + br label %2 + +2: ; preds = %2, %0 +; We should tag the memory after the br (in the loop). + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %1) + %3 = tail call i1 (...) @cond() #2 +; We should tag the memory before the next br (before the jump back). + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %1) + br i1 %3, label %4, label %2 + +4: ; preds = %2 + call void @use(i8* nonnull %1) #2 + ret i32 0 +} + +define dso_local i32 @multiple_lifetimes() local_unnamed_addr sanitize_hwaddress { +; SCOPE-LABEL: @multiple_lifetimes( +; SCOPE-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call i8* asm "", "=r,0"(i8* null) +; SCOPE-NEXT: [[TMP1:%.*]] = alloca { i8, [15 x i8] }, align 16 +; SCOPE-NEXT: [[TMP2:%.*]] = bitcast { i8, [15 x i8] }* [[TMP1]] to i8* +; SCOPE-NEXT: [[TMP3:%.*]] = call i8 @__hwasan_generate_tag() +; SCOPE-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i64 +; SCOPE-NEXT: [[TMP5:%.*]] = ptrtoint i8* [[TMP2]] to i64 +; SCOPE-NEXT: [[TMP6:%.*]] = shl i64 [[TMP4]], 57 +; SCOPE-NEXT: [[TMP7:%.*]] = or i64 [[TMP5]], [[TMP6]] +; SCOPE-NEXT: [[ALLOCA_0_HWASAN:%.*]] = inttoptr i64 [[TMP7]] to i8* +; SCOPE-NEXT: [[TMP8:%.*]] = trunc i64 [[TMP4]] to i8 +; SCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 [[TMP8]], i64 16) +; SCOPE-NEXT: call void @use(i8* nonnull [[ALLOCA_0_HWASAN]]) +; SCOPE-NEXT: call void @use(i8* nonnull [[ALLOCA_0_HWASAN]]) +; SCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 0, i64 16) +; SCOPE-NEXT: ret i32 0 +; +; NOSCOPE-LABEL: @multiple_lifetimes( +; NOSCOPE-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call i8* asm "", "=r,0"(i8* null) +; NOSCOPE-NEXT: [[TMP1:%.*]] = alloca { i8, [15 x i8] }, align 16 +; NOSCOPE-NEXT: [[TMP2:%.*]] = bitcast { i8, [15 x i8] }* [[TMP1]] to i8* +; NOSCOPE-NEXT: [[TMP3:%.*]] = call i8 @__hwasan_generate_tag() +; NOSCOPE-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i64 +; NOSCOPE-NEXT: [[TMP5:%.*]] = ptrtoint i8* [[TMP2]] to i64 +; NOSCOPE-NEXT: [[TMP6:%.*]] = shl i64 [[TMP4]], 57 +; NOSCOPE-NEXT: [[TMP7:%.*]] = or i64 [[TMP5]], [[TMP6]] +; NOSCOPE-NEXT: [[ALLOCA_0_HWASAN:%.*]] = inttoptr i64 [[TMP7]] to i8* +; NOSCOPE-NEXT: [[TMP8:%.*]] = trunc i64 [[TMP4]] to i8 +; NOSCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 [[TMP8]], i64 16) +; NOSCOPE-NEXT: call void @use(i8* nonnull [[ALLOCA_0_HWASAN]]) +; NOSCOPE-NEXT: call void @use(i8* nonnull [[ALLOCA_0_HWASAN]]) +; NOSCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 0, i64 16) +; NOSCOPE-NEXT: ret i32 0 +; + %1 = alloca i8, align 1 +; We erase lifetime markers if we insert instrumentation outside of the +; lifetime. + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %1) + call void @use(i8* nonnull %1) #2 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %1) + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %1) + call void @use(i8* nonnull %1) #2 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %1) + ret i32 0 +} + +define dso_local i32 @unreachable_exit() local_unnamed_addr sanitize_hwaddress { +; SCOPE-LABEL: @unreachable_exit( +; SCOPE-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call i8* asm "", "=r,0"(i8* null) +; SCOPE-NEXT: [[TMP1:%.*]] = alloca { i8, [15 x i8] }, align 16 +; SCOPE-NEXT: [[TMP2:%.*]] = bitcast { i8, [15 x i8] }* [[TMP1]] to i8* +; SCOPE-NEXT: [[TMP3:%.*]] = call i8 @__hwasan_generate_tag() +; SCOPE-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i64 +; SCOPE-NEXT: [[TMP5:%.*]] = ptrtoint i8* [[TMP2]] to i64 +; SCOPE-NEXT: [[TMP6:%.*]] = shl i64 [[TMP4]], 57 +; SCOPE-NEXT: [[TMP7:%.*]] = or i64 [[TMP5]], [[TMP6]] +; SCOPE-NEXT: [[ALLOCA_0_HWASAN:%.*]] = inttoptr i64 [[TMP7]] to i8* +; SCOPE-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull [[ALLOCA_0_HWASAN]]) +; SCOPE-NEXT: [[TMP8:%.*]] = trunc i64 [[TMP4]] to i8 +; SCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 [[TMP8]], i64 16) +; SCOPE-NEXT: [[TMP9:%.*]] = tail call i1 (...) @cond() +; SCOPE-NEXT: br i1 [[TMP9]], label [[TMP10:%.*]], label [[TMP11:%.*]] +; SCOPE: 10: +; SCOPE-NEXT: call void @use(i8* nonnull [[ALLOCA_0_HWASAN]]) +; SCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 0, i64 16) +; SCOPE-NEXT: ret i32 0 +; SCOPE: 11: +; SCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 0, i64 16) +; SCOPE-NEXT: ret i32 0 +; +; NOSCOPE-LABEL: @unreachable_exit( +; NOSCOPE-NEXT: [[DOTHWASAN_SHADOW:%.*]] = call i8* asm "", "=r,0"(i8* null) +; NOSCOPE-NEXT: [[TMP1:%.*]] = alloca { i8, [15 x i8] }, align 16 +; NOSCOPE-NEXT: [[TMP2:%.*]] = bitcast { i8, [15 x i8] }* [[TMP1]] to i8* +; NOSCOPE-NEXT: [[TMP3:%.*]] = call i8 @__hwasan_generate_tag() +; NOSCOPE-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i64 +; NOSCOPE-NEXT: [[TMP5:%.*]] = ptrtoint i8* [[TMP2]] to i64 +; NOSCOPE-NEXT: [[TMP6:%.*]] = shl i64 [[TMP4]], 57 +; NOSCOPE-NEXT: [[TMP7:%.*]] = or i64 [[TMP5]], [[TMP6]] +; NOSCOPE-NEXT: [[ALLOCA_0_HWASAN:%.*]] = inttoptr i64 [[TMP7]] to i8* +; NOSCOPE-NEXT: [[TMP8:%.*]] = trunc i64 [[TMP4]] to i8 +; NOSCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 [[TMP8]], i64 16) +; NOSCOPE-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull [[ALLOCA_0_HWASAN]]) +; NOSCOPE-NEXT: [[TMP9:%.*]] = tail call i1 (...) @cond() +; NOSCOPE-NEXT: br i1 [[TMP9]], label [[TMP10:%.*]], label [[TMP11:%.*]] +; NOSCOPE: 10: +; NOSCOPE-NEXT: call void @use(i8* nonnull [[ALLOCA_0_HWASAN]]) +; NOSCOPE-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull [[ALLOCA_0_HWASAN]]) +; NOSCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 0, i64 16) +; NOSCOPE-NEXT: ret i32 0 +; NOSCOPE: 11: +; NOSCOPE-NEXT: call void @__hwasan_tag_memory(i8* [[TMP2]], i8 0, i64 16) +; NOSCOPE-NEXT: ret i32 0 +; + %1 = alloca i8, align 1 + call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %1) + %2 = tail call i1 (...) @cond() #2 + br i1 %2, label %3, label %4 + +3: + call void @use(i8* nonnull %1) #2 + call void @llvm.lifetime.end.p0i8(i64 1, i8* nonnull %1) + ret i32 0 + +4: + ret i32 0 +} + +declare dso_local i1 @cond(...) local_unnamed_addr + +declare dso_local void @use(i8*) local_unnamed_addr + +; 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)