[PATCH] D126385: [msan] Implement -msan-pass-caller-to-runtime.

Alexander Potapenko via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Wed May 25 08:10:28 PDT 2022


glider created this revision.
glider added reviewers: eugenis, vitalybuka.
Herald added a subscriber: hiraditya.
Herald added a project: All.
glider requested review of this revision.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

Linux kernel has a concept of noinstr code, which is used to prevent
all kinds of instrumentation for annotated functions.
In particular, syscall and IRQ entry functions are implemented as
noinstr.

When these functions call KMSAN-instrumented functions, they fail to
properly set up the metadata for function arguments, potentially leading
to false positive reports.

In order to detect transitions from noinstr to instrumented code, we
introduce the -msan-pass-caller-to-runtime flag, which allows KMSAN to
pass the caller address to __msan_get_context_state(). That address can
be used by the runtime to figure out whether a call happened from a
noinstr function, and wipe the context state, preventing the error
reports.

For backward compatibility with BSD systems that use KMSAN, we keep
-msan-pass-caller-to-runtime=0 a default value.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D126385

Files:
  llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
  llvm/test/Instrumentation/MemorySanitizer/msan_kernel_pass-caller-to-runtime.ll


Index: llvm/test/Instrumentation/MemorySanitizer/msan_kernel_pass-caller-to-runtime.ll
===================================================================
--- /dev/null
+++ 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
Index: llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
===================================================================
--- llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
+++ 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<bool> 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");


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D126385.432002.patch
Type: text/x-patch
Size: 3887 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20220525/07e223ed/attachment.bin>


More information about the llvm-commits mailing list