diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index d3c6a7151ec37..3941bed37ebaf 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -265,9 +265,22 @@ static cl::opt cl::desc("Print name of local stack variable"), cl::Hidden, cl::init(true)); -static cl::opt ClPoisonUndef("msan-poison-undef", - cl::desc("poison undef temps"), cl::Hidden, - cl::init(true)); +static cl::opt + ClPoisonUndef("msan-poison-undef", + cl::desc("Poison fully undef temporary values. " + "Partially undefined constant vectors " + "are unaffected by this flag (see " + "-msan-poison-undef-vectors)."), + cl::Hidden, cl::init(true)); + +static cl::opt ClPoisonUndefVectors( + "msan-poison-undef-vectors", + cl::desc("Precisely poison partially undefined constant vectors. " + "If false (legacy behavior), the entire vector is " + "considered fully initialized, which may lead to false " + "negatives. Fully undefined constant vectors are " + "unaffected by this flag (see -msan-poison-undef)."), + cl::Hidden, cl::init(false)); static cl::opt ClHandleICmp("msan-handle-icmp", @@ -1181,6 +1194,7 @@ struct MemorySanitizerVisitor : public InstVisitor { bool PropagateShadow; bool PoisonStack; bool PoisonUndef; + bool PoisonUndefVectors; struct ShadowOriginAndInsertPoint { Value *Shadow; @@ -1207,6 +1221,7 @@ struct MemorySanitizerVisitor : public InstVisitor { PropagateShadow = SanitizeFunction; PoisonStack = SanitizeFunction && ClPoisonStack; PoisonUndef = SanitizeFunction && ClPoisonUndef; + PoisonUndefVectors = SanitizeFunction && ClPoisonUndefVectors; // In the presence of unreachable blocks, we may see Phi nodes with // incoming nodes from such blocks. Since InstVisitor skips unreachable @@ -1989,6 +2004,8 @@ struct MemorySanitizerVisitor : public InstVisitor { } return Shadow; } + // Handle fully undefined values + // (partially undefined constant vectors are handled later) if (UndefValue *U = dyn_cast(V)) { Value *AllOnes = (PropagateShadow && PoisonUndef) ? getPoisonedShadow(V) : getCleanShadow(V); @@ -2086,8 +2103,27 @@ struct MemorySanitizerVisitor : public InstVisitor { return ShadowPtr; } - // TODO: Partially undefined vectors are handled by the fall-through case - // below (see partial-poison.ll); this causes false negatives. + // Check for partially-undefined constant vectors + // TODO: scalable vectors (this is hard because we do not have IRBuilder) + if (isa(V->getType()) && isa(V) && + cast(V)->containsUndefOrPoisonElement() && PropagateShadow && + PoisonUndefVectors) { + unsigned NumElems = cast(V->getType())->getNumElements(); + SmallVector ShadowVector(NumElems); + for (unsigned i = 0; i != NumElems; ++i) { + Constant *Elem = cast(V)->getAggregateElement(i); + ShadowVector[i] = isa(Elem) ? getPoisonedShadow(Elem) + : getCleanShadow(Elem); + } + + Value *ShadowConstant = ConstantVector::get(ShadowVector); + LLVM_DEBUG(dbgs() << "Partial undef constant vector: " << *V << " ==> " + << *ShadowConstant << "\n"); + + return ShadowConstant; + } + + // TODO: partially-undefined constant arrays, structures, and nested types // For everything else the shadow is zero. return getCleanShadow(V); diff --git a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll index 5164441c17e10..025317a53c8d1 100644 --- a/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll +++ b/llvm/test/Instrumentation/MemorySanitizer/partial-poison.ll @@ -1,8 +1,9 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 -; RUN: opt < %s -S -passes='msan' 2>&1 | FileCheck %s +; RUN: opt < %s -S -passes='msan' -msan-poison-undef-vectors=true 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-PRECISE +; RUN: opt < %s -S -passes='msan' -msan-poison-undef-vectors=false 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-IMPRECISE ; -; Test case to show that MSan computes shadows for partially poisoned vectors -; as fully initialized, resulting in false negatives. +; Regression test case for computing shadows of partially poisoned vectors. +; Partially poisoned structs and arrays are not correctly implemented. 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" target triple = "x86_64-unknown-linux-gnu" @@ -11,7 +12,8 @@ define <2 x i64> @left_poison(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @left_poison( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-PRECISE: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-IMPRECISE: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -21,7 +23,8 @@ define <2 x i64> @right_poison(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @right_poison( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-PRECISE: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-IMPRECISE: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -51,7 +54,8 @@ define <2 x i64> @left_undef(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @left_undef( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-PRECISE: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-IMPRECISE: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -61,7 +65,8 @@ define <2 x i64> @right_undef(ptr %add.ptr) sanitize_memory { ; CHECK-LABEL: define <2 x i64> @right_undef( ; CHECK-SAME: ptr [[ADD_PTR:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: call void @llvm.donothing() -; CHECK-NEXT: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-PRECISE: store <2 x i64> , ptr @__msan_retval_tls, align 8 +; CHECK-IMPRECISE: store <2 x i64> zeroinitializer, ptr @__msan_retval_tls, align 8 ; CHECK-NEXT: ret <2 x i64> ; ret <2 x i64> @@ -76,3 +81,23 @@ define <2 x i64> @full_undef(ptr %add.ptr) sanitize_memory { ; ret <2 x i64> } + +define {i64, i64} @struct_left_undef() sanitize_memory { +; CHECK-LABEL: define { i64, i64 } @struct_left_undef( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: call void @llvm.donothing() +; CHECK-NEXT: store { i64, i64 } zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: ret { i64, i64 } { i64 undef, i64 42 } +; + ret {i64, i64} { i64 undef, i64 42 } +} + +define [2x i64] @array_right_undef() sanitize_memory { +; CHECK-LABEL: define [2 x i64] @array_right_undef( +; CHECK-SAME: ) #[[ATTR0]] { +; CHECK-NEXT: call void @llvm.donothing() +; CHECK-NEXT: store [2 x i64] zeroinitializer, ptr @__msan_retval_tls, align 8 +; CHECK-NEXT: ret [2 x i64] [i64 42, i64 undef] +; + ret [2x i64] [ i64 42, i64 undef ] +}