Skip to content

Commit afe6af1

Browse files
authored
[msan] Add optional flag to improve instrumentation of disjoint OR (llvm#145990)
The disjoint OR (llvm#72583) of two '1's is poison, hence the MSan ought to consider the result uninitialized (rather than initialized - i.e. a false negative - as per the existing instrumentation which ignores disjointedness). This patch adds a flag, `-msan-precise-disjoint-or`, which defaults to false (the legacy behavior). A future patch will default this flag to true. Updates the test from llvm#145982
1 parent 56b2c7d commit afe6af1

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,12 @@ static cl::opt<bool> ClPoisonUndefVectors(
282282
"unaffected by this flag (see -msan-poison-undef)."),
283283
cl::Hidden, cl::init(false));
284284

285+
static cl::opt<bool> ClPreciseDisjointOr(
286+
"msan-precise-disjoint-or",
287+
cl::desc("Precisely poison disjoint OR. If false (legacy behavior), "
288+
"disjointedness is ignored (i.e., 1|1 is initialized)."),
289+
cl::Hidden, cl::init(false));
290+
285291
static cl::opt<bool>
286292
ClHandleICmp("msan-handle-icmp",
287293
cl::desc("propagate shadow through ICmpEQ and ICmpNE"),
@@ -2497,11 +2503,16 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
24972503

24982504
void visitOr(BinaryOperator &I) {
24992505
IRBuilder<> IRB(&I);
2500-
// "Or" of 1 and a poisoned value results in unpoisoned value.
2501-
// 1|1 => 1; 0|1 => 1; p|1 => 1;
2502-
// 1|0 => 1; 0|0 => 0; p|0 => p;
2503-
// 1|p => 1; 0|p => p; p|p => p;
2504-
// S = (S1 & S2) | (~V1 & S2) | (S1 & ~V2)
2506+
// "Or" of 1 and a poisoned value results in unpoisoned value:
2507+
// 1|1 => 1; 0|1 => 1; p|1 => 1;
2508+
// 1|0 => 1; 0|0 => 0; p|0 => p;
2509+
// 1|p => 1; 0|p => p; p|p => p;
2510+
//
2511+
// S = (S1 & S2) | (~V1 & S2) | (S1 & ~V2)
2512+
//
2513+
// Addendum if the "Or" is "disjoint":
2514+
// 1|1 => p;
2515+
// S = S | (V1 & V2)
25052516
Value *S1 = getShadow(&I, 0);
25062517
Value *S2 = getShadow(&I, 1);
25072518
Value *V1 = IRB.CreateNot(I.getOperand(0));
@@ -2513,7 +2524,14 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
25132524
Value *S1S2 = IRB.CreateAnd(S1, S2);
25142525
Value *V1S2 = IRB.CreateAnd(V1, S2);
25152526
Value *S1V2 = IRB.CreateAnd(S1, V2);
2516-
setShadow(&I, IRB.CreateOr({S1S2, V1S2, S1V2}));
2527+
2528+
Value *S = IRB.CreateOr({S1S2, V1S2, S1V2});
2529+
if (ClPreciseDisjointOr && cast<PossiblyDisjointInst>(&I)->isDisjoint()) {
2530+
Value *V1V2 = IRB.CreateAnd(V1, V2);
2531+
S = IRB.CreateOr({S, V1V2});
2532+
}
2533+
2534+
setShadow(&I, S);
25172535
setOriginForNaryOp(I);
25182536
}
25192537

llvm/test/Instrumentation/MemorySanitizer/or.ll

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2-
; RUN: opt < %s -S -passes=msan 2>&1 | FileCheck %s
2+
; RUN: opt < %s -S -passes=msan -msan-precise-disjoint-or=false 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-IMPRECISE
3+
; RUN: opt < %s -S -passes=msan -msan-precise-disjoint-or=true 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-PRECISE
34
;
4-
; Test bitwise OR instructions, especially the "disjoint OR", which is
5-
; currently handled incorrectly by MSan (as if it was a regular OR).
5+
; Test bitwise OR instructions, including "disjoint OR".
66

77
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
88
target triple = "x86_64-unknown-linux-gnu"
@@ -41,8 +41,15 @@ define i8 @test_disjoint_or(i8 %a, i8 %b) sanitize_memory {
4141
; CHECK-NEXT: [[TMP7:%.*]] = and i8 [[TMP1]], [[TMP4]]
4242
; CHECK-NEXT: [[TMP8:%.*]] = or i8 [[TMP5]], [[TMP6]]
4343
; CHECK-NEXT: [[TMP11:%.*]] = or i8 [[TMP8]], [[TMP7]]
44-
; CHECK-NEXT: [[C:%.*]] = or disjoint i8 [[A]], [[B]]
45-
; CHECK-NEXT: store i8 [[TMP11]], ptr @__msan_retval_tls, align 8
44+
;
45+
; CHECK-IMPRECISE: [[C:%.*]] = or disjoint i8 [[A]], [[B]]
46+
; CHECK-IMPRECISE-NEXT: store i8 [[TMP11]], ptr @__msan_retval_tls, align 8
47+
;
48+
; CHECK-PRECISE: [[TMP10:%.*]] = and i8 [[TMP3]], [[TMP4]]
49+
; CHECK-PRECISE-NEXT: [[TMP12:%.*]] = or i8 [[TMP11]], [[TMP10]]
50+
; CHECK-PRECISE-NEXT: [[C:%.*]] = or disjoint i8 [[A]], [[B]]
51+
; CHECK-PRECISE-NEXT: store i8 [[TMP12]], ptr @__msan_retval_tls, align 8
52+
;
4653
; CHECK-NEXT: ret i8 [[C]]
4754
;
4855
%c = or disjoint i8 %a, %b

0 commit comments

Comments
 (0)