diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -1152,11 +1152,11 @@ const DataLayout &DL = F.getParent()->getDataLayout(); const Align OriginAlignment = std::max(kMinOriginAlignment, Alignment); unsigned StoreSize = DL.getTypeStoreSize(Shadow->getType()); - if (Shadow->getType()->isAggregateType()) { + if (Shadow->getType()->isArrayTy()) { paintOrigin(IRB, updateOrigin(Origin, IRB), OriginPtr, StoreSize, OriginAlignment); } else { - Value *ConvertedShadow = convertToShadowTyNoVec(Shadow, IRB); + Value *ConvertedShadow = convertShadowToScalar(Shadow, IRB); if (auto *ConstantShadow = dyn_cast(ConvertedShadow)) { if (ClCheckConstantShadow && !ConstantShadow->isZeroValue()) paintOrigin(IRB, updateOrigin(Origin, IRB), OriginPtr, StoreSize, @@ -1234,7 +1234,7 @@ bool AsCall) { IRBuilder<> IRB(OrigIns); LLVM_DEBUG(dbgs() << " SHAD0 : " << *Shadow << "\n"); - Value *ConvertedShadow = convertToShadowTyNoVec(Shadow, IRB); + Value *ConvertedShadow = convertShadowToScalar(Shadow, IRB); LLVM_DEBUG(dbgs() << " SHAD1 : " << *ConvertedShadow << "\n"); if (auto *ConstantShadow = dyn_cast(ConvertedShadow)) { @@ -1401,8 +1401,33 @@ return ty; } + /// Extract combined shadow of struct elements as a bool + Value *collapseStructShadow(StructType *Struct, Value *Shadow, + IRBuilder<> &IRB) { + Value *FalseVal = IRB.getIntN(/* width */ 1, /* value */ 0); + Value *Aggregator = FalseVal; + + for (unsigned Idx = 0; Idx < Struct->getNumElements(); Idx++) { + // Combine by ORing together each element's bool shadow + Value *ShadowItem = IRB.CreateExtractValue(Shadow, Idx); + Value *ShadowInner = convertShadowToScalar(ShadowItem, IRB); + Value *ShadowBool = + IRB.CreateICmpNE(ShadowInner, getCleanShadow(ShadowInner)); + + if (Aggregator != FalseVal) + Aggregator = IRB.CreateOr(Aggregator, ShadowBool); + else + Aggregator = ShadowBool; + } + + return Aggregator; + } + /// Convert a shadow value to it's flattened variant. - Value *convertToShadowTyNoVec(Value *V, IRBuilder<> &IRB) { + Value *convertShadowToScalar(Value *V, IRBuilder<> &IRB) { + if (StructType *Struct = dyn_cast(V->getType())) { + return collapseStructShadow(Struct, V, IRB); + } Type *Ty = V->getType(); Type *NoVecTy = getShadowTyNoVec(Ty); if (Ty == NoVecTy) return V; @@ -1729,8 +1754,10 @@ if (!InsertChecks) return; #ifndef NDEBUG Type *ShadowTy = Shadow->getType(); - assert((isa(ShadowTy) || isa(ShadowTy)) && - "Can only insert checks for integer and vector shadow types"); + assert( + (isa(ShadowTy) || isa(ShadowTy) || + isa(ShadowTy)) && + "Can only insert checks for integer, vector, and struct shadow types"); #endif InstrumentationList.push_back( ShadowOriginAndInsertPoint(Shadow, Origin, OrigIns)); @@ -2049,7 +2076,7 @@ Constant *ConstOrigin = dyn_cast(OpOrigin); // No point in adding something that might result in 0 origin value. if (!ConstOrigin || !ConstOrigin->isNullValue()) { - Value *FlatShadow = MSV->convertToShadowTyNoVec(OpShadow, IRB); + Value *FlatShadow = MSV->convertShadowToScalar(OpShadow, IRB); Value *Cond = IRB.CreateICmpNE(FlatShadow, MSV->getCleanShadow(FlatShadow)); Origin = IRB.CreateSelect(Cond, OpOrigin, Origin); diff --git a/llvm/test/Instrumentation/MemorySanitizer/check-struct.ll b/llvm/test/Instrumentation/MemorySanitizer/check-struct.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/MemorySanitizer/check-struct.ll @@ -0,0 +1,23 @@ +; RUN: opt < %s -msan-check-access-address=0 -msan-track-origins=1 -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" + +; CHECK-LABEL: @main +define { i32, i8 } @main() sanitize_memory { +; CHECK: %p = inttoptr i64 0 to { i32, i8 }* + %p = inttoptr i64 0 to { i32, i8 } * +; CHECK: %o = load { i32, i8 }, { i32, i8 }* %p + %o = load { i32, i8 }, { i32, i8 } *%p +; CHECK: [[FIELD0:%.+]] = extractvalue { i32, i8 } %_msld, 0 +; CHECK: [[F0_POISONED:%.+]] = icmp ne i32 [[FIELD0]] +; CHECK: [[FIELD1:%.+]] = extractvalue { i32, i8 } %_msld, 1 +; CHECK: [[F1_POISONED:%.+]] = icmp ne i8 [[FIELD1]] +; CHECK: [[F1_OR:%.+]] = or i1 [[F0_POISONED]], [[F1_POISONED]] +; CHECK: %_mscmp = icmp ne i1 [[F1_OR]], false +; CHECK: br i1 %_mscmp +; CHECK: call void @__msan_warning +; CHECK: ret { i32, i8 } %o + ret { i32, i8 } %o +}