diff --git a/compiler-rt/lib/msan/msan.cpp b/compiler-rt/lib/msan/msan.cpp index a3c0c2e485af3..8cc38d98e4dc5 100644 --- a/compiler-rt/lib/msan/msan.cpp +++ b/compiler-rt/lib/msan/msan.cpp @@ -352,10 +352,46 @@ void __sanitizer::BufferedStackTrace::UnwindImpl( using namespace __msan; +static inline void PrintFaultingInstructionIfRequested(char *instname) { + if (__msan::flags()->print_faulting_instruction) { + Printf("Instruction that failed the shadow check: %s\n", instname); + Printf("\n"); + } +} + +static inline void WarnIfPrintFaultingInstructionRequested() { + 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) { \ + GET_CALLER_PC_BP; \ + if (UNLIKELY(s)) { \ + if (instname) \ + PrintFaultingInstructionIfRequested(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; \ if (UNLIKELY(s)) { \ + WarnIfPrintFaultingInstructionRequested(); \ PrintWarningWithOrigin(pc, bp, o); \ if (__msan::flags()->halt_on_error) { \ Printf("Exiting\n"); \ @@ -386,44 +422,88 @@ MSAN_MAYBE_STORE_ORIGIN(u16, 2) MSAN_MAYBE_STORE_ORIGIN(u32, 4) MSAN_MAYBE_STORE_ORIGIN(u64, 8) -void __msan_warning() { - GET_CALLER_PC_BP; - PrintWarningWithOrigin(pc, bp, 0); - if (__msan::flags()->halt_on_error) { - if (__msan::flags()->print_stats) - ReportStats(); - Printf("Exiting\n"); - Die(); +// These macros to reuse the function body are kludgy, but are better than the +// alternatives: +// - call a common function: this pollutes the stack traces +// - have x_instname() be a simple macro wrapper around x(): the +// instrumentation pass expects function symbols +// - add instname as a parameter everywhere (with a check whether instname is +// null): this pollutes the fastpath +// - duplicate the function body: redundancy is redundant +#define __MSAN_WARNING_BODY \ + 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() { + WarnIfPrintFaultingInstructionRequested(); + __MSAN_WARNING_BODY } -void __msan_warning_noreturn() { - GET_CALLER_PC_BP; - PrintWarningWithOrigin(pc, bp, 0); - if (__msan::flags()->print_stats) - ReportStats(); - Printf("Exiting\n"); +void __msan_warning_instname(char *instname) { + PrintFaultingInstructionIfRequested(instname); + __MSAN_WARNING_BODY +} + +#define __MSAN_WARNING_NORETURN_BODY \ + GET_CALLER_PC_BP; \ + PrintWarningWithOrigin(pc, bp, 0); \ + if (__msan::flags()->print_stats) \ + ReportStats(); \ + Printf("Exiting\n"); \ Die(); + +void __msan_warning_noreturn() { + WarnIfPrintFaultingInstructionRequested(); + __MSAN_WARNING_NORETURN_BODY } -void __msan_warning_with_origin(u32 origin) { - 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_noreturn_instname(char *instname) { + PrintFaultingInstructionIfRequested(instname); + __MSAN_WARNING_NORETURN_BODY +} + +#define __MSAN_WARNING_WITH_ORIGIN_BODY(origin) \ + 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(u32 origin) { + WarnIfPrintFaultingInstructionRequested(); + __MSAN_WARNING_WITH_ORIGIN_BODY(origin) } -void __msan_warning_with_origin_noreturn(u32 origin) { - GET_CALLER_PC_BP; - PrintWarningWithOrigin(pc, bp, origin); - if (__msan::flags()->print_stats) - ReportStats(); - Printf("Exiting\n"); +void __msan_warning_with_origin_instname(u32 origin, char *instname) { + PrintFaultingInstructionIfRequested(instname); + __MSAN_WARNING_WITH_ORIGIN_BODY(origin) +} + +#define __MSAN_WARNING_WITH_ORIGIN_NORETURN_BODY(origin) \ + GET_CALLER_PC_BP; \ + PrintWarningWithOrigin(pc, bp, origin); \ + if (__msan::flags()->print_stats) \ + ReportStats(); \ + Printf("Exiting\n"); \ Die(); + +void __msan_warning_with_origin_noreturn(u32 origin) { + WarnIfPrintFaultingInstructionRequested(); + __MSAN_WARNING_WITH_ORIGIN_NORETURN_BODY(origin) +} + +void __msan_warning_with_origin_noreturn_instname(u32 origin, char *instname) { + PrintFaultingInstructionIfRequested(instname); + __MSAN_WARNING_WITH_ORIGIN_NORETURN_BODY(origin) } static void OnStackUnwind(const SignalContext &sig, const void *, diff --git a/compiler-rt/lib/msan/msan_flags.inc b/compiler-rt/lib/msan/msan_flags.inc index 16db26bd42ed9..969a1cc8cc448 100644 --- a/compiler-rt/lib/msan/msan_flags.inc +++ b/compiler-rt/lib/msan/msan_flags.inc @@ -25,6 +25,11 @@ 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, + "[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, "") MSAN_FLAG(bool, wrap_signals, true, "") MSAN_FLAG(bool, print_stats, false, "") diff --git a/compiler-rt/lib/msan/msan_interface_internal.h b/compiler-rt/lib/msan/msan_interface_internal.h index c2eead13c20cf..c7fb08d713680 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_instname_1(u8 s, u32 o, char *instname); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_warning_instname_2(u16 s, u32 o, char *instname); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_warning_instname_4(u32 s, u32 o, char *instname); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_warning_instname_8(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..bf39ce2ce79db --- /dev/null +++ b/compiler-rt/test/msan/print_faulting_inst.cpp @@ -0,0 +1,93 @@ +// Try parameter '0' (program runs cleanly) +// ------------------------------------------------------- +// 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=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=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 +// ------------------------------------------------------- +// 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=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=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 +// ------------------------------------------------------- +// 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=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=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 +#include + +#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/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 8e31e8d2a4fbd..451d1588849bd 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -274,6 +274,20 @@ static cl::opt cl::desc("propagate shadow through ICmpEQ and ICmpNE"), cl::Hidden, cl::init(true)); +static cl::opt ClEmbedFaultingInst( + "msan-embed-faulting-instruction", + 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 ClHandleICmpExact("msan-handle-icmp-exact", cl::desc("exact handling of relational integer ICmp"), @@ -803,6 +817,39 @@ MemorySanitizer::getOrInsertMsanMetadataFunction(Module &M, StringRef Name, std::forward(Args)...); } +StringRef getWarningFnName(bool TrackOrigins, bool Recover, + bool EmbedFaultingInst) { + StringRef warningFnName[2][2][2] = { + { + // TrackOrigins=false + { + // Recover=false + "__msan_warning_noreturn", // EmbedFaultingInst=false + "__msan_warning_noreturn_instname" // EmbedFaultingInst=true + }, + { + // Recover=true + "__msan_warning", // EmbedFaultingInst=false + "__msan_warning_instname" // EmbedFaultingInst=true + }, + }, + { + // TrackOrigins=true + { + // Recover=false + "__msan_warning_with_origin_noreturn", // EmbedFaultingInst=false + "__msan_warning_with_origin_noreturn_instname" // EmbedFaultingInst=true + }, + { + // Recover=true + "__msan_warning_with_origin", // EmbedFaultingInst=false + "__msan_warning_with_origin_instname" // EmbedFaultingInst=true + }, + }}; + + return warningFnName[TrackOrigins][Recover][EmbedFaultingInst]; +} + /// Create KMSAN API callbacks. void MemorySanitizer::createKernelApi(Module &M, const TargetLibraryInfo &TLI) { IRBuilder<> IRB(*C); @@ -816,9 +863,15 @@ 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()); + SmallVector ArgsTy = {IRB.getInt32Ty()}; + StringRef FnName = getWarningFnName( + /*TrackOrigins=*/false, /*Recover=*/true, + ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None); + if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) + ArgsTy.push_back(IRB.getPtrTy()); + WarningFn = M.getOrInsertFunction( + FnName, FunctionType::get(IRB.getVoidTy(), ArgsTy, false), + TLI.getAttrList(C, {0}, /*Signed=*/false)); // Requests the per-task context state (kmsan_context_state*) from the // runtime library. @@ -873,16 +926,22 @@ void MemorySanitizer::createUserspaceApi(Module &M, // Create the callback. // FIXME: this function should have "Cold" calling conv, // which is not yet implemented. + StringRef WarningFnName = getWarningFnName( + TrackOrigins, Recover, + ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None); + SmallVector ArgsTy = {}; 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()); + ArgsTy.push_back(IRB.getInt32Ty()); + if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) + ArgsTy.push_back(IRB.getPtrTy()); + WarningFn = M.getOrInsertFunction( + WarningFnName, FunctionType::get(IRB.getVoidTy(), ArgsTy, false), + TLI.getAttrList(C, {0}, /*Signed=*/false)); } else { - StringRef WarningFnName = - Recover ? "__msan_warning" : "__msan_warning_noreturn"; - WarningFn = M.getOrInsertFunction(WarningFnName, IRB.getVoidTy()); + if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) + ArgsTy.push_back(IRB.getPtrTy()); + WarningFn = M.getOrInsertFunction( + WarningFnName, FunctionType::get(IRB.getVoidTy(), ArgsTy, false)); } // Create the global TLS variables. @@ -915,9 +974,15 @@ void MemorySanitizer::createUserspaceApi(Module &M, AccessSizeIndex++) { unsigned AccessSize = 1 << AccessSizeIndex; std::string FunctionName = "__msan_maybe_warning_" + itostr(AccessSize); + SmallVector ArgsTy = {IRB.getIntNTy(AccessSize * 8), + IRB.getInt32Ty()}; + if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) { + FunctionName = "__msan_maybe_warning_instname_" + itostr(AccessSize); + ArgsTy.push_back(IRB.getPtrTy()); + } MaybeWarningFn[AccessSizeIndex] = M.getOrInsertFunction( - FunctionName, TLI.getAttrList(C, {0, 1}, /*Signed=*/false), - IRB.getVoidTy(), IRB.getIntNTy(AccessSize * 8), IRB.getInt32Ty()); + FunctionName, FunctionType::get(IRB.getVoidTy(), ArgsTy, false), + TLI.getAttrList(C, {0, 1}, /*Signed=*/false)); FunctionName = "__msan_maybe_store_origin_" + itostr(AccessSize); MaybeStoreOriginFn[AccessSizeIndex] = M.getOrInsertFunction( @@ -1387,7 +1452,7 @@ struct MemorySanitizerVisitor : public InstVisitor { } /// 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 +1475,20 @@ struct MemorySanitizerVisitor : public InstVisitor { } } + SmallVector Args; if (MS.CompileKernel || MS.TrackOrigins) - IRB.CreateCall(MS.WarningFn, Origin)->setCannotMerge(); - else - IRB.CreateCall(MS.WarningFn)->setCannotMerge(); + Args.push_back(Origin); + if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) + Args.push_back(InstName); + IRB.CreateCall(MS.WarningFn, Args)->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 +1499,15 @@ struct MemorySanitizerVisitor : public InstVisitor { 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)}); + + SmallVector Args = { + ConvertedShadow2, + MS.TrackOrigins && Origin ? Origin : (Value *)IRB.getInt32(0)}; + if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) + Args.push_back(InstName); + + CallBase *CB = IRB.CreateCall(Fn, Args); + CB->addParamAttr(0, Attribute::ZExt); CB->addParamAttr(1, Attribute::ZExt); } else { @@ -1443,11 +1517,51 @@ struct MemorySanitizerVisitor : public InstVisitor { /* Unreachable */ !MS.Recover, MS.ColdCallWeights); IRB.SetInsertPoint(CheckTerm); - insertWarningFn(IRB, Origin); + // InstName will be ignored by insertWarningFn if ClEmbedFaultingInst is + // MSanEmbedFaultingInstructionMode::None + insertWarningFn(IRB, Origin, InstName); LLVM_DEBUG(dbgs() << " CHECK: " << *Cmp << "\n"); } } + Value *getInstName(Instruction *Instruction) { + Value *InstName = nullptr; + + if (ClEmbedFaultingInst != MSanEmbedFaultingInstructionMode::None) { + IRBuilder<> IRB(Instruction); + StringRef InstNameStrRef; + // Keep str and maybeBuf at this scope level because they may be + // indirectly needed by CreateGlobalString + std::string str; + SmallVector maybeBuf; + + // 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 == MSanEmbedFaultingInstructionMode::Full) { + llvm::raw_string_ostream buf(str); + Instruction->print(buf); + InstNameStrRef = StringRef(str); + } else if (ClEmbedFaultingInst == + MSanEmbedFaultingInstructionMode::Name) { + if (CallInst *CI = dyn_cast(Instruction)) { + if (CI->getCalledFunction()) { + Twine description = "call " + CI->getCalledFunction()->getName(); + InstNameStrRef = description.toStringRef(maybeBuf); + } else { + InstNameStrRef = StringRef("Unknown call"); + } + } else { + InstNameStrRef = StringRef(Instruction->getOpcodeName()); + } + } + + InstName = IRB.CreateGlobalString(InstNameStrRef); + } + + return InstName; + } + void materializeInstructionChecks( ArrayRef InstructionChecks) { const DataLayout &DL = F.getDataLayout(); @@ -1455,6 +1569,9 @@ struct MemorySanitizerVisitor : public InstVisitor { // 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); @@ -1469,7 +1586,7 @@ struct MemorySanitizerVisitor : public InstVisitor { } 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 +1596,7 @@ struct MemorySanitizerVisitor : public InstVisitor { } if (!Combine) { - materializeOneCheck(IRB, ConvertedShadow, ShadowData.Origin); + materializeOneCheck(IRB, ConvertedShadow, ShadowData.Origin, InstName); continue; } @@ -1496,7 +1613,7 @@ struct MemorySanitizerVisitor : public InstVisitor { if (Shadow) { assert(Combine); IRBuilder<> IRB(Instruction); - materializeOneCheck(IRB, Shadow, nullptr); + materializeOneCheck(IRB, Shadow, nullptr, InstName); } }