Skip to content

[msan] Add experimental '-mllvm -msan-embed-faulting-instruction' and MSAN_OPTIONS=print_faulting_instruction #136539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cb98d98
[msan] Add experimental '-mllvm -msan-print-faulting-instruction'
thurstond Mar 18, 2025
39a5ab9
Separate out embedding the instruction vs. printing the faulting
thurstond Apr 21, 2025
cacf9f2
clang-format
thurstond Apr 21, 2025
e539a52
Address Florian's feedback
thurstond Apr 21, 2025
2f2f248
Address one other piece of feedback
thurstond Apr 21, 2025
5ac8339
Reword comment
thurstond Apr 21, 2025
3dec04f
Rename CANT_PRINT_FAULTING_INSTRUCTION to WARN_IF_PRINT_FAULTING_INST…
thurstond Apr 22, 2025
97f827b
Revert scope level change because it causes use-after-scope
thurstond Apr 22, 2025
f41479c
Move maybeBuf to higher scope too
thurstond Apr 22, 2025
19fd6df
Rename IRB0 -> IRB since there is no longer name collision
thurstond Apr 22, 2025
97bece2
Add newline (and restart CI)
thurstond Apr 22, 2025
069356d
Rerun CI
thurstond Apr 22, 2025
e244b02
Fix rename
thurstond Apr 22, 2025
84728f1
Refactor to minimize code duplication between x() and x_instname()
thurstond Apr 22, 2025
f2ac5c7
clang-format
thurstond Apr 22, 2025
12f1a08
Typo
thurstond Apr 22, 2025
c639c69
Rerun CI
thurstond Apr 22, 2025
44ca7d0
Use FunctionType::get to reduce duplication for creating _instname
thurstond Apr 22, 2025
cdf5671
More simplification with FunctionType::get
thurstond Apr 22, 2025
1ef2ac5
Refactor to use getWarningFnName
thurstond Apr 22, 2025
91bd6bd
clang-format
thurstond Apr 22, 2025
c8c0762
Format getWarningFnName
thurstond Apr 22, 2025
7b9cebb
Change macro to function
thurstond Apr 22, 2025
277467f
PrintFaultingInstruction -> PrintFaultingInstructionIfRequested
thurstond Apr 22, 2025
973c7c2
Fix function declaration
thurstond Apr 22, 2025
9907618
Smaller SmallVectors
thurstond Apr 23, 2025
2ebd231
Really fix function declaration
thurstond May 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 108 additions & 28 deletions compiler-rt/lib/msan/msan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"); \
Expand Down Expand Up @@ -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 *,
Expand Down
5 changes: 5 additions & 0 deletions compiler-rt/lib/msan/msan_flags.inc
Original file line number Diff line number Diff line change
Expand Up @@ -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, "")
Expand Down
20 changes: 20 additions & 0 deletions compiler-rt/lib/msan/msan_interface_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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
Expand Down
93 changes: 93 additions & 0 deletions compiler-rt/test/msan/print_faulting_inst.cpp
Original file line number Diff line number Diff line change
@@ -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 <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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading