diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -271,6 +271,11 @@ cl::desc("conservative handling of inline assembly"), cl::Hidden, cl::init(true)); +static cl::opt ClPassCallerToRuntime( + "msan-pass-caller-to-runtime", + cl::desc("(KMSAN only) pass caller address to the runtime to detect calls " + "from non-instrumented code"), cl::Hidden, cl::init(false)); + // This flag controls whether we check the shadow of the address // operand of load or store. Such bugs are very rare, since load from // a garbage address typically results in SEGV, but still happen @@ -709,8 +714,18 @@ ArrayType::get(IRB.getInt64Ty(), kParamTLSSize / 8), /* va_arg_origin */ IRB.getInt64Ty(), ArrayType::get(OriginTy, kParamTLSSize / 4), OriginTy, OriginTy); - MsanGetContextStateFn = M.getOrInsertFunction( - "__msan_get_context_state", PointerType::get(MsanContextStateTy, 0)); + + if (ClPassCallerToRuntime) { + // __msan_get_context_state() takes the result of __builtin_return_address(0). + // Used by Linux. + MsanGetContextStateFn = M.getOrInsertFunction( + "__msan_get_context_state", PointerType::get(MsanContextStateTy, 0), + PointerType::get(IRB.getInt8Ty(), 0)); + } else { + // __msan_get_context_state() doesn't take any parameters. Used by *BSD systems. + MsanGetContextStateFn = M.getOrInsertFunction( + "__msan_get_context_state", PointerType::get(MsanContextStateTy, 0)); + } Type *RetTy = StructType::get(PointerType::get(IRB.getInt8Ty(), 0), PointerType::get(IRB.getInt32Ty(), 0)); @@ -1252,7 +1267,15 @@ // Returns the last instruction in the new prologue void insertKmsanPrologue(IRBuilder<> &IRB) { - Value *ContextState = IRB.CreateCall(MS.MsanGetContextStateFn, {}); + Value *ContextState = nullptr; + if (ClPassCallerToRuntime) { + Value *RetAddr = IRB.CreateCall( + Intrinsic::getDeclaration(F.getParent(), Intrinsic::returnaddress), + IRB.getInt32(0)); + ContextState = IRB.CreateCall(MS.MsanGetContextStateFn, RetAddr); + } else { + ContextState = IRB.CreateCall(MS.MsanGetContextStateFn, {}); + } Constant *Zero = IRB.getInt32(0); MS.ParamTLS = IRB.CreateGEP(MS.MsanContextStateTy, ContextState, {Zero, IRB.getInt32(0)}, "param_shadow"); diff --git a/llvm/test/Instrumentation/MemorySanitizer/msan_kernel_pass-caller-to-runtime.ll b/llvm/test/Instrumentation/MemorySanitizer/msan_kernel_pass-caller-to-runtime.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/MemorySanitizer/msan_kernel_pass-caller-to-runtime.ll @@ -0,0 +1,22 @@ +; Test KMSAN behavior with and without -msan-pass-caller-to-runtime. +; RUN: opt < %s -msan-kernel=1 -S -passes=msan 2>&1 | FileCheck %s -check-prefixes=CHECK-NOPASS +; RUN: opt < %s -msan-kernel=1 -S -passes=msan -msan-pass-caller-to-runtime=1 2>&1 | FileCheck %s -check-prefixes=CHECK-PASS + +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-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Check the instrumentation prologue. +define void @Empty() nounwind uwtable sanitize_memory { +entry: + ret void +} + +; CHECK-LABEL: @Empty +; CHECK: entry: +; BSD systems: __msan_get_context_state() does not accept parameters. +; CHECK-NOPASS: @__msan_get_context_state() +; Linux systems: pass __builtin_return_address(0) to __msan_get_context_state(). +; CHECK-PASS: @llvm.returnaddress +; CHECK-PASS: @__msan_get_context_state({{.*}}) +; %param_shadow: +; CHECK: getelementptr {{.*}} i32 0, i32 0