cl::desc("report accesses through a pointer which has poisoned shadow"),
cl::Hidden, cl::init(true));
+static cl::opt<bool> ClEagerChecks(
+ "msan-eager-checks",
+ cl::desc("check arguments and return values at function call boundaries"),
+ cl::Hidden, cl::init(false));
+
static cl::opt<bool> ClDumpStrictInstructions("msan-dump-strict-instructions",
cl::desc("print out instructions with default strict semantics"),
cl::Hidden, cl::init(false));
bool PropagateShadow;
bool PoisonStack;
bool PoisonUndef;
- bool CheckReturnValue;
struct ShadowOriginAndInsertPoint {
Value *Shadow;
PropagateShadow = SanitizeFunction;
PoisonStack = SanitizeFunction && ClPoisonStack;
PoisonUndef = SanitizeFunction && ClPoisonUndef;
- // FIXME: Consider using SpecialCaseList to specify a list of functions that
- // must always return fully initialized values. For now, we hardcode "main".
- CheckReturnValue = SanitizeFunction && (F.getName() == "main");
MS.initializeCallbacks(*F.getParent());
if (MS.CompileKernel)
LLVM_DEBUG(dbgs() << "Arg is not sized\n");
continue;
}
+
+ bool FArgByVal = FArg.hasByValAttr();
+ bool FArgNoUndef = FArg.hasAttribute(Attribute::NoUndef);
+ bool FArgEagerCheck = ClEagerChecks && !FArgByVal && FArgNoUndef;
unsigned Size =
FArg.hasByValAttr()
? DL.getTypeAllocSize(FArg.getParamByValType())
: DL.getTypeAllocSize(FArg.getType());
+
if (A == &FArg) {
bool Overflow = ArgOffset + Size > kParamTLSSize;
- Value *Base = getShadowPtrForArgument(&FArg, EntryIRB, ArgOffset);
- if (FArg.hasByValAttr()) {
+ if (FArgEagerCheck) {
+ *ShadowPtr = getCleanShadow(V);
+ setOrigin(A, getCleanOrigin());
+ continue;
+ } else if (FArgByVal) {
+ Value *Base = getShadowPtrForArgument(&FArg, EntryIRB, ArgOffset);
// ByVal pointer itself has clean shadow. We copy the actual
// argument shadow to the underlying memory.
// Figure out maximal valid memcpy alignment.
}
*ShadowPtr = getCleanShadow(V);
} else {
+ // Shadow over TLS
+ Value *Base = getShadowPtrForArgument(&FArg, EntryIRB, ArgOffset);
if (Overflow) {
// ParamTLS overflow.
*ShadowPtr = getCleanShadow(V);
setOrigin(A, getCleanOrigin());
}
}
- ArgOffset += alignTo(Size, kShadowTLSAlignment);
+
+ if (!FArgEagerCheck)
+ ArgOffset += alignTo(Size, kShadowTLSAlignment);
}
assert(*ShadowPtr && "Could not find shadow for an argument");
return *ShadowPtr;
<< " Shadow: " << *ArgShadow << "\n");
bool ArgIsInitialized = false;
const DataLayout &DL = F.getParent()->getDataLayout();
- if (CB.paramHasAttr(i, Attribute::ByVal)) {
+
+ bool ByVal = CB.paramHasAttr(i, Attribute::ByVal);
+ bool NoUndef = CB.paramHasAttr(i, Attribute::NoUndef);
+ bool EagerCheck = ClEagerChecks && !ByVal && NoUndef;
+
+ if (EagerCheck) {
+ insertShadowCheck(A, &CB);
+ continue;
+ }
+ if (ByVal) {
+ // ByVal requires some special handling as it's too big for a single
+ // load
assert(A->getType()->isPointerTy() &&
"ByVal argument is not a pointer!");
Size = DL.getTypeAllocSize(CB.getParamByValType(i));
Alignment, Size);
// TODO(glider): need to copy origins.
} else {
+ // Any other parameters mean we need bit-grained tracking of uninit data
Size = DL.getTypeAllocSize(A->getType());
if (ArgOffset + Size > kParamTLSSize) break;
Store = IRB.CreateAlignedStore(ArgShadow, ArgShadowBase,
// Don't emit the epilogue for musttail call returns.
if (isa<CallInst>(CB) && cast<CallInst>(CB).isMustTailCall())
return;
+
+ if (ClEagerChecks && CB.hasRetAttr(Attribute::NoUndef)) {
+ setShadow(&CB, getCleanShadow(&CB));
+ setOrigin(&CB, getCleanOrigin());
+ return;
+ }
+
IRBuilder<> IRBBefore(&CB);
// Until we have full dynamic coverage, make sure the retval shadow is 0.
Value *Base = getShadowPtrForRetval(&CB, IRBBefore);
// Don't emit the epilogue for musttail call returns.
if (isAMustTailRetVal(RetVal)) return;
Value *ShadowPtr = getShadowPtrForRetval(RetVal, IRB);
- if (CheckReturnValue) {
+ bool HasNoUndef =
+ F.hasAttribute(AttributeList::ReturnIndex, Attribute::NoUndef);
+ bool StoreShadow = !(ClEagerChecks && HasNoUndef);
+ // FIXME: Consider using SpecialCaseList to specify a list of functions that
+ // must always return fully initialized values. For now, we hardcode "main".
+ bool EagerCheck = (ClEagerChecks && HasNoUndef) || (F.getName() == "main");
+
+ Value *Shadow = getShadow(RetVal);
+ bool StoreOrigin = true;
+ if (EagerCheck) {
insertShadowCheck(RetVal, &I);
- Value *Shadow = getCleanShadow(RetVal);
- IRB.CreateAlignedStore(Shadow, ShadowPtr, kShadowTLSAlignment);
- } else {
- Value *Shadow = getShadow(RetVal);
+ Shadow = getCleanShadow(RetVal);
+ StoreOrigin = false;
+ }
+
+ // The caller may still expect information passed over TLS if we pass our
+ // check
+ if (StoreShadow) {
IRB.CreateAlignedStore(Shadow, ShadowPtr, kShadowTLSAlignment);
- if (MS.TrackOrigins)
+ if (MS.TrackOrigins && StoreOrigin)
IRB.CreateStore(getOrigin(RetVal), getOriginPtrForRetval(IRB));
}
}
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -msan-check-access-address=0 -msan-track-origins=1 -msan-eager-checks -S -passes='module(msan-module),function(msan)' 2>&1 | \
+; RUN: FileCheck -allow-deprecated-dag-overlap -check-prefixes=CHECK,CHECK-ORIGINS %s
+
+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"
+
+define noundef i32 @NormalRet() nounwind uwtable sanitize_memory {
+; CHECK-LABEL: @NormalRet(
+; CHECK-NEXT: ret i32 123
+;
+ ret i32 123
+}
+
+define i32 @PartialRet() nounwind uwtable sanitize_memory {
+; CHECK-LABEL: @PartialRet(
+; CHECK-NEXT: store i32 0, i32* bitcast ([100 x i64]* @__msan_retval_tls to i32*), align 8
+; CHECK-NEXT: store i32 0, i32* @__msan_retval_origin_tls, align 4
+; CHECK-NEXT: ret i32 123
+;
+ ret i32 123
+}
+
+define noundef i32 @LoadedRet() nounwind uwtable sanitize_memory {
+; CHECK-LABEL: @LoadedRet(
+; CHECK-NEXT: [[P:%.*]] = inttoptr i64 0 to i32*
+; CHECK-NEXT: [[O:%.*]] = load i32, i32* [[P]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint i32* [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = xor i64 [[TMP1]], 87960930222080
+; CHECK-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to i32*
+; CHECK-NEXT: [[TMP4:%.*]] = add i64 [[TMP2]], 17592186044416
+; CHECK-NEXT: [[TMP5:%.*]] = inttoptr i64 [[TMP4]] to i32*
+; CHECK-NEXT: [[_MSLD:%.*]] = load i32, i32* [[TMP3]], align 4
+; CHECK-NEXT: [[TMP6:%.*]] = load i32, i32* [[TMP5]], align 4
+; CHECK-NEXT: [[_MSCMP:%.*]] = icmp ne i32 [[_MSLD]], 0
+; CHECK-NEXT: br i1 [[_MSCMP]], label [[TMP7:%.*]], label [[TMP8:%.*]], !prof !0
+; CHECK: 7:
+; CHECK-NEXT: call void @__msan_warning_with_origin_noreturn(i32 [[TMP6]]) #1
+; CHECK-NEXT: unreachable
+; CHECK: 8:
+; CHECK-NEXT: ret i32 [[O]]
+;
+ %p = inttoptr i64 0 to i32 *
+ %o = load i32, i32 *%p
+ ret i32 %o
+}
+
+
+define void @NormalArg(i32 noundef %a) nounwind uwtable sanitize_memory {
+; CHECK-LABEL: @NormalArg(
+; CHECK-NEXT: [[P:%.*]] = inttoptr i64 0 to i32*
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint i32* [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = xor i64 [[TMP1]], 87960930222080
+; CHECK-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to i32*
+; CHECK-NEXT: [[TMP4:%.*]] = add i64 [[TMP2]], 17592186044416
+; CHECK-NEXT: [[TMP5:%.*]] = inttoptr i64 [[TMP4]] to i32*
+; CHECK-NEXT: store i32 0, i32* [[TMP3]], align 4
+; CHECK-NEXT: store i32 [[A:%.*]], i32* [[P]], align 4
+; CHECK-NEXT: ret void
+;
+ %p = inttoptr i64 0 to i32 *
+ store i32 %a, i32 *%p
+ ret void
+}
+
+define void @PartialArg(i32 %a) nounwind uwtable sanitize_memory {
+; CHECK-LABEL: @PartialArg(
+; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* bitcast ([100 x i64]* @__msan_param_tls to i32*), align 8
+; CHECK-NEXT: [[TMP2:%.*]] = load i32, i32* getelementptr inbounds ([200 x i32], [200 x i32]* @__msan_param_origin_tls, i32 0, i32 0), align 4
+; CHECK-NEXT: [[P:%.*]] = inttoptr i64 0 to i32*
+; CHECK-NEXT: [[TMP3:%.*]] = ptrtoint i32* [[P]] to i64
+; CHECK-NEXT: [[TMP4:%.*]] = xor i64 [[TMP3]], 87960930222080
+; CHECK-NEXT: [[TMP5:%.*]] = inttoptr i64 [[TMP4]] to i32*
+; CHECK-NEXT: [[TMP6:%.*]] = add i64 [[TMP4]], 17592186044416
+; CHECK-NEXT: [[TMP7:%.*]] = inttoptr i64 [[TMP6]] to i32*
+; CHECK-NEXT: store i32 [[TMP1]], i32* [[TMP5]], align 4
+; CHECK-NEXT: [[_MSCMP:%.*]] = icmp ne i32 [[TMP1]], 0
+; CHECK-NEXT: br i1 [[_MSCMP]], label [[TMP8:%.*]], label [[TMP9:%.*]], !prof !0
+; CHECK: 8:
+; CHECK-NEXT: store i32 [[TMP2]], i32* [[TMP7]], align 4
+; CHECK-NEXT: br label [[TMP9]]
+; CHECK: 9:
+; CHECK-NEXT: store i32 [[A:%.*]], i32* [[P]], align 4
+; CHECK-NEXT: ret void
+;
+ %p = inttoptr i64 0 to i32 *
+ store i32 %a, i32 *%p
+ ret void
+}
+
+define void @CallNormal() nounwind uwtable sanitize_memory {
+; CHECK-LABEL: @CallNormal(
+; CHECK-NEXT: [[R:%.*]] = call i32 @NormalRet() #0
+; CHECK-NEXT: call void @NormalArg(i32 [[R]]) #0
+; CHECK-NEXT: ret void
+;
+ %r = call i32 @NormalRet() nounwind uwtable sanitize_memory
+ call void @NormalArg(i32 %r) nounwind uwtable sanitize_memory
+ ret void
+}
+
+define void @CallWithLoaded() nounwind uwtable sanitize_memory {
+; CHECK-LABEL: @CallWithLoaded(
+; CHECK-NEXT: [[P:%.*]] = inttoptr i64 0 to i32*
+; CHECK-NEXT: [[O:%.*]] = load i32, i32* [[P]], align 4
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint i32* [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = xor i64 [[TMP1]], 87960930222080
+; CHECK-NEXT: [[TMP3:%.*]] = inttoptr i64 [[TMP2]] to i32*
+; CHECK-NEXT: [[TMP4:%.*]] = add i64 [[TMP2]], 17592186044416
+; CHECK-NEXT: [[TMP5:%.*]] = inttoptr i64 [[TMP4]] to i32*
+; CHECK-NEXT: [[_MSLD:%.*]] = load i32, i32* [[TMP3]], align 4
+; CHECK-NEXT: [[TMP6:%.*]] = load i32, i32* [[TMP5]], align 4
+; CHECK-NEXT: [[_MSCMP:%.*]] = icmp ne i32 [[_MSLD]], 0
+; CHECK-NEXT: br i1 [[_MSCMP]], label [[TMP7:%.*]], label [[TMP8:%.*]], !prof !0
+; CHECK: 7:
+; CHECK-NEXT: call void @__msan_warning_with_origin_noreturn(i32 [[TMP6]]) #1
+; CHECK-NEXT: unreachable
+; CHECK: 8:
+; CHECK-NEXT: call void @NormalArg(i32 [[O]]) #0
+; CHECK-NEXT: ret void
+;
+ %p = inttoptr i64 0 to i32 *
+ %o = load i32, i32 *%p
+ call void @NormalArg(i32 %o) nounwind uwtable sanitize_memory
+ ret void
+}
+
+define void @CallPartial() nounwind uwtable sanitize_memory {
+; CHECK-LABEL: @CallPartial(
+; CHECK-NEXT: store i32 0, i32* bitcast ([100 x i64]* @__msan_retval_tls to i32*), align 8
+; CHECK-NEXT: [[R:%.*]] = call i32 @PartialRet() #0
+; CHECK-NEXT: [[_MSRET:%.*]] = load i32, i32* bitcast ([100 x i64]* @__msan_retval_tls to i32*), align 8
+; CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* @__msan_retval_origin_tls, align 4
+; CHECK-NEXT: store i32 [[_MSRET]], i32* bitcast ([100 x i64]* @__msan_param_tls to i32*), align 8
+; CHECK-NEXT: store i32 [[TMP1]], i32* getelementptr inbounds ([200 x i32], [200 x i32]* @__msan_param_origin_tls, i32 0, i32 0), align 4
+; CHECK-NEXT: call void @PartialArg(i32 [[R]]) #0
+; CHECK-NEXT: ret void
+;
+ %r = call i32 @PartialRet() nounwind uwtable sanitize_memory
+ call void @PartialArg(i32 %r) nounwind uwtable sanitize_memory
+ ret void
+}