Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -106,14 +106,15 @@ static const char *const kAsanOptionDetectUAR = "__asan_option_detect_stack_use_after_return"; +static const char *const kAsanAllocaPoison = + "__asan_alloca_poison"; +static const char *const kAsanAllocasUnpoison = + "__asan_allocas_unpoison"; + // Accesses sizes are powers of two: 1, 2, 4, 8, 16. static const size_t kNumberOfAccessSizes = 5; static const unsigned kAllocaRzSize = 32; -static const unsigned kAsanAllocaLeftMagic = 0xcacacacaU; -static const unsigned kAsanAllocaRightMagic = 0xcbcbcbcbU; -static const unsigned kAsanAllocaPartialVal1 = 0xcbcbcb00U; -static const unsigned kAsanAllocaPartialVal2 = 0x000000cbU; // Command-line flags. @@ -230,8 +231,6 @@ STATISTIC(NumInstrumentedReads, "Number of instrumented reads"); STATISTIC(NumInstrumentedWrites, "Number of instrumented writes"); -STATISTIC(NumInstrumentedDynamicAllocas, - "Number of instrumented dynamic allocas"); STATISTIC(NumOptimizedAccessesToGlobalVar, "Number of optimized accesses to global vars"); STATISTIC(NumOptimizedAccessesToStackVar, @@ -402,6 +401,12 @@ } /// Check if we want (and can) handle this alloca. bool isInterestingAlloca(AllocaInst &AI); + + // Check if we have dynamic alloca. + bool isDynamicAlloca(AllocaInst &AI) const { + return AI.isArrayAllocation() || !AI.isStaticAlloca(); + } + /// If it is an interesting memory access, return the PointerOperand /// and set IsWrite/Alignment. Otherwise return nullptr. Value *isInterestingMemoryAccess(Instruction *I, bool *IsWrite, @@ -517,6 +522,7 @@ Function *AsanStackMallocFunc[kMaxAsanStackMallocSizeClass + 1], *AsanStackFreeFunc[kMaxAsanStackMallocSizeClass + 1]; Function *AsanPoisonStackMemoryFunc, *AsanUnpoisonStackMemoryFunc; + Function *AsanAllocaPoisonFunc, *AsanAllocasUnpoisonFunc; // Stores a place and arguments of poisoning/unpoisoning call for alloca. struct AllocaPoisonCall { @@ -527,23 +533,9 @@ }; SmallVector AllocaPoisonCallVec; - // Stores left and right redzone shadow addresses for dynamic alloca - // and pointer to alloca instruction itself. - // LeftRzAddr is a shadow address for alloca left redzone. - // RightRzAddr is a shadow address for alloca right redzone. - struct DynamicAllocaCall { - AllocaInst *AI; - Value *LeftRzAddr; - Value *RightRzAddr; - bool Poison; - explicit DynamicAllocaCall(AllocaInst *AI, Value *LeftRzAddr = nullptr, - Value *RightRzAddr = nullptr) - : AI(AI), - LeftRzAddr(LeftRzAddr), - RightRzAddr(RightRzAddr), - Poison(true) {} - }; - SmallVector DynamicAllocaVec; + SmallVector DynamicAllocaVec; + SmallVector StackRestoreVec; + AllocaInst *DynamicAllocaLayout = nullptr; // Maps Value to an AllocaInst from which the Value is originated. typedef DenseMap AllocaForValueMapTy; @@ -586,41 +578,29 @@ // Then unpoison everything back before the function returns. void poisonStack(); + void createDynamicAllocasInitStorage(); + // ----------------------- Visitors. /// \brief Collect all Ret instructions. void visitReturnInst(ReturnInst &RI) { RetVec.push_back(&RI); } - // Unpoison dynamic allocas redzones. - void unpoisonDynamicAlloca(DynamicAllocaCall &AllocaCall) { - if (!AllocaCall.Poison) return; - for (auto Ret : RetVec) { - IRBuilder<> IRBRet(Ret); - PointerType *Int32PtrTy = PointerType::getUnqual(IRBRet.getInt32Ty()); - Value *Zero = Constant::getNullValue(IRBRet.getInt32Ty()); - Value *PartialRzAddr = IRBRet.CreateSub(AllocaCall.RightRzAddr, - ConstantInt::get(IntptrTy, 4)); - IRBRet.CreateStore( - Zero, IRBRet.CreateIntToPtr(AllocaCall.LeftRzAddr, Int32PtrTy)); - IRBRet.CreateStore(Zero, - IRBRet.CreateIntToPtr(PartialRzAddr, Int32PtrTy)); - IRBRet.CreateStore( - Zero, IRBRet.CreateIntToPtr(AllocaCall.RightRzAddr, Int32PtrTy)); - } + void unpoisonDynamicAllocasBeforeInst(Instruction *InstBefore, + Value *SavedStack) { + IRBuilder<> IRB(InstBefore); + IRB.CreateCall2(AsanAllocasUnpoisonFunc, + IRB.CreateLoad(DynamicAllocaLayout), + IRB.CreatePtrToInt(SavedStack, IntptrTy)); } - // Right shift for BigEndian and left shift for LittleEndian. - Value *shiftAllocaMagic(Value *Val, IRBuilder<> &IRB, Value *Shift) { - auto &DL = F.getParent()->getDataLayout(); - return DL.isLittleEndian() ? IRB.CreateShl(Val, Shift) - : IRB.CreateLShr(Val, Shift); - } + // Unpoison dynamic allocas redzones. + void unpoisonDynamicAllocas() { + for (auto &Ret : RetVec) + unpoisonDynamicAllocasBeforeInst(Ret, DynamicAllocaLayout); - // Compute PartialRzMagic for dynamic alloca call. Since we don't know the - // size of requested memory until runtime, we should compute it dynamically. - // If PartialSize is 0, PartialRzMagic would contain kAsanAllocaRightMagic, - // otherwise it would contain the value that we will use to poison the - // partial redzone for alloca call. - Value *computePartialRzMagic(Value *PartialSize, IRBuilder<> &IRB); + for (auto &StackRestoreInst : StackRestoreVec) + unpoisonDynamicAllocasBeforeInst(StackRestoreInst, + StackRestoreInst->getOperand(0)); + } // Deploy and poison redzones around dynamic alloca call. To do this, we // should replace this call with another one with changed parameters and @@ -632,20 +612,15 @@ // addr = tmp + 32 (first 32 bytes are for the left redzone). // Additional_size is added to make new memory allocation contain not only // requested memory, but also left, partial and right redzones. - // After that, we should poison redzones: - // (1) Left redzone with kAsanAllocaLeftMagic. - // (2) Partial redzone with the value, computed in runtime by - // computePartialRzMagic function. - // (3) Right redzone with kAsanAllocaRightMagic. - void handleDynamicAllocaCall(DynamicAllocaCall &AllocaCall); + void handleDynamicAllocaCall(AllocaInst *AI); /// \brief Collect Alloca instructions we want (and can) handle. void visitAllocaInst(AllocaInst &AI) { if (!ASan.isInterestingAlloca(AI)) return; StackAlignment = std::max(StackAlignment, AI.getAlignment()); - if (isDynamicAlloca(AI)) - DynamicAllocaVec.push_back(DynamicAllocaCall(&AI)); + if (ASan.isDynamicAlloca(AI)) + DynamicAllocaVec.push_back(&AI); else AllocaVec.push_back(&AI); } @@ -653,8 +628,9 @@ /// \brief Collect lifetime intrinsic calls to check for use-after-scope /// errors. void visitIntrinsicInst(IntrinsicInst &II) { - if (!ClCheckLifetime) return; Intrinsic::ID ID = II.getIntrinsicID(); + if (ID == Intrinsic::stackrestore) StackRestoreVec.push_back(&II); + if (!ClCheckLifetime) return; if (ID != Intrinsic::lifetime_start && ID != Intrinsic::lifetime_end) return; // Found lifetime intrinsic, add ASan instrumentation if necessary. @@ -690,9 +666,6 @@ return true; } - bool isDynamicAlloca(AllocaInst &AI) const { - return AI.isArrayAllocation() || !AI.isStaticAlloca(); - } /// Finds alloca where the value comes from. AllocaInst *findAllocaForValue(Value *V); void poisonRedZones(ArrayRef ShadowBytes, IRBuilder<> &IRB, @@ -811,12 +784,14 @@ if (PreviouslySeenAllocaInfo != ProcessedAllocas.end()) return PreviouslySeenAllocaInfo->getSecond(); - bool IsInteresting = (AI.getAllocatedType()->isSized() && - // alloca() may be called with 0 size, ignore it. - getAllocaSizeInBytes(&AI) > 0 && - // We are only interested in allocas not promotable to registers. - // Promotable allocas are common under -O0. - (!ClSkipPromotableAllocas || !isAllocaPromotable(&AI))); + bool IsInteresting = + (AI.getAllocatedType()->isSized() && + // alloca() may be called with 0 size, ignore it. + getAllocaSizeInBytes(&AI) > 0 && + // We are only interested in allocas not promotable to registers. + // Promotable allocas are common under -O0. + (!ClSkipPromotableAllocas || !isAllocaPromotable(&AI) || + isDynamicAlloca(AI))); ProcessedAllocas[&AI] = IsInteresting; return IsInteresting; @@ -1623,6 +1598,11 @@ AsanUnpoisonStackMemoryFunc = checkSanitizerInterfaceFunction( M.getOrInsertFunction(kAsanUnpoisonStackMemoryName, IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr)); + AsanAllocaPoisonFunc = checkSanitizerInterfaceFunction(M.getOrInsertFunction( + kAsanAllocaPoison, IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr)); + AsanAllocasUnpoisonFunc = + checkSanitizerInterfaceFunction(M.getOrInsertFunction( + kAsanAllocasUnpoison, IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr)); } void FunctionStackPoisoner::poisonRedZones(ArrayRef ShadowBytes, @@ -1718,15 +1698,24 @@ return IRB.CreatePointerCast(Alloca, IntptrTy); } +void FunctionStackPoisoner::createDynamicAllocasInitStorage() { + BasicBlock &FirstBB = *F.begin(); + IRBuilder<> IRB(dyn_cast(FirstBB.begin())); + DynamicAllocaLayout = IRB.CreateAlloca(IntptrTy, nullptr); + IRB.CreateStore(Constant::getNullValue(IntptrTy), DynamicAllocaLayout); + DynamicAllocaLayout->setAlignment(32); +} + void FunctionStackPoisoner::poisonStack() { assert(AllocaVec.size() > 0 || DynamicAllocaVec.size() > 0); - if (ClInstrumentAllocas) { + if (ClInstrumentAllocas && DynamicAllocaVec.size() > 0) { // Handle dynamic allocas. - for (auto &AllocaCall : DynamicAllocaVec) { - handleDynamicAllocaCall(AllocaCall); - unpoisonDynamicAlloca(AllocaCall); - } + createDynamicAllocasInitStorage(); + for (auto &AI : DynamicAllocaVec) + handleDynamicAllocaCall(AI); + + unpoisonDynamicAllocas(); } if (AllocaVec.size() == 0) return; @@ -1961,78 +1950,25 @@ return Res; } -// Compute PartialRzMagic for dynamic alloca call. PartialRzMagic is -// constructed from two separate 32-bit numbers: PartialRzMagic = Val1 | Val2. -// (1) Val1 is resposible for forming base value for PartialRzMagic, containing -// only 00 for fully addressable and 0xcb for fully poisoned bytes for each -// 8-byte chunk of user memory respectively. -// (2) Val2 forms the value for marking first poisoned byte in shadow memory -// with appropriate value (0x01 - 0x07 or 0xcb if Padding % 8 == 0). - -// Shift = Padding & ~7; // the number of bits we need to shift to access first -// chunk in shadow memory, containing nonzero bytes. -// Example: -// Padding = 21 Padding = 16 -// Shadow: |00|00|05|cb| Shadow: |00|00|cb|cb| -// ^ ^ -// | | -// Shift = 21 & ~7 = 16 Shift = 16 & ~7 = 16 -// -// Val1 = 0xcbcbcbcb << Shift; -// PartialBits = Padding ? Padding & 7 : 0xcb; -// Val2 = PartialBits << Shift; -// Result = Val1 | Val2; -Value *FunctionStackPoisoner::computePartialRzMagic(Value *PartialSize, - IRBuilder<> &IRB) { - PartialSize = IRB.CreateIntCast(PartialSize, IRB.getInt32Ty(), false); - Value *Shift = IRB.CreateAnd(PartialSize, IRB.getInt32(~7)); - unsigned Val1Int = kAsanAllocaPartialVal1; - unsigned Val2Int = kAsanAllocaPartialVal2; - if (!F.getParent()->getDataLayout().isLittleEndian()) { - Val1Int = sys::getSwappedBytes(Val1Int); - Val2Int = sys::getSwappedBytes(Val2Int); - } - Value *Val1 = shiftAllocaMagic(IRB.getInt32(Val1Int), IRB, Shift); - Value *PartialBits = IRB.CreateAnd(PartialSize, IRB.getInt32(7)); - // For BigEndian get 0x000000YZ -> 0xYZ000000. - if (F.getParent()->getDataLayout().isBigEndian()) - PartialBits = IRB.CreateShl(PartialBits, IRB.getInt32(24)); - Value *Val2 = IRB.getInt32(Val2Int); - Value *Cond = - IRB.CreateICmpNE(PartialBits, Constant::getNullValue(IRB.getInt32Ty())); - Val2 = IRB.CreateSelect(Cond, shiftAllocaMagic(PartialBits, IRB, Shift), - shiftAllocaMagic(Val2, IRB, Shift)); - return IRB.CreateOr(Val1, Val2); -} - -void FunctionStackPoisoner::handleDynamicAllocaCall( - DynamicAllocaCall &AllocaCall) { - AllocaInst *AI = AllocaCall.AI; - if (!doesDominateAllExits(AI)) { - // We do not yet handle complex allocas - AllocaCall.Poison = false; - return; - } - +void FunctionStackPoisoner::handleDynamicAllocaCall(AllocaInst *AI) { IRBuilder<> IRB(AI); - PointerType *Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty()); const unsigned Align = std::max(kAllocaRzSize, AI->getAlignment()); const uint64_t AllocaRedzoneMask = kAllocaRzSize - 1; Value *Zero = Constant::getNullValue(IntptrTy); Value *AllocaRzSize = ConstantInt::get(IntptrTy, kAllocaRzSize); Value *AllocaRzMask = ConstantInt::get(IntptrTy, AllocaRedzoneMask); - Value *NotAllocaRzMask = ConstantInt::get(IntptrTy, ~AllocaRedzoneMask); // Since we need to extend alloca with additional memory to locate // redzones, and OldSize is number of allocated blocks with // ElementSize size, get allocated memory size in bytes by // OldSize * ElementSize. - unsigned ElementSize = + const unsigned ElementSize = F.getParent()->getDataLayout().getTypeAllocSize(AI->getAllocatedType()); - Value *OldSize = IRB.CreateMul(AI->getArraySize(), - ConstantInt::get(IntptrTy, ElementSize)); + Value *OldSize = + IRB.CreateMul(IRB.CreateIntCast(AI->getArraySize(), IntptrTy, false), + ConstantInt::get(IntptrTy, ElementSize)); // PartialSize = OldSize % 32 Value *PartialSize = IRB.CreateAnd(OldSize, AllocaRzMask); @@ -2060,43 +1996,20 @@ Value *NewAddress = IRB.CreateAdd(IRB.CreatePtrToInt(NewAlloca, IntptrTy), ConstantInt::get(IntptrTy, Align)); - Value *NewAddressPtr = IRB.CreateIntToPtr(NewAddress, AI->getType()); - - // LeftRzAddress = NewAddress - kAllocaRzSize - Value *LeftRzAddress = IRB.CreateSub(NewAddress, AllocaRzSize); + // Insert __asan_alloca_poison call for new created alloca. + IRB.CreateCall2(AsanAllocaPoisonFunc, NewAddress, OldSize); - // Poisoning left redzone. - AllocaCall.LeftRzAddr = ASan.memToShadow(LeftRzAddress, IRB); - IRB.CreateStore(ConstantInt::get(IRB.getInt32Ty(), kAsanAllocaLeftMagic), - IRB.CreateIntToPtr(AllocaCall.LeftRzAddr, Int32PtrTy)); + // Store the last alloca's address to DynamicAllocaLayout. We'll need this + // for unpoisoning stuff. + IRB.CreateStore(IRB.CreatePtrToInt(NewAlloca, IntptrTy), DynamicAllocaLayout); - // PartialRzAligned = PartialRzAddr & ~AllocaRzMask - Value *PartialRzAddr = IRB.CreateAdd(NewAddress, OldSize); - Value *PartialRzAligned = IRB.CreateAnd(PartialRzAddr, NotAllocaRzMask); - - // Poisoning partial redzone. - Value *PartialRzMagic = computePartialRzMagic(PartialSize, IRB); - Value *PartialRzShadowAddr = ASan.memToShadow(PartialRzAligned, IRB); - IRB.CreateStore(PartialRzMagic, - IRB.CreateIntToPtr(PartialRzShadowAddr, Int32PtrTy)); - - // RightRzAddress - // = (PartialRzAddr + AllocaRzMask) & ~AllocaRzMask - Value *RightRzAddress = IRB.CreateAnd( - IRB.CreateAdd(PartialRzAddr, AllocaRzMask), NotAllocaRzMask); - - // Poisoning right redzone. - AllocaCall.RightRzAddr = ASan.memToShadow(RightRzAddress, IRB); - IRB.CreateStore(ConstantInt::get(IRB.getInt32Ty(), kAsanAllocaRightMagic), - IRB.CreateIntToPtr(AllocaCall.RightRzAddr, Int32PtrTy)); + Value *NewAddressPtr = IRB.CreateIntToPtr(NewAddress, AI->getType()); - // Replace all uses of AddessReturnedByAlloca with NewAddress. + // Replace all uses of AddessReturnedByAlloca with NewAddressPtr. AI->replaceAllUsesWith(NewAddressPtr); - // We are done. Erase old alloca and store left, partial and right redzones - // shadow addresses for future unpoisoning. + // We are done. Erase old alloca from parent. AI->eraseFromParent(); - NumInstrumentedDynamicAllocas++; } // isSafeAccess returns true if Addr is always inbounds with respect to its Index: lib/asan/asan_fake_stack.cc =================================================================== --- lib/asan/asan_fake_stack.cc +++ lib/asan/asan_fake_stack.cc @@ -22,6 +22,9 @@ static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; +static const u64 kAllocaRedzoneSize = 32UL; +static const u64 kAllocaRedzoneMask = 31UL; + // For small size classes inline PoisonShadow for better performance. ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3. @@ -253,4 +256,24 @@ if (end) *end = reinterpret_cast(frame_end); return reinterpret_cast(frame->real_stack); } + +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_alloca_poison(uptr addr, uptr size) { + uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize; + uptr PartialRzAddr = addr + size; + uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask; + uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1); + FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic); + FastPoisonShadowPartialRightRedzone( + PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY, + RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic); + FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_allocas_unpoison(uptr top, uptr bottom) { + if ((!top) || (top > bottom)) return; + REAL(memset)(reinterpret_cast(MemToShadow(top)), 0, + (bottom - top) / SHADOW_GRANULARITY); +} } // extern "C" Index: lib/asan/asan_interface_internal.h =================================================================== --- lib/asan/asan_interface_internal.h +++ lib/asan/asan_interface_internal.h @@ -195,6 +195,10 @@ void __asan_poison_intra_object_redzone(uptr p, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void __asan_unpoison_intra_object_redzone(uptr p, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_alloca_poison(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_allocas_unpoison(uptr top, uptr bottom); } // extern "C" #endif // ASAN_INTERFACE_INTERNAL_H Index: test/Instrumentation/AddressSanitizer/instrument-dynamic-allocas.ll =================================================================== --- test/Instrumentation/AddressSanitizer/instrument-dynamic-allocas.ll +++ test/Instrumentation/AddressSanitizer/instrument-dynamic-allocas.ll @@ -2,17 +2,13 @@ ; -asan-instrument-allocas=1 ; RUN: opt < %s -asan -asan-module -asan-instrument-allocas=1 -S | FileCheck %s --check-prefix=CHECK-ALLOCA -; RUN: opt < %s -asan -asan-module -asan-instrument-allocas=0 -S | FileCheck %s --check-prefix=CHECK-NOALLOCA -; RUN: opt < %s -asan -asan-module -S | FileCheck %s --check-prefix=CHECK-NOALLOCA target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-unknown-linux-gnu" define void @foo(i32 %len) sanitize_address { entry: -; CHECK-ALLOCA: store i32 -892679478 -; CHECK-ALLOCA: store i32 -875836469 -; CHECK-NOALLOCA-NOT: store i32 -892679478 -; CHECK-NOALLOCA-NOT: store i32 -875836469 +; CHECK-ALLOCA: __asan_alloca_poison +; CHECK-ALLOCA: __asan_allocas_unpoison %0 = alloca i32, align 4 %1 = alloca i8* store volatile i32 %len, i32* %0, align 4 Index: test/Instrumentation/AddressSanitizer/undecidable-dynamic-alloca-1.ll =================================================================== --- test/Instrumentation/AddressSanitizer/undecidable-dynamic-alloca-1.ll +++ /dev/null @@ -1,23 +0,0 @@ -; Test that undecidable dynamic allocas are skipped by ASan. - -; RUN: opt < %s -asan -asan-module -asan-instrument-allocas=1 -S | FileCheck %s -target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" -target triple = "x86_64-unknown-linux-gnu" - -define void @g(i64 %n) sanitize_address { -entry: - %cmp = icmp sgt i64 %n, 100 - br i1 %cmp, label %do_alloca, label %done - -do_alloca: -; CHECK-NOT: store i32 -892679478 - %0 = alloca i8, i64 %n, align 1 - call void @f(i8* %0) - br label %done - -done: - ret void -} - -declare void @f(i8*) - Index: test/asan/TestCases/Linux/interface_symbols_linux.c =================================================================== --- test/asan/TestCases/Linux/interface_symbols_linux.c +++ test/asan/TestCases/Linux/interface_symbols_linux.c @@ -38,6 +38,8 @@ // RUN: echo __asan_report_exp_store_n >> %t.interface // RUN: echo __asan_get_current_fake_stack >> %t.interface // RUN: echo __asan_addr_is_in_fake_stack >> %t.interface +// RUN: echo __asan_alloca_poison >> %t.interface +// RUN: echo __asan_allocas_unpoison >> %t.interface // RUN: cat %t.interface | sort -u | diff %t.symbols - // FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing Index: test/asan/TestCases/alloca_loop_unpoisoning.cc =================================================================== --- /dev/null +++ test/asan/TestCases/alloca_loop_unpoisoning.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: %run %t 2>&1 +// + +// This testcase checks that allocas and VLAs inside loop are correctly unpoisoned. + +#include +#include +#include +#include "sanitizer/asan_interface.h" + +void *top, *bot; + +__attribute__((noinline)) void foo(int len) { + char x; + top = &x; + char array[len]; // NOLINT + assert(!(reinterpret_cast(array) & 31L)); + alloca(len); + for (int i = 0; i < 32; ++i) { + char array[i]; // NOLINT + bot = alloca(i); + assert(!(reinterpret_cast(bot) & 31L)); + } +} + +int main(int argc, char **argv) { + foo(32); + void *q = __asan_region_is_poisoned(bot, (char *)top - (char *)bot); + assert(!q); + return 0; +} Index: test/asan/TestCases/alloca_vla_interact.cc =================================================================== --- /dev/null +++ test/asan/TestCases/alloca_vla_interact.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: %run %t 2>&1 +// + +// This testcase checks correct interaction between VLAs and allocas. + +#include +#include +#include +#include "sanitizer/asan_interface.h" + +#define RZ 32 + +__attribute__((noinline)) void foo(int len) { + char *top, *bot; + // This alloca call should live until the end of foo. + char *alloca1 = (char *)alloca(len); + assert(!(reinterpret_cast(alloca1) & 31L)); + // This should be first poisoned address after loop. + top = alloca1 - RZ; + for (int i = 0; i < 32; ++i) { + // Check that previous alloca was unpoisoned at the end of iteration. + if (i) assert(!__asan_region_is_poisoned(bot, 96)); + // VLA is unpoisoned at the end of iteration. + volatile char array[i]; + assert(!(reinterpret_cast(array) & 31L)); + // Alloca is unpoisoned at the end of iteration, + // because dominated by VLA. + bot = (char *)alloca(i) - RZ; + } + // Check that all allocas from loop were unpoisoned correctly. + void *q = __asan_region_is_poisoned(bot, (char *)top - (char *)bot + 1); + assert(q == top); +} + +int main(int argc, char **argv) { + foo(32); + return 0; +} Index: test/asan/TestCases/vla_chrome_testcase.cc =================================================================== --- /dev/null +++ test/asan/TestCases/vla_chrome_testcase.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +// This is reduced testcase based on Chromium code. +// See http://reviews.llvm.org/D6055?vs=on&id=15616&whitespace=ignore-all#toc. + +#include +#include + +int a = 7; +int b; +int c; +int *p; + +__attribute__((noinline)) void fn3(int *first, int second) { +} + +int main() { + int d = b && c; + int e[a]; // NOLINT + assert(!(reinterpret_cast(e) & 31L)); + int f; + if (d) + fn3(&f, sizeof 0 * (&c - e)); + e[a] = 0; +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 + return 0; +} Index: test/asan/TestCases/vla_condition_overflow.cc =================================================================== --- /dev/null +++ test/asan/TestCases/vla_condition_overflow.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +#include +#include + +__attribute__((noinline)) void foo(int index, int len) { + if (index > len) { + char str[len]; //NOLINT + assert(!(reinterpret_cast(str) & 31L)); + str[index] = '1'; // BOOM +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 + } +} + +int main(int argc, char **argv) { + foo(33, 10); + return 0; +} Index: test/asan/TestCases/vla_loop_overfow.cc =================================================================== --- /dev/null +++ test/asan/TestCases/vla_loop_overfow.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +#include +#include + +void foo(int index, int len) { + for (int i = 1; i < len; ++i) { + char array[len]; // NOLINT + assert(!(reinterpret_cast(array) & 31L)); + array[index + i] = 0; +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 + } +} + +int main(int argc, char **argv) { + foo(9, 21); + return 0; +}