Skip to content

Commit c85466d

Browse files
authored
Reapply "[msan] Automatically print shadow for failing outlined checks" (#145611) (#145615)
This reverts commit 5eb5f0d i.e., relands 1b71ea4. Test case was failing on aarch64 because the long double type is implemented differently on x86 vs aarch64. This reland restricts the test to x86. ---- Original CL description: A commonly used aid for debugging MSan reports is `__msan_print_shadow()`, which requires manual app code annotations (typically of the variable in the UUM report or nearby). This is in contrast to ASan, which automatically prints out the shadow map when a check fails. This patch changes MSan to print the shadow that failed an outlined check (checks are outlined per function after the `-msan-instrumentation-with-call-threshold` is exceeded) if verbosity >= 1. Note that we do not print out the shadow map of "neighboring" variables because this is technically infeasible; see "Caveat" below. This patch can be easier to use than `__msan_print_shadow()` because this does not require manual app code annotations. Additionally, due to optimizations, `__msan_print_shadow()` calls can sometimes spuriously affect whether a variable is initialized. As a side effect, this patch also enables outlined checks for arbitrary-sized shadows (vs. the current hardcoded handlers for {1,2,4,8}-byte shadows). Caveat: the shadow does not necessarily correspond to an individual user variable, because MSan instrumentation may combine and/or truncate multiple shadows prior to emitting a check that the mangled shadow is zero (e.g., `convertShadowToScalar()`, `handleSSEVectorConvertIntrinsic()`, `materializeInstructionChecks()`). OTOH it is arguably a strength that this feature emit the shadow that directly matters for the MSan check, but which cannot be obtained using the MSan API.
1 parent 3de2af3 commit c85466d

File tree

5 files changed

+145
-22
lines changed

5 files changed

+145
-22
lines changed

compiler-rt/lib/msan/msan.cpp

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -352,23 +352,69 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(
352352

353353
using namespace __msan;
354354

355-
#define MSAN_MAYBE_WARNING(type, size) \
356-
void __msan_maybe_warning_##size(type s, u32 o) { \
357-
GET_CALLER_PC_BP; \
358-
if (UNLIKELY(s)) { \
359-
PrintWarningWithOrigin(pc, bp, o); \
360-
if (__msan::flags()->halt_on_error) { \
361-
Printf("Exiting\n"); \
362-
Die(); \
363-
} \
364-
} \
355+
// N.B. Only [shadow, shadow+size) is defined. shadow is *not* a pointer into
356+
// an MSan shadow region.
357+
static void print_shadow_value(void *shadow, u64 size) {
358+
Printf("Shadow value (%llu byte%s):", size, size == 1 ? "" : "s");
359+
for (unsigned int i = 0; i < size; i++) {
360+
if (i % 4 == 0)
361+
Printf(" ");
362+
363+
unsigned char x = ((unsigned char *)shadow)[i];
364+
Printf("%x%x", x >> 4, x & 0xf);
365+
}
366+
Printf("\n");
367+
Printf(
368+
"Caveat: the shadow value does not necessarily directly correspond to a "
369+
"single user variable. The correspondence is stronger, but not always "
370+
"perfect, when origin tracking is enabled.\n");
371+
Printf("\n");
372+
}
373+
374+
#define MSAN_MAYBE_WARNING(type, size) \
375+
void __msan_maybe_warning_##size(type s, u32 o) { \
376+
GET_CALLER_PC_BP; \
377+
\
378+
if (UNLIKELY(s)) { \
379+
if (Verbosity() >= 1) \
380+
print_shadow_value((void *)(&s), sizeof(s)); \
381+
PrintWarningWithOrigin(pc, bp, o); \
382+
if (__msan::flags()->halt_on_error) { \
383+
Printf("Exiting\n"); \
384+
Die(); \
385+
} \
386+
} \
365387
}
366388

367389
MSAN_MAYBE_WARNING(u8, 1)
368390
MSAN_MAYBE_WARNING(u16, 2)
369391
MSAN_MAYBE_WARNING(u32, 4)
370392
MSAN_MAYBE_WARNING(u64, 8)
371393

394+
// N.B. Only [shadow, shadow+size) is defined. shadow is *not* a pointer into
395+
// an MSan shadow region.
396+
void __msan_maybe_warning_N(void *shadow, u64 size, u32 o) {
397+
GET_CALLER_PC_BP;
398+
399+
bool allZero = true;
400+
for (unsigned int i = 0; i < size; i++) {
401+
if (((char *)shadow)[i]) {
402+
allZero = false;
403+
break;
404+
}
405+
}
406+
407+
if (UNLIKELY(!allZero)) {
408+
if (Verbosity() >= 1)
409+
print_shadow_value(shadow, size);
410+
PrintWarningWithOrigin(pc, bp, o);
411+
if (__msan::flags()->halt_on_error) {
412+
Printf("Exiting\n");
413+
Die();
414+
}
415+
}
416+
}
417+
372418
#define MSAN_MAYBE_STORE_ORIGIN(type, size) \
373419
void __msan_maybe_store_origin_##size(type s, void *p, u32 o) { \
374420
if (UNLIKELY(s)) { \

compiler-rt/lib/msan/msan_interface_internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ SANITIZER_INTERFACE_ATTRIBUTE
6060
void __msan_maybe_warning_4(u32 s, u32 o);
6161
SANITIZER_INTERFACE_ATTRIBUTE
6262
void __msan_maybe_warning_8(u64 s, u32 o);
63+
SANITIZER_INTERFACE_ATTRIBUTE
64+
void __msan_maybe_warning_N(void *shadow, u64 size, u32 o);
6365

6466
SANITIZER_INTERFACE_ATTRIBUTE
6567
void __msan_maybe_store_origin_1(u8 s, void *p, u32 o);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %clangxx_msan -fsanitize-recover=memory -mllvm -msan-instrumentation-with-call-threshold=0 -g %s -o %t \
2+
// RUN: && not env MSAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s
3+
4+
// REQUIRES: x86_64-target-arch
5+
// 'long double' implementation varies between platforms.
6+
7+
#include <ctype.h>
8+
#include <stdio.h>
9+
10+
#include <sanitizer/msan_interface.h>
11+
12+
int main(int argc, char *argv[]) {
13+
long double a;
14+
printf("a: %Lf\n", a);
15+
// CHECK: Shadow value (16 bytes): ffffffff ffffffff ffff0000 00000000
16+
17+
unsigned long long b;
18+
printf("b: %llu\n", b);
19+
// CHECK: Shadow value (8 bytes): ffffffff ffffffff
20+
21+
char *p = (char *)(&b);
22+
p[2] = 36;
23+
printf("b: %lld\n", b);
24+
// CHECK: Shadow value (8 bytes): ffff00ff ffffffff
25+
26+
b = b << 8;
27+
printf("b: %lld\n", b);
28+
__msan_print_shadow(&b, sizeof(b));
29+
// CHECK: Shadow value (8 bytes): 00ffff00 ffffffff
30+
31+
unsigned int c;
32+
printf("c: %u\n", c);
33+
// CHECK: Shadow value (4 bytes): ffffffff
34+
35+
// Converted to boolean
36+
if (c) {
37+
// CHECK: Shadow value (1 byte): 01
38+
printf("Hello\n");
39+
}
40+
41+
return 0;
42+
}

llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,7 @@ class MemorySanitizer {
665665

666666
// These arrays are indexed by log2(AccessSize).
667667
FunctionCallee MaybeWarningFn[kNumberOfAccessSizes];
668+
FunctionCallee MaybeWarningVarSizeFn;
668669
FunctionCallee MaybeStoreOriginFn[kNumberOfAccessSizes];
669670

670671
/// Run-time helper that generates a new origin value for a stack
@@ -939,7 +940,9 @@ void MemorySanitizer::createUserspaceApi(Module &M,
939940
MaybeWarningFn[AccessSizeIndex] = M.getOrInsertFunction(
940941
FunctionName, TLI.getAttrList(C, {0, 1}, /*Signed=*/false),
941942
IRB.getVoidTy(), IRB.getIntNTy(AccessSize * 8), IRB.getInt32Ty());
942-
943+
MaybeWarningVarSizeFn = M.getOrInsertFunction(
944+
"__msan_maybe_warning_N", TLI.getAttrList(C, {}, /*Signed=*/false),
945+
IRB.getVoidTy(), PtrTy, IRB.getInt64Ty(), IRB.getInt32Ty());
943946
FunctionName = "__msan_maybe_store_origin_" + itostr(AccessSize);
944947
MaybeStoreOriginFn[AccessSizeIndex] = M.getOrInsertFunction(
945948
FunctionName, TLI.getAttrList(C, {0, 2}, /*Signed=*/false),
@@ -1248,7 +1251,6 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
12481251
// Constants likely will be eliminated by follow-up passes.
12491252
if (isa<Constant>(V))
12501253
return false;
1251-
12521254
++SplittableBlocksCount;
12531255
return ClInstrumentationWithCallThreshold >= 0 &&
12541256
SplittableBlocksCount > ClInstrumentationWithCallThreshold;
@@ -1447,18 +1449,32 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
14471449
const DataLayout &DL = F.getDataLayout();
14481450
TypeSize TypeSizeInBits = DL.getTypeSizeInBits(ConvertedShadow->getType());
14491451
unsigned SizeIndex = TypeSizeToSizeIndex(TypeSizeInBits);
1450-
if (instrumentWithCalls(ConvertedShadow) &&
1451-
SizeIndex < kNumberOfAccessSizes && !MS.CompileKernel) {
1452-
FunctionCallee Fn = MS.MaybeWarningFn[SizeIndex];
1452+
if (instrumentWithCalls(ConvertedShadow) && !MS.CompileKernel) {
14531453
// ZExt cannot convert between vector and scalar
14541454
ConvertedShadow = convertShadowToScalar(ConvertedShadow, IRB);
14551455
Value *ConvertedShadow2 =
14561456
IRB.CreateZExt(ConvertedShadow, IRB.getIntNTy(8 * (1 << SizeIndex)));
1457-
CallBase *CB = IRB.CreateCall(
1458-
Fn, {ConvertedShadow2,
1459-
MS.TrackOrigins && Origin ? Origin : (Value *)IRB.getInt32(0)});
1460-
CB->addParamAttr(0, Attribute::ZExt);
1461-
CB->addParamAttr(1, Attribute::ZExt);
1457+
1458+
if (SizeIndex < kNumberOfAccessSizes) {
1459+
FunctionCallee Fn = MS.MaybeWarningFn[SizeIndex];
1460+
CallBase *CB = IRB.CreateCall(
1461+
Fn,
1462+
{ConvertedShadow2,
1463+
MS.TrackOrigins && Origin ? Origin : (Value *)IRB.getInt32(0)});
1464+
CB->addParamAttr(0, Attribute::ZExt);
1465+
CB->addParamAttr(1, Attribute::ZExt);
1466+
} else {
1467+
FunctionCallee Fn = MS.MaybeWarningVarSizeFn;
1468+
Value *ShadowAlloca = IRB.CreateAlloca(ConvertedShadow2->getType(), 0u);
1469+
IRB.CreateStore(ConvertedShadow2, ShadowAlloca);
1470+
unsigned ShadowSize = DL.getTypeAllocSize(ConvertedShadow2->getType());
1471+
CallBase *CB = IRB.CreateCall(
1472+
Fn,
1473+
{ShadowAlloca, ConstantInt::get(IRB.getInt64Ty(), ShadowSize),
1474+
MS.TrackOrigins && Origin ? Origin : (Value *)IRB.getInt32(0)});
1475+
CB->addParamAttr(1, Attribute::ZExt);
1476+
CB->addParamAttr(2, Attribute::ZExt);
1477+
}
14621478
} else {
14631479
Value *Cmp = convertToBool(ConvertedShadow, IRB, "_mscmp");
14641480
Instruction *CheckTerm = SplitBlockAndInsertIfThen(

llvm/test/Instrumentation/MemorySanitizer/with-call-type-size.ll

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,30 @@ define <4 x i32> @test64(<4 x i32> %vec, i64 %idx, i32 %x) sanitize_memory {
7373
; CHECK: call void @__msan_maybe_warning_8(i64 zeroext %{{.*}}, i32 zeroext 0)
7474
; CHECK: ret <4 x i32>
7575

76-
; Type size too large => inline check.
76+
; Type size too large => use variable-size handler.
7777
define <4 x i32> @test65(<4 x i32> %vec, i65 %idx, i32 %x) sanitize_memory {
7878
%vec1 = insertelement <4 x i32> %vec, i32 %x, i65 %idx
7979
ret <4 x i32> %vec1
8080
}
8181
; CHECK-LABEL: @test65(
82-
; CHECK: call void @__msan_warning_noreturn
82+
; CHECK: %[[A:.*]] = zext i65 %1 to i128
83+
; CHECK: call void @__msan_maybe_warning_N(ptr %{{.*}}, i64 zeroext 16, i32 zeroext 0)
84+
; CHECK: ret <4 x i32>
85+
86+
define <4 x i32> @test128(<4 x i32> %vec, i128 %idx, i32 %x) sanitize_memory {
87+
%vec1 = insertelement <4 x i32> %vec, i32 %x, i128 %idx
88+
ret <4 x i32> %vec1
89+
}
90+
; CHECK-LABEL: @test128(
91+
; CHECK: call void @__msan_maybe_warning_N(ptr %{{.*}}, i64 zeroext 16, i32 zeroext 0)
92+
; CHECK: ret <4 x i32>
93+
94+
define <4 x i32> @test256(<4 x i32> %vec, i256 %idx, i32 %x) sanitize_memory {
95+
%vec1 = insertelement <4 x i32> %vec, i32 %x, i256 %idx
96+
ret <4 x i32> %vec1
97+
}
98+
; CHECK-LABEL: @test256(
99+
; CHECK: call void @__msan_maybe_warning_N(ptr %{{.*}}, i64 zeroext 32, i32 zeroext 0)
83100
; CHECK: ret <4 x i32>
84101

85102
define <4 x i32> @testUndef(<4 x i32> %vec, i32 %x) sanitize_memory {

0 commit comments

Comments
 (0)