Skip to content

Commit cb98d98

Browse files
committed
[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.
1 parent 7547ad3 commit cb98d98

File tree

4 files changed

+293
-29
lines changed

4 files changed

+293
-29
lines changed

compiler-rt/lib/msan/msan.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,29 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
352352

353353
using namespace __msan;
354354

355+
#define PRINT_FAULTING_INSTRUCTION(instname) \
356+
Printf("Instruction that failed the shadow check: %s\n", instname); \
357+
Printf("\n");
358+
359+
#define MSAN_MAYBE_WARNING_INSTNAME(type, size, instname) \
360+
void __msan_maybe_warning_instname_##size(type s, u32 o, char *instname) { \
361+
GET_CALLER_PC_BP; \
362+
if (UNLIKELY(s)) { \
363+
if (instname) \
364+
PRINT_FAULTING_INSTRUCTION(instname); \
365+
PrintWarningWithOrigin(pc, bp, o); \
366+
if (__msan::flags()->halt_on_error) { \
367+
Printf("Exiting\n"); \
368+
Die(); \
369+
} \
370+
} \
371+
}
372+
373+
MSAN_MAYBE_WARNING_INSTNAME(u8, 1, instname)
374+
MSAN_MAYBE_WARNING_INSTNAME(u16, 2, instname)
375+
MSAN_MAYBE_WARNING_INSTNAME(u32, 4, instname)
376+
MSAN_MAYBE_WARNING_INSTNAME(u64, 8, instname)
377+
355378
#define MSAN_MAYBE_WARNING(type, size) \
356379
void __msan_maybe_warning_##size(type s, u32 o) { \
357380
GET_CALLER_PC_BP; \
@@ -426,6 +449,57 @@ void __msan_warning_with_origin_noreturn(u32 origin) {
426449
Die();
427450
}
428451

452+
// We duplicate the non _instname function's body because we don't want to
453+
// pollute the stack traces with an additional function call.
454+
//
455+
// We can't use a simple macro wrapper, because the instrumentation pass
456+
// expects function symbols.
457+
// We don't add instname as a parameter everywhere (with a check whether the
458+
// value is null) to avoid polluting the fastpath.
459+
void __msan_warning_instname(char *instname) {
460+
PRINT_FAULTING_INSTRUCTION(instname);
461+
GET_CALLER_PC_BP;
462+
PrintWarningWithOrigin(pc, bp, 0);
463+
if (__msan::flags()->halt_on_error) {
464+
if (__msan::flags()->print_stats)
465+
ReportStats();
466+
Printf("Exiting\n");
467+
Die();
468+
}
469+
}
470+
471+
void __msan_warning_noreturn_instname(char *instname) {
472+
PRINT_FAULTING_INSTRUCTION(instname);
473+
GET_CALLER_PC_BP;
474+
PrintWarningWithOrigin(pc, bp, 0);
475+
if (__msan::flags()->print_stats)
476+
ReportStats();
477+
Printf("Exiting\n");
478+
Die();
479+
}
480+
481+
void __msan_warning_with_origin_instname(u32 origin, char *instname) {
482+
PRINT_FAULTING_INSTRUCTION(instname);
483+
GET_CALLER_PC_BP;
484+
PrintWarningWithOrigin(pc, bp, origin);
485+
if (__msan::flags()->halt_on_error) {
486+
if (__msan::flags()->print_stats)
487+
ReportStats();
488+
Printf("Exiting\n");
489+
Die();
490+
}
491+
}
492+
493+
void __msan_warning_with_origin_noreturn_instname(u32 origin, char *instname) {
494+
PRINT_FAULTING_INSTRUCTION(instname);
495+
GET_CALLER_PC_BP;
496+
PrintWarningWithOrigin(pc, bp, origin);
497+
if (__msan::flags()->print_stats)
498+
ReportStats();
499+
Printf("Exiting\n");
500+
Die();
501+
}
502+
429503
static void OnStackUnwind(const SignalContext &sig, const void *,
430504
BufferedStackTrace *stack) {
431505
stack->Unwind(StackTrace::GetNextInstructionPc(sig.pc), sig.bp, sig.context,

compiler-rt/lib/msan/msan_interface_internal.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,18 @@ void __msan_init();
3030
SANITIZER_INTERFACE_ATTRIBUTE
3131
void __msan_warning();
3232

33+
SANITIZER_INTERFACE_ATTRIBUTE
34+
void __msan_warning_instname(char *instname);
35+
3336
// Print a warning and die.
3437
// Instrumentation inserts calls to this function when building in "fast" mode
3538
// (i.e. -mllvm -msan-keep-going)
3639
SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn))
3740
void __msan_warning_noreturn();
3841

42+
SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) void
43+
__msan_warning_noreturn_instname(char *instname);
44+
3945
using __sanitizer::uptr;
4046
using __sanitizer::sptr;
4147
using __sanitizer::uu64;
@@ -49,8 +55,13 @@ using __sanitizer::u8;
4955
// Versions of the above which take Origin as a parameter
5056
SANITIZER_INTERFACE_ATTRIBUTE
5157
void __msan_warning_with_origin(u32 origin);
58+
SANITIZER_INTERFACE_ATTRIBUTE
59+
void __msan_warning_with_origin_instname(u32 origin, char *instname);
60+
5261
SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) void
5362
__msan_warning_with_origin_noreturn(u32 origin);
63+
SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) void
64+
__msan_warning_with_origin_noreturn_instname(u32 origin, char *instname);
5465

5566
SANITIZER_INTERFACE_ATTRIBUTE
5667
void __msan_maybe_warning_1(u8 s, u32 o);
@@ -61,6 +72,15 @@ void __msan_maybe_warning_4(u32 s, u32 o);
6172
SANITIZER_INTERFACE_ATTRIBUTE
6273
void __msan_maybe_warning_8(u64 s, u32 o);
6374

75+
SANITIZER_INTERFACE_ATTRIBUTE
76+
void __msan_maybe_warning_1_instname(u8 s, u32 o, char *instname);
77+
SANITIZER_INTERFACE_ATTRIBUTE
78+
void __msan_maybe_warning_2_instname(u16 s, u32 o, char *instname);
79+
SANITIZER_INTERFACE_ATTRIBUTE
80+
void __msan_maybe_warning_4_instname(u32 s, u32 o, char *instname);
81+
SANITIZER_INTERFACE_ATTRIBUTE
82+
void __msan_maybe_warning_8_instname(u64 s, u32 o, char *instname);
83+
6484
SANITIZER_INTERFACE_ATTRIBUTE
6585
void __msan_maybe_store_origin_1(u8 s, void *p, u32 o);
6686
SANITIZER_INTERFACE_ATTRIBUTE
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Try parameter '0' (program runs cleanly)
2+
// -------------------------------------------------------
3+
// RUN: %clangxx_msan -g %s -o %t && %run %t 0
4+
5+
// Try parameter '1'
6+
// -------------------------------------------------------
7+
// RUN: %clangxx_msan -g %s -o %t && not %run %t 1 >%t.out 2>&1
8+
// RUN: FileCheck --check-prefix STORE-CHECK %s < %t.out
9+
10+
// RUN: %clangxx_msan -mllvm -msan-print-faulting-instruction=1 -g %s -o %t && not %run %t 1 >%t.out 2>&1
11+
// RUN: FileCheck --check-prefixes VERBOSE-STORE-CHECK,STORE-CHECK %s < %t.out
12+
13+
// RUN: %clangxx_msan -mllvm -msan-print-faulting-instruction=2 -g %s -o %t && not %run %t 1 >%t.out 2>&1
14+
// RUN: FileCheck --check-prefixes VERY-VERBOSE-STORE-CHECK,STORE-CHECK %s < %t.out
15+
16+
// Try parameter '2', with -fsanitize-memory-param-retval
17+
// -------------------------------------------------------
18+
// RUN: %clangxx_msan -fsanitize-memory-param-retval -g %s -o %t && not %run %t 2 >%t.out 2>&1
19+
// RUN: FileCheck --check-prefix PARAM-CHECK %s < %t.out
20+
21+
// 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
22+
// RUN: FileCheck --check-prefixes VERBOSE-PARAM-CHECK,PARAM-CHECK %s < %t.out
23+
24+
// 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
25+
// RUN: FileCheck --check-prefixes VERY-VERBOSE-PARAM-CHECK,PARAM-CHECK %s < %t.out
26+
27+
// Try parameter '2', with -fno-sanitize-memory-param-retval
28+
// -------------------------------------------------------
29+
// RUN: %clangxx_msan -fno-sanitize-memory-param-retval -g %s -o %t && not %run %t 2 >%t.out 2>&1
30+
// RUN: FileCheck --check-prefix NO-PARAM-CHECK %s < %t.out
31+
32+
// 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
33+
// RUN: FileCheck --check-prefixes VERBOSE-NO-PARAM-CHECK,NO-PARAM-CHECK %s < %t.out
34+
35+
// 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
36+
// RUN: FileCheck --check-prefixes VERY-VERBOSE-NO-PARAM-CHECK,NO-PARAM-CHECK %s < %t.out
37+
38+
#include <stdio.h>
39+
#include <stdlib.h>
40+
41+
#define THRICE(o,t) twice(o,t)
42+
43+
__attribute__((noinline)) extern "C" int twice(int o, int t) {
44+
return o + t < 3;
45+
}
46+
47+
int main(int argc, char *argv[]) {
48+
int buf[100];
49+
buf[0] = 42;
50+
buf[1] = 43;
51+
52+
if (argc != 2) {
53+
printf("Usage: %s index\n", argv[0]);
54+
return 1;
55+
}
56+
57+
int index = atoi(argv[1]);
58+
int val = buf[index];
59+
60+
printf("index %d, abs(val) %d, THRICE(val,5) %d\n", index, abs(val), THRICE(val,5));
61+
// VERY-VERBOSE-PARAM-CHECK: Instruction that failed the shadow check: %{{.*}} = call noundef i32 @twice(i32 noundef %{{.*}}, i32 noundef 5)
62+
// VERBOSE-PARAM-CHECK: Instruction that failed the shadow check: call twice
63+
// PARAM-CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
64+
// PARAM-CHECK: {{#0 0x.* in main .*print_faulting_inst.cpp:}}[[@LINE-4]]
65+
66+
if (val)
67+
// VERY-VERBOSE-NO-PARAM-CHECK: Instruction that failed the shadow check: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}
68+
// VERBOSE-NO-PARAM-CHECK: Instruction that failed the shadow check: br
69+
// NO-PARAM-CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
70+
// NO-PARAM-CHECK: {{#0 0x.* in main .*print_faulting_inst.cpp:}}[[@LINE-4]]
71+
printf("Variable is non-zero\n");
72+
else
73+
printf("Variable is zero\n");
74+
75+
int nextval = buf[index + 1];
76+
buf[nextval + abs(index)] = twice(index,6);
77+
// VERY-VERBOSE-STORE-CHECK: Instruction that failed the shadow check: store i32 %{{.*}}, ptr %{{.*}}
78+
// VERBOSE-STORE-CHECK: Instruction that failed the shadow check: store
79+
// STORE-CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
80+
// STORE-CHECK: {{#0 0x.* in main .*print_faulting_inst.cpp:}}[[@LINE-4]]
81+
82+
return 0;
83+
}

0 commit comments

Comments
 (0)