Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -22,9 +22,11 @@ #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" #include "llvm/Analysis/MemoryBuiltins.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/Argument.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DataLayout.h" @@ -747,6 +749,9 @@ bool runOnFunction() { if (!ClStack) return false; + + copyArgsPassedByValToAllocas(); + // Collect alloca, ret, lifetime instructions etc. for (BasicBlock *BB : depth_first(&F.getEntryBlock())) visit(*BB); @@ -763,6 +768,11 @@ return true; } + // Arguments marked with the "byval" attribute are implicitly copied without + // using an alloca instruction. To produce redzones for those arguments, we + // copy them a second time into memory allocated with an alloca instruction. + void copyArgsPassedByValToAllocas(); + // Finds all Alloca instructions and puts // poisoned red zones around all of them. // Then unpoison everything back before the function returns. @@ -2528,6 +2538,32 @@ llvm_unreachable("impossible LocalStackSize"); } +void FunctionStackPoisoner::copyArgsPassedByValToAllocas() { + BasicBlock &FirstBB = *F.begin(); + IRBuilder<> IRB(&FirstBB, FirstBB.getFirstInsertionPt()); + const DataLayout &DL = F.getParent()->getDataLayout(); + unsigned ArgNum = 0; + for (Argument &Arg : F.args()) { + if (Arg.hasByValAttr()) { + Type *Ty = Arg.getType()->getPointerElementType(); + unsigned Align = Arg.getParamAlignment(); + if (Align == 0) { + Align = DL.getABITypeAlignment(Ty); + } + + const StringRef &Name = Arg.getName().empty() ? + StringRef("Arg" + std::to_string(ArgNum)) : Arg.getName(); + AllocaInst *AI = IRB.CreateAlloca(Ty, nullptr, Twine(Name) + ".byval"); + AI->setAlignment(Align); + Arg.replaceAllUsesWith(AI); + + uint64_t AllocSize = DL.getTypeAllocSize(Ty); + IRB.CreateMemCpy(AI, &Arg, AllocSize, Align); + } + ++ArgNum; + } +} + PHINode *FunctionStackPoisoner::createPHI(IRBuilder<> &IRB, Value *Cond, Value *ValueIfTrue, Instruction *ThenTerm, Index: test/Instrumentation/AddressSanitizer/stack-poisoning-byval-args.ll =================================================================== --- /dev/null +++ test/Instrumentation/AddressSanitizer/stack-poisoning-byval-args.ll @@ -0,0 +1,22 @@ +; This check verifies that arguments passed by value get redzones. +; RUN: opt < %s -asan -S | FileCheck %s + +%struct.A = type { [8 x i32] } + +declare i32 @bar(%struct.A*) + +define void @foo(%struct.A* byval align 8 %a) sanitize_address { +entry: +; CHECK-LABEL: foo +; CHECK: call i64 @__asan_stack_malloc +; CHECK: alloca i8, i64 +; CHECK: [[copyPtr:%[^ \t]+]] = inttoptr i64 %{{[^ \t]+}} to %struct.A* +; CHECK: [[copyBytePtr:%[^ \t]+]] = bitcast %struct.A* [[copyPtr]] +; CHECK: [[aBytePtr:%[^ \t]+]] = bitcast %struct.A* %a +; CHECK: call void @llvm.memcpy{{[^%]+}}[[copyBytePtr]]{{[^%]+}}[[aBytePtr]] +; CHECK: call i32 @bar(%struct.A* [[copyPtr]]) +; CHECK: ret void + + %call = call i32 @bar(%struct.A* %a) + ret void +}