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,29 @@ llvm_unreachable("impossible LocalStackSize"); } +void FunctionStackPoisoner::copyArgsPassedByValToAllocas() { + BasicBlock &FirstBB = *F.begin(); + Instruction *InsBefore = dyn_cast(FirstBB.getFirstInsertionPt()); + IRBuilder<> IRB(InsBefore); + unsigned ArgNum = 0; + for (Argument &Arg : F.args()) { + if (Arg.hasByValAttr()) { + Type *Ty = Arg.getType()->getPointerElementType(); + unsigned Align = Arg.getParamAlignment(); + + AllocaInst *AI = + IRB.CreateAlloca(Ty, nullptr, Twine(Arg.getName()) + ".byval"); + AI->setAlignment(Align); + Arg.replaceAllUsesWith(AI); + + const Module *M = F.getParent(); + uint64_t AllocSize = M->getDataLayout().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 +}