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 @@ -584,6 +589,7 @@ /// KMSAN callback for task-local function argument shadow. StructType *MsanContextStateTy; FunctionCallee MsanGetContextStateFn; + FunctionCallee MsanGetContextStateCallerFn; /// Functions for poisoning/unpoisoning local variables FunctionCallee MsanPoisonAllocaFn, MsanUnpoisonAllocaFn; @@ -700,8 +706,8 @@ WarningFn = M.getOrInsertFunction("__msan_warning", IRB.getVoidTy(), IRB.getInt32Ty()); - // Requests the per-task context state (kmsan_context_state*) from the - // runtime library. + // Per-task context state (kmsan_context_state*) provided by the runtime + // library. MsanContextStateTy = StructType::get( ArrayType::get(IRB.getInt64Ty(), kParamTLSSize / 8), ArrayType::get(IRB.getInt64Ty(), kRetvalTLSSize / 8), @@ -709,9 +715,24 @@ ArrayType::get(IRB.getInt64Ty(), kParamTLSSize / 8), /* va_arg_origin */ IRB.getInt64Ty(), ArrayType::get(OriginTy, kParamTLSSize / 4), OriginTy, OriginTy); + + // __msan_get_context_state() requests the context state from the runtime. + // For BSD kernels it is inserted into every instrumented function. + // For Linux kernels - into every instrumented function without parameters. MsanGetContextStateFn = M.getOrInsertFunction( "__msan_get_context_state", PointerType::get(MsanContextStateTy, 0)); + if (ClPassCallerToRuntime) { + // __msan_get_context_state_caller() takes the result of __builtin_return_address(0). + // When building Linux with KMSAN, __msan_get_context_state_caller() is used to + // request the context state for functions with one or more parameters. + MsanGetContextStateCallerFn = M.getOrInsertFunction( + "__msan_get_context_state_caller", PointerType::get(MsanContextStateTy, 0), + PointerType::get(IRB.getInt8Ty(), 0)); + } else { + MsanGetContextStateCallerFn = nullptr; + } + Type *RetTy = StructType::get(PointerType::get(IRB.getInt8Ty(), 0), PointerType::get(IRB.getInt32Ty(), 0)); @@ -1252,7 +1273,16 @@ // Returns the last instruction in the new prologue void insertKmsanPrologue(IRBuilder<> &IRB) { - Value *ContextState = IRB.CreateCall(MS.MsanGetContextStateFn, {}); + Value *ContextState = nullptr; + if (MS.MsanGetContextStateCallerFn && F.arg_size() > 0) { + Value *RetAddr = IRB.CreateCall( + Intrinsic::getDeclaration(F.getParent(), Intrinsic::returnaddress), + IRB.getInt32(0)); + ContextState = IRB.CreateCall(MS.MsanGetContextStateCallerFn, 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,35 @@ +; 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 for a function without arguments. +define void @FnNoArgs() nounwind uwtable sanitize_memory { +entry: + ret void +} + +; CHECK-LABEL: @FnNoArgs +; CHECK: entry: +; BSD and Linux systems: instrument with __msan_get_context_state(). +; CHECK: @__msan_get_context_state() +; %param_shadow: +; CHECK: getelementptr {{.*}} i32 0, i32 0 + +; Check the instrumentation prologue for a function with arguments. +define void @FnOneArg(i32 %input) nounwind uwtable sanitize_memory { +entry: + ret void +} + +; CHECK-LABEL: @FnOneArg +; CHECK: entry: +; BSD systems instrument with __msan_get_context_state() that does not accept parameters. +; CHECK-NOPASS: @__msan_get_context_state() +; Linux systems: pass __builtin_return_address(0) to __msan_get_context_state_caller(). +; CHECK-PASS: @llvm.returnaddress +; CHECK-PASS: @__msan_get_context_state_caller({{.*}}) +; %param_shadow: +; CHECK: getelementptr {{.*}} i32 0, i32 0