[MSan] Add KMSAN instrumentation to MSan pass

Introduce the -msan-kernel flag, which enables the kernel instrumentation.

The main differences between KMSAN and MSan instrumentations are:

- KMSAN implies msan-track-origins=2, msan-keep-going=true;
- there're no explicit accesses to shadow and origin memory.
  Shadow and origin values for a particular X-byte memory location are
  read and written via pointers returned by
  __msan_metadata_ptr_for_load_X(u8 *addr) and
  __msan_store_shadow_origin_X(u8 *addr, uptr shadow, uptr origin);
- TLS variables are stored in a single struct in per-task storage. A call
  to a function returning that struct is inserted into every instrumented
  function before the entry block;
- __msan_warning() takes a 32-bit origin parameter;
- local variables are poisoned with __msan_poison_alloca() upon function
  entry and unpoisoned with __msan_unpoison_alloca() before leaving the
  function;
- the pass doesn't declare any global variables or add global constructors
  to the translation unit.

llvm-svn: 341637
This commit is contained in:
Alexander Potapenko 2018-09-07 09:10:30 +00:00
parent a67a0ed849
commit d0446d3d9b
5 changed files with 757 additions and 85 deletions

View File

@ -134,7 +134,8 @@ ModulePass *createAddressSanitizerModulePass(bool CompileKernel = false,
// Insert MemorySanitizer instrumentation (detection of uninitialized reads)
FunctionPass *createMemorySanitizerPass(int TrackOrigins = 0,
bool Recover = false);
bool Recover = false,
bool EnableKmsan = false);
FunctionPass *createHWAddressSanitizerPass(bool CompileKernel = false,
bool Recover = false);

View File

@ -89,7 +89,38 @@
/// implementation ignores the load aspect of CAS/RMW, always returning a clean
/// value. It implements the store part as a simple atomic store by storing a
/// clean shadow.
//
///
/// KernelMemorySanitizer (KMSAN) implementation.
///
/// The major differences between KMSAN and MSan instrumentation are:
/// - KMSAN always tracks the origins and implies msan-keep-going=true;
/// - KMSAN allocates shadow and origin memory for each page separately, so
/// there are no explicit accesses to shadow and origin in the
/// instrumentation.
/// Shadow and origin values for a particular X-byte memory location
/// (X=1,2,4,8) are accessed through pointers obtained via the
/// __msan_metadata_ptr_for_load_X(ptr)
/// __msan_metadata_ptr_for_store_X(ptr)
/// functions. The corresponding functions check that the X-byte accesses
/// are possible and returns the pointers to shadow and origin memory.
/// Arbitrary sized accesses are handled with:
/// __msan_metadata_ptr_for_load_n(ptr, size)
/// __msan_metadata_ptr_for_store_n(ptr, size);
/// - TLS variables are stored in a single per-task struct. A call to a
/// function __msan_get_context_state() returning a pointer to that struct
/// is inserted into every instrumented function before the entry block;
/// - __msan_warning() takes a 32-bit origin parameter;
/// - local variables are poisoned with __msan_poison_alloca() upon function
/// entry and unpoisoned with __msan_unpoison_alloca() before leaving the
/// function;
/// - the pass doesn't declare any global variables or add global constructors
/// to the translation unit.
///
/// Also, KMSAN currently ignores uninitialized memory passed into inline asm
/// calls, making sure we're on the safe side wrt. possible false positives.
///
/// KernelMemorySanitizer only supports X86_64 at the moment.
///
//===----------------------------------------------------------------------===//
#include "llvm/ADT/APInt.h"
@ -233,6 +264,11 @@ static cl::opt<int> ClInstrumentationWithCallThreshold(
"inline checks (-1 means never use callbacks)."),
cl::Hidden, cl::init(3500));
static cl::opt<bool>
ClEnableKmsan("msan-kernel",
cl::desc("Enable KernelMemorySanitizer instrumentation"),
cl::Hidden, cl::init(false));
// This is an experiment to enable handling of cases where shadow is a non-zero
// compile-time constant. For some unexplainable reason they were silently
// ignored in the instrumentation.
@ -400,11 +436,19 @@ public:
// Pass identification, replacement for typeid.
static char ID;
MemorySanitizer(int TrackOrigins = 0, bool Recover = false)
: FunctionPass(ID),
TrackOrigins(std::max(TrackOrigins, (int)ClTrackOrigins)),
Recover(Recover || ClKeepGoing) {}
MemorySanitizer(int TrackOrigins = 0, bool Recover = false,
bool EnableKmsan = false)
: FunctionPass(ID) {
this->CompileKernel =
ClEnableKmsan.getNumOccurrences() > 0 ? ClEnableKmsan : EnableKmsan;
if (ClTrackOrigins.getNumOccurrences() > 0)
this->TrackOrigins = ClTrackOrigins;
else
this->TrackOrigins = this->CompileKernel ? 2 : TrackOrigins;
this->Recover = ClKeepGoing.getNumOccurrences() > 0
? ClKeepGoing
: (this->CompileKernel | Recover);
}
StringRef getPassName() const override { return "MemorySanitizer"; }
void getAnalysisUsage(AnalysisUsage &AU) const override {
@ -422,8 +466,12 @@ private:
friend struct VarArgPowerPC64Helper;
void initializeCallbacks(Module &M);
void createKernelApi(Module &M);
void createUserspaceApi(Module &M);
/// True if we're compiling the Linux kernel.
bool CompileKernel;
/// Track origins (allocation points) of uninitialized values.
int TrackOrigins;
bool Recover;
@ -432,33 +480,39 @@ private:
Type *IntptrTy;
Type *OriginTy;
// XxxTLS variables represent the per-thread state in MSan and per-task state
// in KMSAN.
// For the userspace these point to thread-local globals. In the kernel land
// they point to the members of a per-task struct obtained via a call to
// __msan_get_context_state().
/// Thread-local shadow storage for function parameters.
GlobalVariable *ParamTLS;
Value *ParamTLS;
/// Thread-local origin storage for function parameters.
GlobalVariable *ParamOriginTLS;
Value *ParamOriginTLS;
/// Thread-local shadow storage for function return value.
GlobalVariable *RetvalTLS;
Value *RetvalTLS;
/// Thread-local origin storage for function return value.
GlobalVariable *RetvalOriginTLS;
Value *RetvalOriginTLS;
/// Thread-local shadow storage for in-register va_arg function
/// parameters (x86_64-specific).
GlobalVariable *VAArgTLS;
Value *VAArgTLS;
/// Thread-local shadow storage for in-register va_arg function
/// parameters (x86_64-specific).
GlobalVariable *VAArgOriginTLS;
Value *VAArgOriginTLS;
/// Thread-local shadow storage for va_arg overflow area
/// (x86_64-specific).
GlobalVariable *VAArgOverflowSizeTLS;
Value *VAArgOverflowSizeTLS;
/// Thread-local space used to pass origin value to the UMR reporting
/// function.
GlobalVariable *OriginTLS;
Value *OriginTLS;
/// Are the instrumentation callbacks set up?
bool CallbacksInitialized = false;
@ -484,6 +538,21 @@ private:
/// MSan runtime replacements for memmove, memcpy and memset.
Value *MemmoveFn, *MemcpyFn, *MemsetFn;
/// KMSAN callback for task-local function argument shadow.
Value *MsanGetContextStateFn;
/// Functions for poisoning/unpoisoning local variables
Value *MsanPoisonAllocaFn, *MsanUnpoisonAllocaFn;
/// Each of the MsanMetadataPtrXxx functions returns a pair of shadow/origin
/// pointers.
Value *MsanMetadataPtrForLoadN, *MsanMetadataPtrForStoreN;
Value *MsanMetadataPtrForLoad_1_8[4];
Value *MsanMetadataPtrForStore_1_8[4];
/// Helper to choose between different MsanMetadataPtrXxx().
Value *getKmsanShadowOriginAccessFn(bool isStore, int size);
/// Memory map parameters used in application-to-shadow calculation.
const MemoryMapParams *MapParams;
@ -514,8 +583,9 @@ INITIALIZE_PASS_END(
MemorySanitizer, "msan",
"MemorySanitizer: detects uninitialized reads.", false, false)
FunctionPass *llvm::createMemorySanitizerPass(int TrackOrigins, bool Recover) {
return new MemorySanitizer(TrackOrigins, Recover);
FunctionPass *llvm::createMemorySanitizerPass(int TrackOrigins, bool Recover,
bool CompileKernel) {
return new MemorySanitizer(TrackOrigins, Recover, CompileKernel);
}
/// Create a non-const global initialized with the given string.
@ -530,6 +600,68 @@ static GlobalVariable *createPrivateNonConstGlobalForString(Module &M,
GlobalValue::PrivateLinkage, StrConst, "");
}
/// Create KMSAN API callbacks.
void MemorySanitizer::createKernelApi(Module &M) {
IRBuilder<> IRB(*C);
// These will be initialized in insertKmsanPrologue().
RetvalTLS = nullptr;
RetvalOriginTLS = nullptr;
ParamTLS = nullptr;
ParamOriginTLS = nullptr;
VAArgTLS = nullptr;
VAArgOriginTLS = nullptr;
VAArgOverflowSizeTLS = nullptr;
// OriginTLS is unused in the kernel.
OriginTLS = nullptr;
// __msan_warning() in the kernel takes an origin.
WarningFn = M.getOrInsertFunction("__msan_warning", IRB.getVoidTy(),
IRB.getInt32Ty());
// Requests the per-task context state (kmsan_context_state*) from the
// runtime library.
MsanGetContextStateFn = M.getOrInsertFunction(
"__msan_get_context_state",
PointerType::get(
StructType::get(ArrayType::get(IRB.getInt64Ty(), kParamTLSSize / 8),
ArrayType::get(IRB.getInt64Ty(), kRetvalTLSSize / 8),
ArrayType::get(IRB.getInt64Ty(), kParamTLSSize / 8),
ArrayType::get(IRB.getInt64Ty(),
kParamTLSSize / 8), /* va_arg_origin */
IRB.getInt64Ty(),
ArrayType::get(OriginTy, kParamTLSSize / 4), OriginTy,
OriginTy),
0));
Type *RetTy = StructType::get(PointerType::get(IRB.getInt8Ty(), 0),
PointerType::get(IRB.getInt32Ty(), 0));
for (int ind = 0, size = 1; ind < 4; ind++, size <<= 1) {
std::string name_load =
"__msan_metadata_ptr_for_load_" + std::to_string(size);
std::string name_store =
"__msan_metadata_ptr_for_store_" + std::to_string(size);
MsanMetadataPtrForLoad_1_8[ind] = M.getOrInsertFunction(
name_load, RetTy, PointerType::get(IRB.getInt8Ty(), 0));
MsanMetadataPtrForStore_1_8[ind] = M.getOrInsertFunction(
name_store, RetTy, PointerType::get(IRB.getInt8Ty(), 0));
}
MsanMetadataPtrForLoadN = M.getOrInsertFunction(
"__msan_metadata_ptr_for_load_n", RetTy,
PointerType::get(IRB.getInt8Ty(), 0), IRB.getInt64Ty());
MsanMetadataPtrForStoreN = M.getOrInsertFunction(
"__msan_metadata_ptr_for_store_n", RetTy,
PointerType::get(IRB.getInt8Ty(), 0), IRB.getInt64Ty());
// Functions for poisoning and unpoisoning memory.
MsanPoisonAllocaFn =
M.getOrInsertFunction("__msan_poison_alloca", IRB.getVoidTy(),
IRB.getInt8PtrTy(), IntptrTy, IRB.getInt8PtrTy());
MsanUnpoisonAllocaFn = M.getOrInsertFunction(
"__msan_unpoison_alloca", IRB.getVoidTy(), IRB.getInt8PtrTy(), IntptrTy);
}
/// Insert declarations for userspace-specific functions and globals.
void MemorySanitizer::createUserspaceApi(Module &M) {
IRBuilder<> IRB(*C);
@ -625,10 +757,31 @@ void MemorySanitizer::initializeCallbacks(Module &M) {
StringRef(""), StringRef(""),
/*hasSideEffects=*/true);
createUserspaceApi(M);
if (CompileKernel) {
createKernelApi(M);
} else {
createUserspaceApi(M);
}
CallbacksInitialized = true;
}
Value *MemorySanitizer::getKmsanShadowOriginAccessFn(bool isStore, int size) {
Value **Fns =
isStore ? MsanMetadataPtrForStore_1_8 : MsanMetadataPtrForLoad_1_8;
switch (size) {
case 1:
return Fns[0];
case 2:
return Fns[1];
case 4:
return Fns[2];
case 8:
return Fns[3];
default:
return nullptr;
}
}
/// Module-level initialization.
///
/// inserts a call to __msan_init to the module's constructor list.
@ -705,27 +858,28 @@ bool MemorySanitizer::doInitialization(Module &M) {
ColdCallWeights = MDBuilder(*C).createBranchWeights(1, 1000);
OriginStoreWeights = MDBuilder(*C).createBranchWeights(1, 1000);
std::tie(MsanCtorFunction, std::ignore) =
createSanitizerCtorAndInitFunctions(M, kMsanModuleCtorName, kMsanInitName,
/*InitArgTypes=*/{},
/*InitArgs=*/{});
if (ClWithComdat) {
Comdat *MsanCtorComdat = M.getOrInsertComdat(kMsanModuleCtorName);
MsanCtorFunction->setComdat(MsanCtorComdat);
appendToGlobalCtors(M, MsanCtorFunction, 0, MsanCtorFunction);
} else {
appendToGlobalCtors(M, MsanCtorFunction, 0);
if (!CompileKernel) {
std::tie(MsanCtorFunction, std::ignore) =
createSanitizerCtorAndInitFunctions(M, kMsanModuleCtorName,
kMsanInitName,
/*InitArgTypes=*/{},
/*InitArgs=*/{});
if (ClWithComdat) {
Comdat *MsanCtorComdat = M.getOrInsertComdat(kMsanModuleCtorName);
MsanCtorFunction->setComdat(MsanCtorComdat);
appendToGlobalCtors(M, MsanCtorFunction, 0, MsanCtorFunction);
} else {
appendToGlobalCtors(M, MsanCtorFunction, 0);
}
if (TrackOrigins)
new GlobalVariable(M, IRB.getInt32Ty(), true, GlobalValue::WeakODRLinkage,
IRB.getInt32(TrackOrigins), "__msan_track_origins");
if (Recover)
new GlobalVariable(M, IRB.getInt32Ty(), true, GlobalValue::WeakODRLinkage,
IRB.getInt32(Recover), "__msan_keep_going");
}
if (TrackOrigins)
new GlobalVariable(M, IRB.getInt32Ty(), true, GlobalValue::WeakODRLinkage,
IRB.getInt32(TrackOrigins), "__msan_track_origins");
if (Recover)
new GlobalVariable(M, IRB.getInt32Ty(), true, GlobalValue::WeakODRLinkage,
IRB.getInt32(Recover), "__msan_keep_going");
return true;
}
@ -819,7 +973,10 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
TLI = &MS.getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
MS.initializeCallbacks(*F.getParent());
ActualFnStart = &F.getEntryBlock();
if (MS.CompileKernel)
ActualFnStart = insertKmsanPrologue(F);
else
ActualFnStart = &F.getEntryBlock();
LLVM_DEBUG(if (!InsertChecks) dbgs()
<< "MemorySanitizer is not inserting checks into '"
@ -893,7 +1050,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
unsigned TypeSizeInBits =
DL.getTypeSizeInBits(ConvertedShadow->getType());
unsigned SizeIndex = TypeSizeToSizeIndex(TypeSizeInBits);
if (AsCall && SizeIndex < kNumberOfAccessSizes) {
if (AsCall && SizeIndex < kNumberOfAccessSizes && !MS.CompileKernel) {
Value *Fn = MS.MaybeStoreOriginFn[SizeIndex];
Value *ConvertedShadow2 = IRB.CreateZExt(
ConvertedShadow, IRB.getIntNTy(8 * (1 << SizeIndex)));
@ -942,10 +1099,14 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
void insertWarningFn(IRBuilder<> &IRB, Value *Origin) {
if (!Origin)
Origin = (Value *)IRB.getInt32(0);
if (MS.TrackOrigins) {
IRB.CreateStore(Origin, MS.OriginTLS);
if (MS.CompileKernel) {
IRB.CreateCall(MS.WarningFn, Origin);
} else {
if (MS.TrackOrigins) {
IRB.CreateStore(Origin, MS.OriginTLS);
}
IRB.CreateCall(MS.WarningFn, {});
}
IRB.CreateCall(MS.WarningFn, {});
IRB.CreateCall(MS.EmptyAsm, {});
// FIXME: Insert UnreachableInst if !MS.Recover?
// This may invalidate some of the following checks and needs to be done
@ -971,7 +1132,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
unsigned TypeSizeInBits = DL.getTypeSizeInBits(ConvertedShadow->getType());
unsigned SizeIndex = TypeSizeToSizeIndex(TypeSizeInBits);
if (AsCall && SizeIndex < kNumberOfAccessSizes) {
if (AsCall && SizeIndex < kNumberOfAccessSizes && !MS.CompileKernel) {
Value *Fn = MS.MaybeWarningFn[SizeIndex];
Value *ConvertedShadow2 =
IRB.CreateZExt(ConvertedShadow, IRB.getIntNTy(8 * (1 << SizeIndex)));
@ -1001,6 +1162,29 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
LLVM_DEBUG(dbgs() << "DONE:\n" << F);
}
BasicBlock *insertKmsanPrologue(Function &F) {
BasicBlock *ret =
SplitBlock(&F.getEntryBlock(), F.getEntryBlock().getFirstNonPHI());
IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
Value *ContextState = IRB.CreateCall(MS.MsanGetContextStateFn, {});
Constant *Zero = IRB.getInt32(0);
MS.ParamTLS =
IRB.CreateGEP(ContextState, {Zero, IRB.getInt32(0)}, "param_shadow");
MS.RetvalTLS =
IRB.CreateGEP(ContextState, {Zero, IRB.getInt32(1)}, "retval_shadow");
MS.VAArgTLS =
IRB.CreateGEP(ContextState, {Zero, IRB.getInt32(2)}, "va_arg_shadow");
MS.VAArgOriginTLS =
IRB.CreateGEP(ContextState, {Zero, IRB.getInt32(3)}, "va_arg_origin");
MS.VAArgOverflowSizeTLS = IRB.CreateGEP(
ContextState, {Zero, IRB.getInt32(4)}, "va_arg_overflow_size");
MS.ParamOriginTLS =
IRB.CreateGEP(ContextState, {Zero, IRB.getInt32(5)}, "param_origin");
MS.RetvalOriginTLS =
IRB.CreateGEP(ContextState, {Zero, IRB.getInt32(6)}, "retval_origin");
return ret;
}
/// Add MemorySanitizer instrumentation to a function.
bool runOnFunction() {
// In the presence of unreachable blocks, we may see Phi nodes with
@ -1149,12 +1333,40 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
return std::make_pair(ShadowPtr, OriginPtr);
}
std::pair<Value *, Value *>
getShadowOriginPtrKernel(Value *Addr, IRBuilder<> &IRB, Type *ShadowTy,
unsigned Alignment, bool isStore) {
Value *ShadowOriginPtrs;
const DataLayout &DL = F.getParent()->getDataLayout();
int Size = DL.getTypeStoreSize(ShadowTy);
Value *Getter = MS.getKmsanShadowOriginAccessFn(isStore, Size);
Value *AddrCast =
IRB.CreatePointerCast(Addr, PointerType::get(IRB.getInt8Ty(), 0));
if (Getter) {
ShadowOriginPtrs = IRB.CreateCall(Getter, AddrCast);
} else {
Value *SizeVal = ConstantInt::get(MS.IntptrTy, Size);
ShadowOriginPtrs = IRB.CreateCall(isStore ? MS.MsanMetadataPtrForStoreN
: MS.MsanMetadataPtrForLoadN,
{AddrCast, SizeVal});
}
Value *ShadowPtr = IRB.CreateExtractValue(ShadowOriginPtrs, 0);
ShadowPtr = IRB.CreatePointerCast(ShadowPtr, PointerType::get(ShadowTy, 0));
Value *OriginPtr = IRB.CreateExtractValue(ShadowOriginPtrs, 1);
return std::make_pair(ShadowPtr, OriginPtr);
}
std::pair<Value *, Value *> getShadowOriginPtr(Value *Addr, IRBuilder<> &IRB,
Type *ShadowTy,
unsigned Alignment,
bool isStore) {
std::pair<Value *, Value *> ret =
getShadowOriginPtrUserspace(Addr, IRB, ShadowTy, Alignment);
std::pair<Value *, Value *> ret;
if (MS.CompileKernel)
ret = getShadowOriginPtrKernel(Addr, IRB, ShadowTy, Alignment, isStore);
else
ret = getShadowOriginPtrUserspace(Addr, IRB, ShadowTy, Alignment);
return ret;
}
@ -1173,7 +1385,8 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
/// Compute the origin address for a given function argument.
Value *getOriginPtrForArgument(Value *A, IRBuilder<> &IRB,
int ArgOffset) {
if (!MS.TrackOrigins) return nullptr;
if (!MS.TrackOrigins)
return nullptr;
Value *Base = IRB.CreatePointerCast(MS.ParamOriginTLS, MS.IntptrTy);
if (ArgOffset)
Base = IRB.CreateAdd(Base, ConstantInt::get(MS.IntptrTy, ArgOffset));
@ -1313,6 +1526,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
getShadowOriginPtr(V, EntryIRB, EntryIRB.getInt8Ty(), ArgAlign,
/*isStore*/ true)
.first;
// TODO(glider): need to copy origins.
if (Overflow) {
// ParamTLS overflow.
EntryIRB.CreateMemSet(
@ -2931,12 +3145,14 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
if (ArgOffset + Size > kParamTLSSize) break;
unsigned ParamAlignment = CS.getParamAlignment(i);
unsigned Alignment = std::min(ParamAlignment, kShadowTLSAlignment);
Value *AShadowPtr = getShadowOriginPtr(A, IRB, IRB.getInt8Ty(),
Alignment, /*isStore*/ false)
.first;
Value *AShadowPtr =
getShadowOriginPtr(A, IRB, IRB.getInt8Ty(), Alignment,
/*isStore*/ false)
.first;
Store = IRB.CreateMemCpy(ArgShadowBase, Alignment, AShadowPtr,
Alignment, Size);
// TODO(glider): need to copy origins.
} else {
Size = DL.getTypeAllocSize(A->getType());
if (ArgOffset + Size > kParamTLSSize) break;
@ -3043,6 +3259,53 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
"_msphi_o"));
}
Value *getLocalVarDescription(AllocaInst &I) {
SmallString<2048> StackDescriptionStorage;
raw_svector_ostream StackDescription(StackDescriptionStorage);
// We create a string with a description of the stack allocation and
// pass it into __msan_set_alloca_origin.
// It will be printed by the run-time if stack-originated UMR is found.
// The first 4 bytes of the string are set to '----' and will be replaced
// by __msan_va_arg_overflow_size_tls at the first call.
StackDescription << "----" << I.getName() << "@" << F.getName();
return createPrivateNonConstGlobalForString(*F.getParent(),
StackDescription.str());
}
void instrumentAllocaUserspace(AllocaInst &I, IRBuilder<> &IRB, Value *Len) {
if (PoisonStack && ClPoisonStackWithCall) {
IRB.CreateCall(MS.MsanPoisonStackFn,
{IRB.CreatePointerCast(&I, IRB.getInt8PtrTy()), Len});
} else {
Value *ShadowBase, *OriginBase;
std::tie(ShadowBase, OriginBase) =
getShadowOriginPtr(&I, IRB, IRB.getInt8Ty(), 1, /*isStore*/ true);
Value *PoisonValue = IRB.getInt8(PoisonStack ? ClPoisonStackPattern : 0);
IRB.CreateMemSet(ShadowBase, PoisonValue, Len, I.getAlignment());
}
if (PoisonStack && MS.TrackOrigins) {
Value *Descr = getLocalVarDescription(I);
IRB.CreateCall(MS.MsanSetAllocaOrigin4Fn,
{IRB.CreatePointerCast(&I, IRB.getInt8PtrTy()), Len,
IRB.CreatePointerCast(Descr, IRB.getInt8PtrTy()),
IRB.CreatePointerCast(&F, MS.IntptrTy)});
}
}
void instrumentAllocaKmsan(AllocaInst &I, IRBuilder<> &IRB, Value *Len) {
Value *Descr = getLocalVarDescription(I);
if (PoisonStack) {
IRB.CreateCall(MS.MsanPoisonAllocaFn,
{IRB.CreatePointerCast(&I, IRB.getInt8PtrTy()), Len,
IRB.CreatePointerCast(Descr, IRB.getInt8PtrTy())});
} else {
IRB.CreateCall(MS.MsanUnpoisonAllocaFn,
{IRB.CreatePointerCast(&I, IRB.getInt8PtrTy()), Len});
}
}
void visitAllocaInst(AllocaInst &I) {
setShadow(&I, getCleanShadow(&I));
setOrigin(&I, getCleanOrigin());
@ -3052,36 +3315,11 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
Value *Len = ConstantInt::get(MS.IntptrTy, TypeSize);
if (I.isArrayAllocation())
Len = IRB.CreateMul(Len, I.getArraySize());
if (PoisonStack && ClPoisonStackWithCall) {
IRB.CreateCall(MS.MsanPoisonStackFn,
{IRB.CreatePointerCast(&I, IRB.getInt8PtrTy()), Len});
} else {
Value *ShadowBase = getShadowOriginPtr(&I, IRB, IRB.getInt8Ty(),
I.getAlignment(), /*isStore*/ true)
.first;
Value *PoisonValue = IRB.getInt8(PoisonStack ? ClPoisonStackPattern : 0);
IRB.CreateMemSet(ShadowBase, PoisonValue, Len, I.getAlignment());
}
if (PoisonStack && MS.TrackOrigins) {
SmallString<2048> StackDescriptionStorage;
raw_svector_ostream StackDescription(StackDescriptionStorage);
// We create a string with a description of the stack allocation and
// pass it into __msan_set_alloca_origin.
// It will be printed by the run-time if stack-originated UMR is found.
// The first 4 bytes of the string are set to '----' and will be replaced
// by __msan_va_arg_overflow_size_tls at the first call.
StackDescription << "----" << I.getName() << "@" << F.getName();
Value *Descr =
createPrivateNonConstGlobalForString(*F.getParent(),
StackDescription.str());
IRB.CreateCall(MS.MsanSetAllocaOrigin4Fn,
{IRB.CreatePointerCast(&I, IRB.getInt8PtrTy()), Len,
IRB.CreatePointerCast(Descr, IRB.getInt8PtrTy()),
IRB.CreatePointerCast(&F, MS.IntptrTy)});
}
if (MS.CompileKernel)
instrumentAllocaKmsan(I, IRB, Len);
else
instrumentAllocaUserspace(I, IRB, Len);
}
void visitSelectInst(SelectInst& I) {

View File

@ -2,6 +2,7 @@
; RUN: opt < %s -msan -msan-check-access-address=0 -msan-poison-stack-with-call=1 -S | FileCheck %s --check-prefixes=CHECK,CALL
; RUN: opt < %s -msan -msan-check-access-address=0 -msan-track-origins=1 -S | FileCheck %s --check-prefixes=CHECK,ORIGIN
; RUN: opt < %s -msan -msan-check-access-address=0 -msan-track-origins=2 -S | FileCheck %s --check-prefixes=CHECK,ORIGIN
; RUN: opt < %s -msan -msan-kernel=1 -S | FileCheck %s --check-prefixes=CHECK,KMSAN
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"
@ -16,6 +17,7 @@ entry:
; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
; CHECK: ret void
@ -31,6 +33,7 @@ l:
; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
; CHECK: ret void
define void @array() sanitize_memory {
@ -43,6 +46,7 @@ entry:
; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 20, i1 false)
; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 20)
; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 20,
; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 20,
; CHECK: ret void
define void @array_non_const(i64 %cnt) sanitize_memory {
@ -56,4 +60,20 @@ entry:
; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 %[[A]], i1 false)
; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 %[[A]])
; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 %[[A]],
; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 %[[A]],
; CHECK: ret void
; Check that the local is unpoisoned in the absence of sanitize_memory
define void @unpoison_local() {
entry:
%x = alloca i32, i64 5, align 4
ret void
}
; CHECK-LABEL: define void @unpoison_local(
; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 0, i64 20, i1 false)
; CALL: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 0, i64 20, i1 false)
; ORIGIN-NOT: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 20,
; KMSAN: call void @__msan_unpoison_alloca(i8* {{.*}}, i64 20)
; CHECK: ret void

View File

@ -0,0 +1,404 @@
; KMSAN instrumentation tests
; RUN: opt < %s -msan -msan-kernel=1 -S | FileCheck %s -check-prefixes=CHECK
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 the instrumentation prologue.
define void @Empty() nounwind uwtable sanitize_memory {
entry:
ret void
}
; CHECK-LABEL: @Empty
; CHECK: entry:
; CHECK: @__msan_get_context_state()
; %param_shadow:
; CHECK: getelementptr {{.*}} i32 0, i32 0
; %retval_shadow:
; CHECK: getelementptr {{.*}} i32 0, i32 1
; %va_arg_shadow:
; CHECK: getelementptr {{.*}} i32 0, i32 2
; %va_arg_origin:
; CHECK: getelementptr {{.*}} i32 0, i32 3
; %va_arg_overflow_size:
; CHECK: getelementptr {{.*}} i32 0, i32 4
; %param_origin:
; CHECK: getelementptr {{.*}} i32 0, i32 5
; %retval_origin:
; CHECK: getelementptr {{.*}} i32 0, i32 6
; CHECK: entry.split:
; Check instrumentation of stores
define void @Store1(i8* nocapture %p, i8 %x) nounwind uwtable sanitize_memory {
entry:
store i8 %x, i8* %p
ret void
}
; CHECK-LABEL: @Store1
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: [[BASE2:%[0-9]+]] = ptrtoint {{.*}} [[PARAM_SHADOW]]
; CHECK: [[BASE:%[0-9]+]] = ptrtoint {{.*}} [[PARAM_SHADOW]]
; CHECK: [[SHADOW:%[a-z0-9_]+]] = inttoptr {{.*}} [[BASE]]
; Load the shadow of %p and check it
; CHECK: load i64, i64* [[SHADOW]]
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; CHECK: @__msan_metadata_ptr_for_store_1(i8* %p)
; CHECK: store i8
; If the new shadow is non-zero, jump to __msan_chain_origin()
; CHECK: icmp
; CHECK: br i1
; CHECK: <label>
; CHECK: @__msan_chain_origin
; Storing origin here:
; CHECK: store i32
; CHECK: br label
; CHECK: <label>
; CHECK: store i8
; CHECK: ret void
define void @Store2(i16* nocapture %p, i16 %x) nounwind uwtable sanitize_memory {
entry:
store i16 %x, i16* %p
ret void
}
; CHECK-LABEL: @Store2
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i64
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; CHECK: [[REG:%[0-9]+]] = bitcast i16* %p to i8*
; CHECK: @__msan_metadata_ptr_for_store_2(i8* [[REG]])
; CHECK: store i16
; If the new shadow is non-zero, jump to __msan_chain_origin()
; CHECK: icmp
; CHECK: br i1
; CHECK: <label>
; CHECK: @__msan_chain_origin
; Storing origin here:
; CHECK: store i32
; CHECK: br label
; CHECK: <label>
; CHECK: store i16
; CHECK: ret void
define void @Store4(i32* nocapture %p, i32 %x) nounwind uwtable sanitize_memory {
entry:
store i32 %x, i32* %p
ret void
}
; CHECK-LABEL: @Store4
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i32
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; CHECK: [[REG:%[0-9]+]] = bitcast i32* %p to i8*
; CHECK: @__msan_metadata_ptr_for_store_4(i8* [[REG]])
; CHECK: store i32
; If the new shadow is non-zero, jump to __msan_chain_origin()
; CHECK: icmp
; CHECK: br i1
; CHECK: <label>
; CHECK: @__msan_chain_origin
; Storing origin here:
; CHECK: store i32
; CHECK: br label
; CHECK: <label>
; CHECK: store i32
; CHECK: ret void
define void @Store8(i64* nocapture %p, i64 %x) nounwind uwtable sanitize_memory {
entry:
store i64 %x, i64* %p
ret void
}
; CHECK-LABEL: @Store8
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i64
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; CHECK: [[REG:%[0-9]+]] = bitcast i64* %p to i8*
; CHECK: @__msan_metadata_ptr_for_store_8(i8* [[REG]])
; CHECK: store i64
; If the new shadow is non-zero, jump to __msan_chain_origin()
; CHECK: icmp
; CHECK: br i1
; CHECK: <label>
; CHECK: @__msan_chain_origin
; Storing origin here:
; CHECK: store i32
; CHECK: br label
; CHECK: <label>
; CHECK: store i64
; CHECK: ret void
define void @Store16(i128* nocapture %p, i128 %x) nounwind uwtable sanitize_memory {
entry:
store i128 %x, i128* %p
ret void
}
; CHECK-LABEL: @Store16
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i64
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; CHECK: [[REG:%[0-9]+]] = bitcast i128* %p to i8*
; CHECK: @__msan_metadata_ptr_for_store_n(i8* [[REG]], i64 16)
; CHECK: store i128
; If the new shadow is non-zero, jump to __msan_chain_origin()
; CHECK: icmp
; CHECK: br i1
; CHECK: <label>
; CHECK: @__msan_chain_origin
; Storing origin here:
; CHECK: store i32
; CHECK: br label
; CHECK: <label>
; CHECK: store i128
; CHECK: ret void
; Check instrumentation of loads
define i8 @Load1(i8* nocapture %p) nounwind uwtable sanitize_memory {
entry:
%0 = load i8, i8* %p
ret i8 %0
}
; CHECK-LABEL: @Load1
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i64
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; Load the value from %p. This is done before accessing the shadow
; to ease atomic handling.
; CHECK: load i8
; CHECK: @__msan_metadata_ptr_for_load_1(i8* %p)
; Load the shadow and origin.
; CHECK: load i8
; CHECK: load i32
define i16 @Load2(i16* nocapture %p) nounwind uwtable sanitize_memory {
entry:
%0 = load i16, i16* %p
ret i16 %0
}
; CHECK-LABEL: @Load2
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i64
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; Load the value from %p. This is done before accessing the shadow
; to ease atomic handling.
; CHECK: load i16
; CHECK: [[REG:%[0-9]+]] = bitcast i16* %p to i8*
; CHECK: @__msan_metadata_ptr_for_load_2(i8* [[REG]])
; Load the shadow and origin.
; CHECK: load i16
; CHECK: load i32
define i32 @Load4(i32* nocapture %p) nounwind uwtable sanitize_memory {
entry:
%0 = load i32, i32* %p
ret i32 %0
}
; CHECK-LABEL: @Load4
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i64
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; Load the value from %p. This is done before accessing the shadow
; to ease atomic handling.
; CHECK: load i32
; CHECK: [[REG:%[0-9]+]] = bitcast i32* %p to i8*
; CHECK: @__msan_metadata_ptr_for_load_4(i8* [[REG]])
; Load the shadow and origin.
; CHECK: load i32
; CHECK: load i32
define i64 @Load8(i64* nocapture %p) nounwind uwtable sanitize_memory {
entry:
%0 = load i64, i64* %p
ret i64 %0
}
; CHECK-LABEL: @Load8
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i64
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; Load the value from %p. This is done before accessing the shadow
; to ease atomic handling.
; CHECK: load i64
; CHECK: [[REG:%[0-9]+]] = bitcast i64* %p to i8*
; CHECK: @__msan_metadata_ptr_for_load_8(i8* [[REG]])
; Load the shadow and origin.
; CHECK: load i64
; CHECK: load i32
define i128 @Load16(i128* nocapture %p) nounwind uwtable sanitize_memory {
entry:
%0 = load i128, i128* %p
ret i128 %0
}
; CHECK-LABEL: @Load16
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK-LABEL: entry.split:
; CHECK: ptrtoint {{.*}} [[PARAM_SHADOW]]
; Load the shadow of %p and check it
; CHECK: load i64
; CHECK: icmp
; CHECK: br i1
; CHECK-LABEL: <label>
; Load the value from %p. This is done before accessing the shadow
; to ease atomic handling.
; CHECK: load i128
; CHECK: [[REG:%[0-9]+]] = bitcast i128* %p to i8*
; CHECK: @__msan_metadata_ptr_for_load_n(i8* [[REG]], i64 16)
; Load the shadow and origin.
; CHECK: load i128
; CHECK: load i32
; Test kernel-specific va_list instrumentation
%struct.__va_list_tag = type { i32, i32, i8*, i8* }
declare void @llvm.va_start(i8*) nounwind
declare void @llvm.va_end(i8*)
@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
declare dso_local i32 @VAListFn(i8*, %struct.__va_list_tag*) local_unnamed_addr
; Function Attrs: nounwind uwtable
define dso_local i32 @VarArgFn(i8* %fmt, ...) local_unnamed_addr sanitize_memory #0 {
entry:
%args = alloca [1 x %struct.__va_list_tag], align 16
%0 = bitcast [1 x %struct.__va_list_tag]* %args to i8*
%arraydecay = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %args, i64 0, i64 0
call void @llvm.va_start(i8* nonnull %0)
%call = call i32 @VAListFn(i8* %fmt, %struct.__va_list_tag* nonnull %arraydecay)
call void @llvm.va_end(i8* nonnull %0)
ret i32 %call
}
; Kernel is built without SSE support.
attributes #0 = { "target-features"="+fxsr,+x87,-sse" }
; CHECK-LABEL: @VarArgFn
; CHECK: @__msan_get_context_state()
; CHECK: [[VA_ARG_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 2
; CHECK: [[VA_ARG_ORIGIN:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 3
; CHECK: [[VA_ARG_OVERFLOW_SIZE:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 4
; CHECK-LABEL: entry.split:
; CHECK: [[OSIZE:%[0-9]+]] = load i64, i64* [[VA_ARG_OVERFLOW_SIZE]]
; Register save area is 48 bytes for non-SSE builds.
; CHECK: [[SIZE:%[0-9]+]] = add i64 48, [[OSIZE]]
; CHECK: [[SHADOWS:%[0-9]+]] = alloca i8, i64 [[SIZE]]
; CHECK: [[VA_ARG_SHADOW]]
; CHECK: call void @llvm.memcpy{{.*}}(i8* align 8 [[SHADOWS]], {{.*}}, i64 [[SIZE]]
; CHECK: [[ORIGINS:%[0-9]+]] = alloca i8, i64 [[SIZE]]
; CHECK: [[VA_ARG_ORIGIN]]
; CHECK: call void @llvm.memcpy{{.*}}(i8* align 8 [[ORIGINS]], {{.*}}, i64 [[SIZE]]
; CHECK: call i32 @VAListFn
; Function Attrs: nounwind uwtable
define dso_local void @VarArgCaller() local_unnamed_addr sanitize_memory {
entry:
%call = tail call i32 (i8*, ...) @VarArgFn(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i32 123)
ret void
}
; CHECK-LABEL: @VarArgCaller
; CHECK-LABEL: entry:
; CHECK: @__msan_get_context_state()
; CHECK: [[PARAM_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 0
; CHECK: [[VA_ARG_SHADOW:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 2
; CHECK: [[VA_ARG_OVERFLOW_SIZE:%[a-z0-9_]+]] = getelementptr {{.*}} i32 0, i32 4
; CHECK-LABEL: entry.split:
; CHECK: [[PARAM_SI:%[_a-z0-9]+]] = ptrtoint {{.*}} [[PARAM_SHADOW]]
; CHECK: [[ARG1_S:%[_a-z0-9]+]] = inttoptr i64 [[PARAM_SI]] to i64*
; First argument is initialized
; CHECK: store i64 0, i64* [[ARG1_S]]
; Dangling cast of va_arg_shadow[0], unused because the first argument is fixed.
; CHECK: [[VA_CAST0:%[_a-z0-9]+]] = ptrtoint {{.*}} [[VA_ARG_SHADOW]] to i64
; CHECK: [[VA_CAST1:%[_a-z0-9]+]] = ptrtoint {{.*}} [[VA_ARG_SHADOW]] to i64
; CHECK: [[ARG1_SI:%[_a-z0-9]+]] = add i64 [[VA_CAST1]], 8
; CHECK: [[PARG1_S:%[_a-z0-9]+]] = inttoptr i64 [[ARG1_SI]] to i32*
; Shadow for 123 is 0.
; CHECK: store i32 0, i32* [[ARG1_S]]
; CHECK: store i64 0, i64* [[VA_ARG_OVERFLOW_SIZE]]
; CHECK: call i32 (i8*, ...) @VarArgFn({{.*}} @.str{{.*}} i32 123)

View File

@ -1,5 +1,6 @@
; RUN: opt < %s -msan -msan-check-access-address=0 -msan-track-origins=1 -S | FileCheck -check-prefix=CHECK -check-prefix=CHECK-ORIGINS1 %s
; RUN: opt < %s -msan -msan-check-access-address=0 -msan-track-origins=2 -S | FileCheck -check-prefix=CHECK -check-prefix=CHECK-ORIGINS2 %s
; RUN: opt < %s -msan -msan-check-access-address=0 -msan-track-origins=1 -S | FileCheck -check-prefixes=CHECK,CHECK-MSAN,CHECK-ORIGINS1 %s
; RUN: opt < %s -msan -msan-check-access-address=0 -msan-track-origins=2 -S | FileCheck -check-prefixes=CHECK,CHECK-MSAN,CHECK-ORIGINS2 %s
; RUN: opt < %s -msan -msan-kernel=1 -msan-check-access-address=0 -S | FileCheck -check-prefixes=CHECK,CHECK-KMSAN,CHECK-ORIGINS2 %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"
@ -52,9 +53,17 @@ attributes #1 = { nounwind readnone }
; CHECK-LABEL: @Store
; CHECK: load {{.*}} @__msan_param_tls
; CHECK: [[ORIGIN:%[01-9a-z]+]] = load {{.*}} @__msan_param_origin_tls
; CHECK: store {{.*}}!dbg ![[DBG:[01-9]+]]
; CHECK-MSAN: load {{.*}} @__msan_param_tls
; CHECK-MSAN: [[ORIGIN:%[0-9a-z]+]] = load {{.*}} @__msan_param_origin_tls
; CHECK-KMSAN-LABEL: entry.split:
; CHECK-KMSAN: %param_shadow
; CHECK-KMSAN: load i32, i32*
; CHECK-KMSAN: %param_origin
; CHECK-KMSAN: [[ORIGIN:%[0-9a-z]+]] = load i32, i32*
; CHECK: store {{.*}}!dbg ![[DBG:[0-9]+]]
; CHECK: icmp
; CHECK: br i1
; CHECK: <label>
@ -63,7 +72,7 @@ attributes #1 = { nounwind readnone }
; CHECK-ORIGINS1: store i32 {{.*}}[[ORIGIN]],{{.*}}!dbg !{{.*}}[[DBG]]
; Origin tracking level 2: pass origin value through __msan_chain_origin and store the result.
; CHECK-ORIGINS2: [[ORIGIN2:%[01-9a-z]+]] = call i32 @__msan_chain_origin(i32 {{.*}}[[ORIGIN]])
; CHECK-ORIGINS2: [[ORIGIN2:%[0-9a-z]+]] = call i32 @__msan_chain_origin(i32 {{.*}}[[ORIGIN]])
; CHECK-ORIGINS2: store i32 {{.*}}[[ORIGIN2]],{{.*}}!dbg !{{.*}}[[DBG]]
; CHECK: br label{{.*}}!dbg !{{.*}}[[DBG]]