[compiler-rt] [llvm] [msan] Add experimental '-mllvm -msan-embed-faulting-instruction' and MSAN_OPTIONS=print_faulting_instruction (PR #136539)
Thurston Dang via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 21 16:20:44 PDT 2025
https://github.com/thurstond updated https://github.com/llvm/llvm-project/pull/136539
>From aa574a27e6c701777d8d783f9dd6e2574c7b190e Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Tue, 18 Mar 2025 16:20:10 +0000
Subject: [PATCH 1/4] [msan] Add experimental '-mllvm
-msan-print-faulting-instruction'
This adds an experimental flag, -mllvm -msan-print-faulting-instruction,
which will print the LLVM instruction that resulted in an MSan UUM
report.
Although MSan UUM reports will print out the line and column number
(assuming symbolization is available), inlining, macros and LLVM
"auto-upgraded" intrinsics can obscure the root cause.
This patch adds a test case,
compiler-rt/test/msan/print_faulting_inst.cpp, which illustrates that
-msan-print-faulting-instruction can provide more information than line
and column number.
---
compiler-rt/lib/msan/msan.cpp | 74 +++++++++
.../lib/msan/msan_interface_internal.h | 20 +++
compiler-rt/test/msan/print_faulting_inst.cpp | 83 ++++++++++
.../Instrumentation/MemorySanitizer.cpp | 145 ++++++++++++++----
4 files changed, 293 insertions(+), 29 deletions(-)
create mode 100644 compiler-rt/test/msan/print_faulting_inst.cpp
diff --git a/compiler-rt/lib/msan/msan.cpp b/compiler-rt/lib/msan/msan.cpp
index a3c0c2e485af3..a22bfe0d7ab32 100644
--- a/compiler-rt/lib/msan/msan.cpp
+++ b/compiler-rt/lib/msan/msan.cpp
@@ -352,6 +352,29 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
using namespace __msan;
+#define PRINT_FAULTING_INSTRUCTION(instname) \
+ Printf("Instruction that failed the shadow check: %s\n", instname); \
+ Printf("\n");
+
+#define MSAN_MAYBE_WARNING_INSTNAME(type, size, instname) \
+ void __msan_maybe_warning_instname_##size(type s, u32 o, char *instname) { \
+ GET_CALLER_PC_BP; \
+ if (UNLIKELY(s)) { \
+ if (instname) \
+ PRINT_FAULTING_INSTRUCTION(instname); \
+ PrintWarningWithOrigin(pc, bp, o); \
+ if (__msan::flags()->halt_on_error) { \
+ Printf("Exiting\n"); \
+ Die(); \
+ } \
+ } \
+ }
+
+MSAN_MAYBE_WARNING_INSTNAME(u8, 1, instname)
+MSAN_MAYBE_WARNING_INSTNAME(u16, 2, instname)
+MSAN_MAYBE_WARNING_INSTNAME(u32, 4, instname)
+MSAN_MAYBE_WARNING_INSTNAME(u64, 8, instname)
+
#define MSAN_MAYBE_WARNING(type, size) \
void __msan_maybe_warning_##size(type s, u32 o) { \
GET_CALLER_PC_BP; \
@@ -426,6 +449,57 @@ void __msan_warning_with_origin_noreturn(u32 origin) {
Die();
}
+// We duplicate the non _instname function's body because we don't want to
+// pollute the stack traces with an additional function call.
+//
+// We can't use a simple macro wrapper, because the instrumentation pass
+// expects function symbols.
+// We don't add instname as a parameter everywhere (with a check whether the
+// value is null) to avoid polluting the fastpath.
+void __msan_warning_instname(char *instname) {
+ PRINT_FAULTING_INSTRUCTION(instname);
+ GET_CALLER_PC_BP;
+ PrintWarningWithOrigin(pc, bp, 0);
+ if (__msan::flags()->halt_on_error) {
+ if (__msan::flags()->print_stats)
+ ReportStats();
+ Printf("Exiting\n");
+ Die();
+ }
+}
+
+void __msan_warning_noreturn_instname(char *instname) {
+ PRINT_FAULTING_INSTRUCTION(instname);
+ GET_CALLER_PC_BP;
+ PrintWarningWithOrigin(pc, bp, 0);
+ if (__msan::flags()->print_stats)
+ ReportStats();
+ Printf("Exiting\n");
+ Die();
+}
+
+void __msan_warning_with_origin_instname(u32 origin, char *instname) {
+ PRINT_FAULTING_INSTRUCTION(instname);
+ GET_CALLER_PC_BP;
+ PrintWarningWithOrigin(pc, bp, origin);
+ if (__msan::flags()->halt_on_error) {
+ if (__msan::flags()->print_stats)
+ ReportStats();
+ Printf("Exiting\n");
+ Die();
+ }
+}
+
+void __msan_warning_with_origin_noreturn_instname(u32 origin, char *instname) {
+ PRINT_FAULTING_INSTRUCTION(instname);
+ GET_CALLER_PC_BP;
+ PrintWarningWithOrigin(pc, bp, origin);
+ if (__msan::flags()->print_stats)
+ ReportStats();
+ Printf("Exiting\n");
+ Die();
+}
+
static void OnStackUnwind(const SignalContext &sig, const void *,
BufferedStackTrace *stack) {
stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,
diff --git a/compiler-rt/lib/msan/msan_interface_internal.h b/compiler-rt/lib/msan/msan_interface_internal.h
index c2eead13c20cf..5607d08e66d86 100644
--- a/compiler-rt/lib/msan/msan_interface_internal.h
+++ b/compiler-rt/lib/msan/msan_interface_internal.h
@@ -30,12 +30,18 @@ void __msan_init();
SANITIZER_INTERFACE_ATTRIBUTE
void __msan_warning();
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_warning_instname(char *instname);
+
// Print a warning and die.
// Instrumentation inserts calls to this function when building in "fast" mode
// (i.e. -mllvm -msan-keep-going)
SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn))
void __msan_warning_noreturn();
+SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) void
+__msan_warning_noreturn_instname(char *instname);
+
using __sanitizer::uptr;
using __sanitizer::sptr;
using __sanitizer::uu64;
@@ -49,8 +55,13 @@ using __sanitizer::u8;
// Versions of the above which take Origin as a parameter
SANITIZER_INTERFACE_ATTRIBUTE
void __msan_warning_with_origin(u32 origin);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_warning_with_origin_instname(u32 origin, char *instname);
+
SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) void
__msan_warning_with_origin_noreturn(u32 origin);
+SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) void
+__msan_warning_with_origin_noreturn_instname(u32 origin, char *instname);
SANITIZER_INTERFACE_ATTRIBUTE
void __msan_maybe_warning_1(u8 s, u32 o);
@@ -61,6 +72,15 @@ void __msan_maybe_warning_4(u32 s, u32 o);
SANITIZER_INTERFACE_ATTRIBUTE
void __msan_maybe_warning_8(u64 s, u32 o);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_maybe_warning_1_instname(u8 s, u32 o, char *instname);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_maybe_warning_2_instname(u16 s, u32 o, char *instname);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_maybe_warning_4_instname(u32 s, u32 o, char *instname);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_maybe_warning_8_instname(u64 s, u32 o, char *instname);
+
SANITIZER_INTERFACE_ATTRIBUTE
void __msan_maybe_store_origin_1(u8 s, void *p, u32 o);
SANITIZER_INTERFACE_ATTRIBUTE
diff --git a/compiler-rt/test/msan/print_faulting_inst.cpp b/compiler-rt/test/msan/print_faulting_inst.cpp
new file mode 100644
index 0000000000000..09c5f22ada132
--- /dev/null
+++ b/compiler-rt/test/msan/print_faulting_inst.cpp
@@ -0,0 +1,83 @@
+// Try parameter '0' (program runs cleanly)
+// -------------------------------------------------------
+// RUN: %clangxx_msan -g %s -o %t && %run %t 0
+
+// Try parameter '1'
+// -------------------------------------------------------
+// RUN: %clangxx_msan -g %s -o %t && not %run %t 1 >%t.out 2>&1
+// RUN: FileCheck --check-prefix STORE-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -mllvm -msan-print-faulting-instruction=1 -g %s -o %t && not %run %t 1 >%t.out 2>&1
+// RUN: FileCheck --check-prefixes VERBOSE-STORE-CHECK,STORE-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -mllvm -msan-print-faulting-instruction=2 -g %s -o %t && not %run %t 1 >%t.out 2>&1
+// RUN: FileCheck --check-prefixes VERY-VERBOSE-STORE-CHECK,STORE-CHECK %s < %t.out
+
+// Try parameter '2', with -fsanitize-memory-param-retval
+// -------------------------------------------------------
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: FileCheck --check-prefix PARAM-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-print-faulting-instruction=1 -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: FileCheck --check-prefixes VERBOSE-PARAM-CHECK,PARAM-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-print-faulting-instruction=2 -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: FileCheck --check-prefixes VERY-VERBOSE-PARAM-CHECK,PARAM-CHECK %s < %t.out
+
+// Try parameter '2', with -fno-sanitize-memory-param-retval
+// -------------------------------------------------------
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: FileCheck --check-prefix NO-PARAM-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-print-faulting-instruction=1 -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: FileCheck --check-prefixes VERBOSE-NO-PARAM-CHECK,NO-PARAM-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-print-faulting-instruction=2 -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: FileCheck --check-prefixes VERY-VERBOSE-NO-PARAM-CHECK,NO-PARAM-CHECK %s < %t.out
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define THRICE(o,t) twice(o,t)
+
+__attribute__((noinline)) extern "C" int twice(int o, int t) {
+ return o + t < 3;
+}
+
+int main(int argc, char *argv[]) {
+ int buf[100];
+ buf[0] = 42;
+ buf[1] = 43;
+
+ if (argc != 2) {
+ printf("Usage: %s index\n", argv[0]);
+ return 1;
+ }
+
+ int index = atoi(argv[1]);
+ int val = buf[index];
+
+ printf("index %d, abs(val) %d, THRICE(val,5) %d\n", index, abs(val), THRICE(val,5));
+ // VERY-VERBOSE-PARAM-CHECK: Instruction that failed the shadow check: %{{.*}} = call noundef i32 @twice(i32 noundef %{{.*}}, i32 noundef 5)
+ // VERBOSE-PARAM-CHECK: Instruction that failed the shadow check: call twice
+ // PARAM-CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
+ // PARAM-CHECK: {{#0 0x.* in main .*print_faulting_inst.cpp:}}[[@LINE-4]]
+
+ if (val)
+ // VERY-VERBOSE-NO-PARAM-CHECK: Instruction that failed the shadow check: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
+ // VERBOSE-NO-PARAM-CHECK: Instruction that failed the shadow check: br
+ // NO-PARAM-CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
+ // NO-PARAM-CHECK: {{#0 0x.* in main .*print_faulting_inst.cpp:}}[[@LINE-4]]
+ printf("Variable is non-zero\n");
+ else
+ printf("Variable is zero\n");
+
+ int nextval = buf[index + 1];
+ buf[nextval + abs(index)] = twice(index,6);
+ // VERY-VERBOSE-STORE-CHECK: Instruction that failed the shadow check: store i32 %{{.*}}, ptr %{{.*}}
+ // VERBOSE-STORE-CHECK: Instruction that failed the shadow check: store
+ // STORE-CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
+ // STORE-CHECK: {{#0 0x.* in main .*print_faulting_inst.cpp:}}[[@LINE-4]]
+
+ return 0;
+}
diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
index 506a880e3f67d..55aa6c74039cc 100644
--- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
@@ -274,6 +274,13 @@ static cl::opt<bool>
cl::desc("propagate shadow through ICmpEQ and ICmpNE"),
cl::Hidden, cl::init(true));
+static cl::opt<uint> ClPrintFaultingInst(
+ "msan-print-faulting-instruction",
+ cl::desc("If set to 1, print the name of the LLVM IR instruction that "
+ "failed the shadow check."
+ "If set to 2, print the full LLVM IR instruction."),
+ cl::Hidden, cl::init(0));
+
static cl::opt<bool>
ClHandleICmpExact("msan-handle-icmp-exact",
cl::desc("exact handling of relational integer ICmp"),
@@ -816,9 +823,14 @@ void MemorySanitizer::createKernelApi(Module &M, const TargetLibraryInfo &TLI) {
VAArgOriginTLS = nullptr;
VAArgOverflowSizeTLS = nullptr;
- WarningFn = M.getOrInsertFunction("__msan_warning",
- TLI.getAttrList(C, {0}, /*Signed=*/false),
- IRB.getVoidTy(), IRB.getInt32Ty());
+ if (ClPrintFaultingInst)
+ WarningFn = M.getOrInsertFunction(
+ "__msan_warning_instname", TLI.getAttrList(C, {0}, /*Signed=*/false),
+ IRB.getVoidTy(), IRB.getInt32Ty(), IRB.getPtrTy());
+ else
+ WarningFn = M.getOrInsertFunction("__msan_warning",
+ TLI.getAttrList(C, {0}, /*Signed=*/false),
+ IRB.getVoidTy(), IRB.getInt32Ty());
// Requests the per-task context state (kmsan_context_state*) from the
// runtime library.
@@ -870,19 +882,37 @@ void MemorySanitizer::createUserspaceApi(Module &M,
const TargetLibraryInfo &TLI) {
IRBuilder<> IRB(*C);
+ Type *Int8Ptr = PointerType::getUnqual(*C);
+
// Create the callback.
// FIXME: this function should have "Cold" calling conv,
// which is not yet implemented.
if (TrackOrigins) {
- StringRef WarningFnName = Recover ? "__msan_warning_with_origin"
- : "__msan_warning_with_origin_noreturn";
- WarningFn = M.getOrInsertFunction(WarningFnName,
- TLI.getAttrList(C, {0}, /*Signed=*/false),
- IRB.getVoidTy(), IRB.getInt32Ty());
+ if (ClPrintFaultingInst) {
+ StringRef WarningFnName =
+ Recover ? "__msan_warning_with_origin_instname"
+ : "__msan_warning_with_origin_noreturn_instname";
+ WarningFn = M.getOrInsertFunction(
+ WarningFnName, TLI.getAttrList(C, {0}, /*Signed=*/false),
+ IRB.getVoidTy(), IRB.getInt32Ty(), Int8Ptr);
+ } else {
+ StringRef WarningFnName = Recover ? "__msan_warning_with_origin"
+ : "__msan_warning_with_origin_noreturn";
+ WarningFn = M.getOrInsertFunction(
+ WarningFnName, TLI.getAttrList(C, {0}, /*Signed=*/false),
+ IRB.getVoidTy(), IRB.getInt32Ty());
+ }
} else {
- StringRef WarningFnName =
- Recover ? "__msan_warning" : "__msan_warning_noreturn";
- WarningFn = M.getOrInsertFunction(WarningFnName, IRB.getVoidTy());
+ if (ClPrintFaultingInst) {
+ StringRef WarningFnName = Recover ? "__msan_warning_instname"
+ : "__msan_warning_noreturn_instname";
+ WarningFn =
+ M.getOrInsertFunction(WarningFnName, IRB.getVoidTy(), Int8Ptr);
+ } else {
+ StringRef WarningFnName =
+ Recover ? "__msan_warning" : "__msan_warning_noreturn";
+ WarningFn = M.getOrInsertFunction(WarningFnName, IRB.getVoidTy());
+ }
}
// Create the global TLS variables.
@@ -914,10 +944,19 @@ void MemorySanitizer::createUserspaceApi(Module &M,
for (size_t AccessSizeIndex = 0; AccessSizeIndex < kNumberOfAccessSizes;
AccessSizeIndex++) {
unsigned AccessSize = 1 << AccessSizeIndex;
- std::string FunctionName = "__msan_maybe_warning_" + itostr(AccessSize);
- MaybeWarningFn[AccessSizeIndex] = M.getOrInsertFunction(
- FunctionName, TLI.getAttrList(C, {0, 1}, /*Signed=*/false),
- IRB.getVoidTy(), IRB.getIntNTy(AccessSize * 8), IRB.getInt32Ty());
+ std::string FunctionName;
+ if (ClPrintFaultingInst) {
+ FunctionName = "__msan_maybe_warning_instname_" + itostr(AccessSize);
+ MaybeWarningFn[AccessSizeIndex] = M.getOrInsertFunction(
+ FunctionName, TLI.getAttrList(C, {0, 1}, /*Signed=*/false),
+ IRB.getVoidTy(), IRB.getIntNTy(AccessSize * 8), IRB.getInt32Ty(),
+ IRB.getPtrTy());
+ } else {
+ FunctionName = "__msan_maybe_warning_" + itostr(AccessSize);
+ MaybeWarningFn[AccessSizeIndex] = M.getOrInsertFunction(
+ FunctionName, TLI.getAttrList(C, {0, 1}, /*Signed=*/false),
+ IRB.getVoidTy(), IRB.getIntNTy(AccessSize * 8), IRB.getInt32Ty());
+ }
FunctionName = "__msan_maybe_store_origin_" + itostr(AccessSize);
MaybeStoreOriginFn[AccessSizeIndex] = M.getOrInsertFunction(
@@ -1387,7 +1426,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
}
/// Helper function to insert a warning at IRB's current insert point.
- void insertWarningFn(IRBuilder<> &IRB, Value *Origin) {
+ void insertWarningFn(IRBuilder<> &IRB, Value *Origin, Value *InstName) {
if (!Origin)
Origin = (Value *)IRB.getInt32(0);
assert(Origin->getType()->isIntegerTy());
@@ -1410,17 +1449,24 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
}
}
- if (MS.CompileKernel || MS.TrackOrigins)
- IRB.CreateCall(MS.WarningFn, Origin)->setCannotMerge();
- else
- IRB.CreateCall(MS.WarningFn)->setCannotMerge();
+ if (ClPrintFaultingInst) {
+ if (MS.CompileKernel || MS.TrackOrigins)
+ IRB.CreateCall(MS.WarningFn, {Origin, InstName})->setCannotMerge();
+ else
+ IRB.CreateCall(MS.WarningFn, {InstName})->setCannotMerge();
+ } else {
+ if (MS.CompileKernel || MS.TrackOrigins)
+ IRB.CreateCall(MS.WarningFn, Origin)->setCannotMerge();
+ else
+ IRB.CreateCall(MS.WarningFn)->setCannotMerge();
+ }
// FIXME: Insert UnreachableInst if !MS.Recover?
// This may invalidate some of the following checks and needs to be done
// at the very end.
}
void materializeOneCheck(IRBuilder<> &IRB, Value *ConvertedShadow,
- Value *Origin) {
+ Value *Origin, Value *InstName) {
const DataLayout &DL = F.getDataLayout();
TypeSize TypeSizeInBits = DL.getTypeSizeInBits(ConvertedShadow->getType());
unsigned SizeIndex = TypeSizeToSizeIndex(TypeSizeInBits);
@@ -1431,9 +1477,18 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
ConvertedShadow = convertShadowToScalar(ConvertedShadow, IRB);
Value *ConvertedShadow2 =
IRB.CreateZExt(ConvertedShadow, IRB.getIntNTy(8 * (1 << SizeIndex)));
- CallBase *CB = IRB.CreateCall(
- Fn, {ConvertedShadow2,
- MS.TrackOrigins && Origin ? Origin : (Value *)IRB.getInt32(0)});
+ CallBase *CB;
+ if (ClPrintFaultingInst)
+ CB = IRB.CreateCall(
+ Fn, {ConvertedShadow2,
+ MS.TrackOrigins && Origin ? Origin : (Value *)IRB.getInt32(0),
+ InstName});
+ else
+ CB = IRB.CreateCall(Fn,
+ {ConvertedShadow2, MS.TrackOrigins && Origin
+ ? Origin
+ : (Value *)IRB.getInt32(0)});
+
CB->addParamAttr(0, Attribute::ZExt);
CB->addParamAttr(1, Attribute::ZExt);
} else {
@@ -1443,7 +1498,9 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
/* Unreachable */ !MS.Recover, MS.ColdCallWeights);
IRB.SetInsertPoint(CheckTerm);
- insertWarningFn(IRB, Origin);
+ // InstName will be ignored by insertWarningFn if ClPrintFaultingInst is
+ // false
+ insertWarningFn(IRB, Origin, InstName);
LLVM_DEBUG(dbgs() << " CHECK: " << *Cmp << "\n");
}
}
@@ -1455,13 +1512,43 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
// correct origin.
bool Combine = !MS.TrackOrigins;
Instruction *Instruction = InstructionChecks.front().OrigIns;
+
+ Value *InstName = nullptr;
+ if (ClPrintFaultingInst >= 1) {
+ IRBuilder<> IRB0(Instruction);
+ std::string str;
+ StringRef InstNameStrRef;
+
+ // Dumping the full instruction is expensive because the operands etc.
+ // likely make the string unique per instruction instance, hence we
+ // offer a choice whether to only print the instruction name.
+ if (ClPrintFaultingInst >= 2) {
+ llvm::raw_string_ostream buf(str);
+ Instruction->print(buf);
+ InstNameStrRef = StringRef(str);
+ } else if (ClPrintFaultingInst >= 1) {
+ if (CallInst *CI = dyn_cast<CallInst>(Instruction)) {
+ if (CI->getCalledFunction()) {
+ Twine description = "call " + CI->getCalledFunction()->getName();
+ SmallVector<char> maybeBuf;
+ InstNameStrRef = description.toStringRef(maybeBuf);
+ } else {
+ InstNameStrRef = StringRef("Unknown call");
+ }
+ } else {
+ InstNameStrRef = StringRef(Instruction->getOpcodeName());
+ }
+ }
+
+ InstName = IRB0.CreateGlobalString(InstNameStrRef);
+ }
+
Value *Shadow = nullptr;
for (const auto &ShadowData : InstructionChecks) {
assert(ShadowData.OrigIns == Instruction);
IRBuilder<> IRB(Instruction);
Value *ConvertedShadow = ShadowData.Shadow;
-
if (auto *ConstantShadow = dyn_cast<Constant>(ConvertedShadow)) {
if (!ClCheckConstantShadow || ConstantShadow->isZeroValue()) {
// Skip, value is initialized or const shadow is ignored.
@@ -1469,7 +1556,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
}
if (llvm::isKnownNonZero(ConvertedShadow, DL)) {
// Report as the value is definitely uninitialized.
- insertWarningFn(IRB, ShadowData.Origin);
+ insertWarningFn(IRB, ShadowData.Origin, InstName);
if (!MS.Recover)
return; // Always fail and stop here, not need to check the rest.
// Skip entire instruction,
@@ -1479,7 +1566,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
}
if (!Combine) {
- materializeOneCheck(IRB, ConvertedShadow, ShadowData.Origin);
+ materializeOneCheck(IRB, ConvertedShadow, ShadowData.Origin, InstName);
continue;
}
@@ -1496,7 +1583,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
if (Shadow) {
assert(Combine);
IRBuilder<> IRB(Instruction);
- materializeOneCheck(IRB, Shadow, nullptr);
+ materializeOneCheck(IRB, Shadow, nullptr, InstName);
}
}
>From a221a090fc4dd9e241021afd768889f8d6f3fe42 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 21 Apr 2025 05:56:12 +0000
Subject: [PATCH 2/4] Separate out embedding the instruction vs. printing the
faulting instruction
---
compiler-rt/lib/msan/msan.cpp | 21 ++++++++++++--
compiler-rt/lib/msan/msan_flags.inc | 4 +++
compiler-rt/test/msan/print_faulting_inst.cpp | 20 ++++++-------
.../Instrumentation/MemorySanitizer.cpp | 29 ++++++++++---------
4 files changed, 47 insertions(+), 27 deletions(-)
diff --git a/compiler-rt/lib/msan/msan.cpp b/compiler-rt/lib/msan/msan.cpp
index a22bfe0d7ab32..676b9908bfd43 100644
--- a/compiler-rt/lib/msan/msan.cpp
+++ b/compiler-rt/lib/msan/msan.cpp
@@ -352,9 +352,19 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
using namespace __msan;
-#define PRINT_FAULTING_INSTRUCTION(instname) \
- Printf("Instruction that failed the shadow check: %s\n", instname); \
- Printf("\n");
+#define PRINT_FAULTING_INSTRUCTION(instname) \
+ if (__msan::flags()->print_faulting_instruction) { \
+ Printf("Instruction that failed the shadow check: %s\n", instname); \
+ Printf("\n"); \
+ }
+
+#define CANNOT_PRINT_FAULTING_INSTRUCTION \
+ if (__msan::flags()->print_faulting_instruction) { \
+ Printf( \
+ "Error: print_faulting_instruction requested but code was not " \
+ "instrumented with -mllvm -embed-faulting-instruction.\n"); \
+ Printf("\n"); \
+ }
#define MSAN_MAYBE_WARNING_INSTNAME(type, size, instname) \
void __msan_maybe_warning_instname_##size(type s, u32 o, char *instname) { \
@@ -379,6 +389,7 @@ MSAN_MAYBE_WARNING_INSTNAME(u64, 8, instname)
void __msan_maybe_warning_##size(type s, u32 o) { \
GET_CALLER_PC_BP; \
if (UNLIKELY(s)) { \
+ CANNOT_PRINT_FAULTING_INSTRUCTION; \
PrintWarningWithOrigin(pc, bp, o); \
if (__msan::flags()->halt_on_error) { \
Printf("Exiting\n"); \
@@ -410,6 +421,7 @@ MSAN_MAYBE_STORE_ORIGIN(u32, 4)
MSAN_MAYBE_STORE_ORIGIN(u64, 8)
void __msan_warning() {
+ CANNOT_PRINT_FAULTING_INSTRUCTION;
GET_CALLER_PC_BP;
PrintWarningWithOrigin(pc, bp, 0);
if (__msan::flags()->halt_on_error) {
@@ -421,6 +433,7 @@ void __msan_warning() {
}
void __msan_warning_noreturn() {
+ CANNOT_PRINT_FAULTING_INSTRUCTION;
GET_CALLER_PC_BP;
PrintWarningWithOrigin(pc, bp, 0);
if (__msan::flags()->print_stats)
@@ -430,6 +443,7 @@ void __msan_warning_noreturn() {
}
void __msan_warning_with_origin(u32 origin) {
+ CANNOT_PRINT_FAULTING_INSTRUCTION;
GET_CALLER_PC_BP;
PrintWarningWithOrigin(pc, bp, origin);
if (__msan::flags()->halt_on_error) {
@@ -441,6 +455,7 @@ void __msan_warning_with_origin(u32 origin) {
}
void __msan_warning_with_origin_noreturn(u32 origin) {
+ CANNOT_PRINT_FAULTING_INSTRUCTION;
GET_CALLER_PC_BP;
PrintWarningWithOrigin(pc, bp, origin);
if (__msan::flags()->print_stats)
diff --git a/compiler-rt/lib/msan/msan_flags.inc b/compiler-rt/lib/msan/msan_flags.inc
index 16db26bd42ed9..30a17fb3db9a8 100644
--- a/compiler-rt/lib/msan/msan_flags.inc
+++ b/compiler-rt/lib/msan/msan_flags.inc
@@ -25,6 +25,10 @@ MSAN_FLAG(bool, poison_stack_with_zeroes, false, "")
MSAN_FLAG(bool, poison_in_malloc, true, "")
MSAN_FLAG(bool, poison_in_free, true, "")
MSAN_FLAG(bool, poison_in_dtor, true, "")
+MSAN_FLAG(bool, print_faulting_instruction, false,
+ "When reporting a UUM, print the name of the faulting instruction. "
+ "Note: requires code to be instrumented with -mllvm "
+ "-msan-embed-faulting-instruction.")
MSAN_FLAG(bool, report_umrs, true, "")
MSAN_FLAG(bool, wrap_signals, true, "")
MSAN_FLAG(bool, print_stats, false, "")
diff --git a/compiler-rt/test/msan/print_faulting_inst.cpp b/compiler-rt/test/msan/print_faulting_inst.cpp
index 09c5f22ada132..1cbea9eb035e0 100644
--- a/compiler-rt/test/msan/print_faulting_inst.cpp
+++ b/compiler-rt/test/msan/print_faulting_inst.cpp
@@ -1,38 +1,38 @@
// Try parameter '0' (program runs cleanly)
// -------------------------------------------------------
-// RUN: %clangxx_msan -g %s -o %t && %run %t 0
+// RUN: env MSAN_OPTIONS=print_faulting_instruction=true %clangxx_msan -g %s -o %t && env MSAN_OPTIONS=print_faulting_instruction=true %run %t 0
// Try parameter '1'
// -------------------------------------------------------
-// RUN: %clangxx_msan -g %s -o %t && not %run %t 1 >%t.out 2>&1
+// RUN: %clangxx_msan -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
// RUN: FileCheck --check-prefix STORE-CHECK %s < %t.out
-// RUN: %clangxx_msan -mllvm -msan-print-faulting-instruction=1 -g %s -o %t && not %run %t 1 >%t.out 2>&1
+// RUN: %clangxx_msan -mllvm -msan-embed-faulting-instruction=1 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERBOSE-STORE-CHECK,STORE-CHECK %s < %t.out
-// RUN: %clangxx_msan -mllvm -msan-print-faulting-instruction=2 -g %s -o %t && not %run %t 1 >%t.out 2>&1
+// RUN: %clangxx_msan -mllvm -msan-embed-faulting-instruction=2 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERY-VERBOSE-STORE-CHECK,STORE-CHECK %s < %t.out
// Try parameter '2', with -fsanitize-memory-param-retval
// -------------------------------------------------------
-// RUN: %clangxx_msan -fsanitize-memory-param-retval -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefix PARAM-CHECK %s < %t.out
-// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-print-faulting-instruction=1 -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=1 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERBOSE-PARAM-CHECK,PARAM-CHECK %s < %t.out
-// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-print-faulting-instruction=2 -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=2 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERY-VERBOSE-PARAM-CHECK,PARAM-CHECK %s < %t.out
// Try parameter '2', with -fno-sanitize-memory-param-retval
// -------------------------------------------------------
-// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefix NO-PARAM-CHECK %s < %t.out
-// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-print-faulting-instruction=1 -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=1 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERBOSE-NO-PARAM-CHECK,NO-PARAM-CHECK %s < %t.out
-// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-print-faulting-instruction=2 -g %s -o %t && not %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=2 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERY-VERBOSE-NO-PARAM-CHECK,NO-PARAM-CHECK %s < %t.out
#include <stdio.h>
diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
index 55aa6c74039cc..76dc539ee6af2 100644
--- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
@@ -274,11 +274,12 @@ static cl::opt<bool>
cl::desc("propagate shadow through ICmpEQ and ICmpNE"),
cl::Hidden, cl::init(true));
-static cl::opt<uint> ClPrintFaultingInst(
- "msan-print-faulting-instruction",
- cl::desc("If set to 1, print the name of the LLVM IR instruction that "
+static cl::opt<uint> ClEmbedFaultingInst(
+ "msan-embed-faulting-instruction",
+ cl::desc("If set to 1, embed the name of the LLVM IR instruction that "
"failed the shadow check."
- "If set to 2, print the full LLVM IR instruction."),
+ "If set to 2, embed the full LLVM IR instruction. "
+ "The runtime can print the embedded instruction."),
cl::Hidden, cl::init(0));
static cl::opt<bool>
@@ -823,7 +824,7 @@ void MemorySanitizer::createKernelApi(Module &M, const TargetLibraryInfo &TLI) {
VAArgOriginTLS = nullptr;
VAArgOverflowSizeTLS = nullptr;
- if (ClPrintFaultingInst)
+ if (ClEmbedFaultingInst)
WarningFn = M.getOrInsertFunction(
"__msan_warning_instname", TLI.getAttrList(C, {0}, /*Signed=*/false),
IRB.getVoidTy(), IRB.getInt32Ty(), IRB.getPtrTy());
@@ -888,7 +889,7 @@ void MemorySanitizer::createUserspaceApi(Module &M,
// FIXME: this function should have "Cold" calling conv,
// which is not yet implemented.
if (TrackOrigins) {
- if (ClPrintFaultingInst) {
+ if (ClEmbedFaultingInst) {
StringRef WarningFnName =
Recover ? "__msan_warning_with_origin_instname"
: "__msan_warning_with_origin_noreturn_instname";
@@ -903,7 +904,7 @@ void MemorySanitizer::createUserspaceApi(Module &M,
IRB.getVoidTy(), IRB.getInt32Ty());
}
} else {
- if (ClPrintFaultingInst) {
+ if (ClEmbedFaultingInst) {
StringRef WarningFnName = Recover ? "__msan_warning_instname"
: "__msan_warning_noreturn_instname";
WarningFn =
@@ -945,7 +946,7 @@ void MemorySanitizer::createUserspaceApi(Module &M,
AccessSizeIndex++) {
unsigned AccessSize = 1 << AccessSizeIndex;
std::string FunctionName;
- if (ClPrintFaultingInst) {
+ if (ClEmbedFaultingInst) {
FunctionName = "__msan_maybe_warning_instname_" + itostr(AccessSize);
MaybeWarningFn[AccessSizeIndex] = M.getOrInsertFunction(
FunctionName, TLI.getAttrList(C, {0, 1}, /*Signed=*/false),
@@ -1449,7 +1450,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
}
}
- if (ClPrintFaultingInst) {
+ if (ClEmbedFaultingInst) {
if (MS.CompileKernel || MS.TrackOrigins)
IRB.CreateCall(MS.WarningFn, {Origin, InstName})->setCannotMerge();
else
@@ -1478,7 +1479,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
Value *ConvertedShadow2 =
IRB.CreateZExt(ConvertedShadow, IRB.getIntNTy(8 * (1 << SizeIndex)));
CallBase *CB;
- if (ClPrintFaultingInst)
+ if (ClEmbedFaultingInst)
CB = IRB.CreateCall(
Fn, {ConvertedShadow2,
MS.TrackOrigins && Origin ? Origin : (Value *)IRB.getInt32(0),
@@ -1498,7 +1499,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
/* Unreachable */ !MS.Recover, MS.ColdCallWeights);
IRB.SetInsertPoint(CheckTerm);
- // InstName will be ignored by insertWarningFn if ClPrintFaultingInst is
+ // InstName will be ignored by insertWarningFn if ClEmbedFaultingInst is
// false
insertWarningFn(IRB, Origin, InstName);
LLVM_DEBUG(dbgs() << " CHECK: " << *Cmp << "\n");
@@ -1514,7 +1515,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
Instruction *Instruction = InstructionChecks.front().OrigIns;
Value *InstName = nullptr;
- if (ClPrintFaultingInst >= 1) {
+ if (ClEmbedFaultingInst >= 1) {
IRBuilder<> IRB0(Instruction);
std::string str;
StringRef InstNameStrRef;
@@ -1522,11 +1523,11 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
// Dumping the full instruction is expensive because the operands etc.
// likely make the string unique per instruction instance, hence we
// offer a choice whether to only print the instruction name.
- if (ClPrintFaultingInst >= 2) {
+ if (ClEmbedFaultingInst >= 2) {
llvm::raw_string_ostream buf(str);
Instruction->print(buf);
InstNameStrRef = StringRef(str);
- } else if (ClPrintFaultingInst >= 1) {
+ } else if (ClEmbedFaultingInst >= 1) {
if (CallInst *CI = dyn_cast<CallInst>(Instruction)) {
if (CI->getCalledFunction()) {
Twine description = "call " + CI->getCalledFunction()->getName();
>From 08c0511b4785756e6022bed50333caccb510162e Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 21 Apr 2025 15:43:05 +0000
Subject: [PATCH 3/4] clang-format
---
compiler-rt/test/msan/print_faulting_inst.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/compiler-rt/test/msan/print_faulting_inst.cpp b/compiler-rt/test/msan/print_faulting_inst.cpp
index 1cbea9eb035e0..7274f4d14de0f 100644
--- a/compiler-rt/test/msan/print_faulting_inst.cpp
+++ b/compiler-rt/test/msan/print_faulting_inst.cpp
@@ -38,7 +38,7 @@
#include <stdio.h>
#include <stdlib.h>
-#define THRICE(o,t) twice(o,t)
+#define THRICE(o, t) twice(o, t)
__attribute__((noinline)) extern "C" int twice(int o, int t) {
return o + t < 3;
@@ -57,7 +57,8 @@ int main(int argc, char *argv[]) {
int index = atoi(argv[1]);
int val = buf[index];
- printf("index %d, abs(val) %d, THRICE(val,5) %d\n", index, abs(val), THRICE(val,5));
+ printf("index %d, abs(val) %d, THRICE(val,5) %d\n", index, abs(val),
+ THRICE(val, 5));
// VERY-VERBOSE-PARAM-CHECK: Instruction that failed the shadow check: %{{.*}} = call noundef i32 @twice(i32 noundef %{{.*}}, i32 noundef 5)
// VERBOSE-PARAM-CHECK: Instruction that failed the shadow check: call twice
// PARAM-CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
@@ -73,7 +74,7 @@ int main(int argc, char *argv[]) {
printf("Variable is zero\n");
int nextval = buf[index + 1];
- buf[nextval + abs(index)] = twice(index,6);
+ buf[nextval + abs(index)] = twice(index, 6);
// VERY-VERBOSE-STORE-CHECK: Instruction that failed the shadow check: store i32 %{{.*}}, ptr %{{.*}}
// VERBOSE-STORE-CHECK: Instruction that failed the shadow check: store
// STORE-CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
>From 6b7bdb84b8dd032420b471761034bf1dbd9ada58 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Mon, 21 Apr 2025 23:19:26 +0000
Subject: [PATCH 4/4] Address Florian's feedback Also add "[EXPERIMENTAL]"
disclaimer
---
compiler-rt/lib/msan/msan_flags.inc | 3 +-
compiler-rt/test/msan/print_faulting_inst.cpp | 23 ++++---
.../Instrumentation/MemorySanitizer.h | 7 +++
.../Instrumentation/MemorySanitizer.cpp | 62 ++++++++++++-------
4 files changed, 63 insertions(+), 32 deletions(-)
diff --git a/compiler-rt/lib/msan/msan_flags.inc b/compiler-rt/lib/msan/msan_flags.inc
index 30a17fb3db9a8..969a1cc8cc448 100644
--- a/compiler-rt/lib/msan/msan_flags.inc
+++ b/compiler-rt/lib/msan/msan_flags.inc
@@ -26,7 +26,8 @@ MSAN_FLAG(bool, poison_in_malloc, true, "")
MSAN_FLAG(bool, poison_in_free, true, "")
MSAN_FLAG(bool, poison_in_dtor, true, "")
MSAN_FLAG(bool, print_faulting_instruction, false,
- "When reporting a UUM, print the name of the faulting instruction. "
+ "[EXPERIMENTAL] When reporting a UUM, print the name of the faulting "
+ "instruction. "
"Note: requires code to be instrumented with -mllvm "
"-msan-embed-faulting-instruction.")
MSAN_FLAG(bool, report_umrs, true, "")
diff --git a/compiler-rt/test/msan/print_faulting_inst.cpp b/compiler-rt/test/msan/print_faulting_inst.cpp
index 7274f4d14de0f..bf39ce2ce79db 100644
--- a/compiler-rt/test/msan/print_faulting_inst.cpp
+++ b/compiler-rt/test/msan/print_faulting_inst.cpp
@@ -1,16 +1,19 @@
// Try parameter '0' (program runs cleanly)
// -------------------------------------------------------
-// RUN: env MSAN_OPTIONS=print_faulting_instruction=true %clangxx_msan -g %s -o %t && env MSAN_OPTIONS=print_faulting_instruction=true %run %t 0
+// RUN: %clangxx_msan -g %s -o %t && env MSAN_OPTIONS=print_faulting_instruction=true %run %t 0
// Try parameter '1'
// -------------------------------------------------------
// RUN: %clangxx_msan -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
// RUN: FileCheck --check-prefix STORE-CHECK %s < %t.out
-// RUN: %clangxx_msan -mllvm -msan-embed-faulting-instruction=1 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
+// RUN: %clangxx_msan -mllvm -msan-embed-faulting-instruction=none -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
+// RUN: FileCheck --check-prefix STORE-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -mllvm -msan-embed-faulting-instruction=name -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERBOSE-STORE-CHECK,STORE-CHECK %s < %t.out
-// RUN: %clangxx_msan -mllvm -msan-embed-faulting-instruction=2 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
+// RUN: %clangxx_msan -mllvm -msan-embed-faulting-instruction=full -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 1 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERY-VERBOSE-STORE-CHECK,STORE-CHECK %s < %t.out
// Try parameter '2', with -fsanitize-memory-param-retval
@@ -18,10 +21,13 @@
// RUN: %clangxx_msan -fsanitize-memory-param-retval -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefix PARAM-CHECK %s < %t.out
-// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=1 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=none -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
+// RUN: FileCheck --check-prefix PARAM-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=name -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERBOSE-PARAM-CHECK,PARAM-CHECK %s < %t.out
-// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=2 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fsanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=full -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERY-VERBOSE-PARAM-CHECK,PARAM-CHECK %s < %t.out
// Try parameter '2', with -fno-sanitize-memory-param-retval
@@ -29,10 +35,13 @@
// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefix NO-PARAM-CHECK %s < %t.out
-// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=1 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=none -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
+// RUN: FileCheck --check-prefix NO-PARAM-CHECK %s < %t.out
+
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=name -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERBOSE-NO-PARAM-CHECK,NO-PARAM-CHECK %s < %t.out
-// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=2 -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
+// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -mllvm -msan-embed-faulting-instruction=full -g %s -o %t && not env MSAN_OPTIONS=print_faulting_instruction=true %run %t 2 >%t.out 2>&1
// RUN: FileCheck --check-prefixes VERY-VERBOSE-NO-PARAM-CHECK,NO-PARAM-CHECK %s < %t.out
#include <stdio.h>
diff --git a/llvm/include/llvm/Transforms/Instrumentation/MemorySanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/MemorySanitizer.h
index f88d832351118..dffa32fab366f 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/MemorySanitizer.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/MemorySanitizer.h
@@ -21,6 +21,13 @@ class Module;
class StringRef;
class raw_ostream;
+/// Mode of MSan -embed-faulting-instruction
+enum class MSanEmbedFaultingInstructionMode {
+ None, ///< Do not embed the faulting instruction information
+ Name, ///< Embed the LLVM IR instruction name (excluding operands)
+ Full, ///< Embed the complete LLVM IR instruction (including operands)
+};
+
struct MemorySanitizerOptions {
MemorySanitizerOptions() : MemorySanitizerOptions(0, false, false, false){};
MemorySanitizerOptions(int TrackOrigins, bool Recover, bool Kernel)
diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
index 76dc539ee6af2..339bb8de0fd2b 100644
--- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
@@ -274,13 +274,19 @@ static cl::opt<bool>
cl::desc("propagate shadow through ICmpEQ and ICmpNE"),
cl::Hidden, cl::init(true));
-static cl::opt<uint> ClEmbedFaultingInst(
+static cl::opt<MSanEmbedFaultingInstructionMode> ClEmbedFaultingInst(
"msan-embed-faulting-instruction",
- cl::desc("If set to 1, embed the name of the LLVM IR instruction that "
- "failed the shadow check."
- "If set to 2, embed the full LLVM IR instruction. "
- "The runtime can print the embedded instruction."),
- cl::Hidden, cl::init(0));
+ cl::desc("[EXPERIMENTAL] Sets whether to embed the LLVM IR instruction "
+ "info for each UUM check."),
+ cl::values(
+ clEnumValN(MSanEmbedFaultingInstructionMode::None, "none",
+ "Do not embed the faulting instruction information."),
+ clEnumValN(MSanEmbedFaultingInstructionMode::Name, "name",
+ "Embed the LLVM IR instruction name (excluding operands)."),
+ clEnumValN(
+ MSanEmbedFaultingInstructionMode::Full, "full",
+ "Embed the complete LLVM IR instruction (including operands).")),
+ cl::Hidden, cl::init(MSanEmbedFaultingInstructionMode::None));
static cl::opt<bool>
ClHandleICmpExact("msan-handle-icmp-exact",
@@ -824,7 +830,7 @@ void MemorySanitizer::createKernelApi(Module &M, const TargetLibraryInfo &TLI) {
VAArgOriginTLS = nullptr;
VAArgOverflowSizeTLS = nullptr;
- if (ClEmbedFaultingInst)
+ if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None)
WarningFn = M.getOrInsertFunction(
"__msan_warning_instname", TLI.getAttrList(C, {0}, /*Signed=*/false),
IRB.getVoidTy(), IRB.getInt32Ty(), IRB.getPtrTy());
@@ -889,7 +895,7 @@ void MemorySanitizer::createUserspaceApi(Module &M,
// FIXME: this function should have "Cold" calling conv,
// which is not yet implemented.
if (TrackOrigins) {
- if (ClEmbedFaultingInst) {
+ if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) {
StringRef WarningFnName =
Recover ? "__msan_warning_with_origin_instname"
: "__msan_warning_with_origin_noreturn_instname";
@@ -904,7 +910,7 @@ void MemorySanitizer::createUserspaceApi(Module &M,
IRB.getVoidTy(), IRB.getInt32Ty());
}
} else {
- if (ClEmbedFaultingInst) {
+ if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) {
StringRef WarningFnName = Recover ? "__msan_warning_instname"
: "__msan_warning_noreturn_instname";
WarningFn =
@@ -946,7 +952,7 @@ void MemorySanitizer::createUserspaceApi(Module &M,
AccessSizeIndex++) {
unsigned AccessSize = 1 << AccessSizeIndex;
std::string FunctionName;
- if (ClEmbedFaultingInst) {
+ if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) {
FunctionName = "__msan_maybe_warning_instname_" + itostr(AccessSize);
MaybeWarningFn[AccessSizeIndex] = M.getOrInsertFunction(
FunctionName, TLI.getAttrList(C, {0, 1}, /*Signed=*/false),
@@ -1450,7 +1456,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
}
}
- if (ClEmbedFaultingInst) {
+ if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) {
if (MS.CompileKernel || MS.TrackOrigins)
IRB.CreateCall(MS.WarningFn, {Origin, InstName})->setCannotMerge();
else
@@ -1479,7 +1485,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
Value *ConvertedShadow2 =
IRB.CreateZExt(ConvertedShadow, IRB.getIntNTy(8 * (1 << SizeIndex)));
CallBase *CB;
- if (ClEmbedFaultingInst)
+ if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None)
CB = IRB.CreateCall(
Fn, {ConvertedShadow2,
MS.TrackOrigins && Origin ? Origin : (Value *)IRB.getInt32(0),
@@ -1500,22 +1506,15 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
IRB.SetInsertPoint(CheckTerm);
// InstName will be ignored by insertWarningFn if ClEmbedFaultingInst is
- // false
+ // MSanEmbedFaultingInstructionMode::None
insertWarningFn(IRB, Origin, InstName);
LLVM_DEBUG(dbgs() << " CHECK: " << *Cmp << "\n");
}
}
- void materializeInstructionChecks(
- ArrayRef<ShadowOriginAndInsertPoint> InstructionChecks) {
- const DataLayout &DL = F.getDataLayout();
- // Disable combining in some cases. TrackOrigins checks each shadow to pick
- // correct origin.
- bool Combine = !MS.TrackOrigins;
- Instruction *Instruction = InstructionChecks.front().OrigIns;
-
+ Value *getInstName(Instruction *Instruction) {
Value *InstName = nullptr;
- if (ClEmbedFaultingInst >= 1) {
+ if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) {
IRBuilder<> IRB0(Instruction);
std::string str;
StringRef InstNameStrRef;
@@ -1523,11 +1522,12 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
// Dumping the full instruction is expensive because the operands etc.
// likely make the string unique per instruction instance, hence we
// offer a choice whether to only print the instruction name.
- if (ClEmbedFaultingInst >= 2) {
+ if (ClEmbedFaultingInst == MSanEmbedFaultingInstructionMode::Full) {
llvm::raw_string_ostream buf(str);
Instruction->print(buf);
InstNameStrRef = StringRef(str);
- } else if (ClEmbedFaultingInst >= 1) {
+ } else if (ClEmbedFaultingInst ==
+ MSanEmbedFaultingInstructionMode::Name) {
if (CallInst *CI = dyn_cast<CallInst>(Instruction)) {
if (CI->getCalledFunction()) {
Twine description = "call " + CI->getCalledFunction()->getName();
@@ -1544,12 +1544,26 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
InstName = IRB0.CreateGlobalString(InstNameStrRef);
}
+ return InstName;
+ }
+
+ void materializeInstructionChecks(
+ ArrayRef<ShadowOriginAndInsertPoint> InstructionChecks) {
+ const DataLayout &DL = F.getDataLayout();
+ // Disable combining in some cases. TrackOrigins checks each shadow to pick
+ // correct origin.
+ bool Combine = !MS.TrackOrigins;
+ Instruction *Instruction = InstructionChecks.front().OrigIns;
+
+ Value *InstName = getInstName(Instruction);
+
Value *Shadow = nullptr;
for (const auto &ShadowData : InstructionChecks) {
assert(ShadowData.OrigIns == Instruction);
IRBuilder<> IRB(Instruction);
Value *ConvertedShadow = ShadowData.Shadow;
+
if (auto *ConstantShadow = dyn_cast<Constant>(ConvertedShadow)) {
if (!ClCheckConstantShadow || ConstantShadow->isZeroValue()) {
// Skip, value is initialized or const shadow is ignored.
More information about the llvm-commits
mailing list