aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:04 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:11 +0000
commite3b557809604d036af6e00c60f012c2025b59a5e (patch)
tree8a11ba2269a3b669601e2fd41145b174008f4da8 /llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
parent08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff)
Diffstat (limited to 'llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp')
-rw-r--r--llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp554
1 files changed, 452 insertions, 102 deletions
diff --git a/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
index 6815688827d2..e9614b48fde7 100644
--- a/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp
@@ -63,13 +63,14 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DepthFirstIterator.h"
-#include "llvm/ADT/None.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/iterator.h"
+#include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/Attributes.h"
@@ -222,6 +223,14 @@ static cl::opt<bool> ClConditionalCallbacks(
cl::desc("Insert calls to callback functions on conditionals."), cl::Hidden,
cl::init(false));
+// Experimental feature that inserts callbacks for data reaching a function,
+// either via function arguments and loads.
+// This must be true for dfsan_set_reaches_function_callback() to have effect.
+static cl::opt<bool> ClReachesFunctionCallbacks(
+ "dfsan-reaches-function-callbacks",
+ cl::desc("Insert calls to callback functions on data reaching a function."),
+ cl::Hidden, cl::init(false));
+
// Controls whether the pass tracks the control flow of select instructions.
static cl::opt<bool> ClTrackSelectControlFlow(
"dfsan-track-select-control-flow",
@@ -278,14 +287,23 @@ struct MemoryMapParams {
} // end anonymous namespace
+// NOLINTBEGIN(readability-identifier-naming)
+// aarch64 Linux
+const MemoryMapParams Linux_AArch64_MemoryMapParams = {
+ 0, // AndMask (not used)
+ 0x0B00000000000, // XorMask
+ 0, // ShadowBase (not used)
+ 0x0200000000000, // OriginBase
+};
+
// x86_64 Linux
-// NOLINTNEXTLINE(readability-identifier-naming)
-static const MemoryMapParams Linux_X86_64_MemoryMapParams = {
+const MemoryMapParams Linux_X86_64_MemoryMapParams = {
0, // AndMask (not used)
0x500000000000, // XorMask
0, // ShadowBase (not used)
0x100000000000, // OriginBase
};
+// NOLINTEND(readability-identifier-naming)
namespace {
@@ -386,7 +404,7 @@ transformFunctionAttributes(const TransformedFunction &TransformedFunction,
return AttributeList::get(Ctx, CallSiteAttrs.getFnAttrs(),
CallSiteAttrs.getRetAttrs(),
- llvm::makeArrayRef(ArgumentAttributes));
+ llvm::ArrayRef(ArgumentAttributes));
}
class DataFlowSanitizer {
@@ -445,12 +463,16 @@ class DataFlowSanitizer {
FunctionType *DFSanVarargWrapperFnTy;
FunctionType *DFSanConditionalCallbackFnTy;
FunctionType *DFSanConditionalCallbackOriginFnTy;
+ FunctionType *DFSanReachesFunctionCallbackFnTy;
+ FunctionType *DFSanReachesFunctionCallbackOriginFnTy;
FunctionType *DFSanCmpCallbackFnTy;
FunctionType *DFSanLoadStoreCallbackFnTy;
FunctionType *DFSanMemTransferCallbackFnTy;
FunctionType *DFSanChainOriginFnTy;
FunctionType *DFSanChainOriginIfTaintedFnTy;
FunctionType *DFSanMemOriginTransferFnTy;
+ FunctionType *DFSanMemShadowOriginTransferFnTy;
+ FunctionType *DFSanMemShadowOriginConditionalExchangeFnTy;
FunctionType *DFSanMaybeStoreOriginFnTy;
FunctionCallee DFSanUnionLoadFn;
FunctionCallee DFSanLoadLabelAndOriginFn;
@@ -464,10 +486,14 @@ class DataFlowSanitizer {
FunctionCallee DFSanMemTransferCallbackFn;
FunctionCallee DFSanConditionalCallbackFn;
FunctionCallee DFSanConditionalCallbackOriginFn;
+ FunctionCallee DFSanReachesFunctionCallbackFn;
+ FunctionCallee DFSanReachesFunctionCallbackOriginFn;
FunctionCallee DFSanCmpCallbackFn;
FunctionCallee DFSanChainOriginFn;
FunctionCallee DFSanChainOriginIfTaintedFn;
FunctionCallee DFSanMemOriginTransferFn;
+ FunctionCallee DFSanMemShadowOriginTransferFn;
+ FunctionCallee DFSanMemShadowOriginConditionalExchangeFn;
FunctionCallee DFSanMaybeStoreOriginFn;
SmallPtrSet<Value *, 16> DFSanRuntimeFunctions;
MDNode *ColdCallWeights;
@@ -498,7 +524,6 @@ class DataFlowSanitizer {
FunctionType *NewFT);
void initializeCallbackFunctions(Module &M);
void initializeRuntimeFunctions(Module &M);
- void injectMetadataGlobals(Module &M);
bool initializeModule(Module &M);
/// Advances \p OriginAddr to point to the next 32-bit origin and then loads
@@ -539,7 +564,8 @@ class DataFlowSanitizer {
public:
DataFlowSanitizer(const std::vector<std::string> &ABIListFiles);
- bool runImpl(Module &M);
+ bool runImpl(Module &M,
+ llvm::function_ref<TargetLibraryInfo &(Function &)> GetTLI);
};
struct DFSanFunction {
@@ -548,6 +574,7 @@ struct DFSanFunction {
DominatorTree DT;
bool IsNativeABI;
bool IsForceZeroLabels;
+ TargetLibraryInfo &TLI;
AllocaInst *LabelReturnAlloca = nullptr;
AllocaInst *OriginReturnAlloca = nullptr;
DenseMap<Value *, Value *> ValShadowMap;
@@ -579,9 +606,9 @@ struct DFSanFunction {
DenseMap<Value *, std::set<Value *>> ShadowElements;
DFSanFunction(DataFlowSanitizer &DFS, Function *F, bool IsNativeABI,
- bool IsForceZeroLabels)
+ bool IsForceZeroLabels, TargetLibraryInfo &TLI)
: DFS(DFS), F(F), IsNativeABI(IsNativeABI),
- IsForceZeroLabels(IsForceZeroLabels) {
+ IsForceZeroLabels(IsForceZeroLabels), TLI(TLI) {
DT.recalculate(*F);
}
@@ -666,6 +693,11 @@ struct DFSanFunction {
// branch instruction using the given conditional expression.
void addConditionalCallbacksIfEnabled(Instruction &I, Value *Condition);
+ // If ClReachesFunctionCallbacks is enabled, insert a callback for each
+ // argument and load instruction.
+ void addReachesFunctionCallbacksIfEnabled(IRBuilder<> &IRB, Instruction &I,
+ Value *Data);
+
bool isLookupTableConstant(Value *P);
private:
@@ -763,6 +795,10 @@ public:
void visitAtomicRMWInst(AtomicRMWInst &I);
void visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
void visitReturnInst(ReturnInst &RI);
+ void visitLibAtomicLoad(CallBase &CB);
+ void visitLibAtomicStore(CallBase &CB);
+ void visitLibAtomicExchange(CallBase &CB);
+ void visitLibAtomicCompareExchange(CallBase &CB);
void visitCallBase(CallBase &CB);
void visitPHINode(PHINode &PN);
void visitExtractElementInst(ExtractElementInst &I);
@@ -791,8 +827,31 @@ private:
void addOriginArguments(Function &F, CallBase &CB, std::vector<Value *> &Args,
IRBuilder<> &IRB);
+
+ Value *makeAddAcquireOrderingTable(IRBuilder<> &IRB);
+ Value *makeAddReleaseOrderingTable(IRBuilder<> &IRB);
};
+bool LibAtomicFunction(const Function &F) {
+ // This is a bit of a hack because TargetLibraryInfo is a function pass.
+ // The DFSan pass would need to be refactored to be function pass oriented
+ // (like MSan is) in order to fit together nicely with TargetLibraryInfo.
+ // We need this check to prevent them from being instrumented, or wrapped.
+ // Match on name and number of arguments.
+ if (!F.hasName() || F.isVarArg())
+ return false;
+ switch (F.arg_size()) {
+ case 4:
+ return F.getName() == "__atomic_load" || F.getName() == "__atomic_store";
+ case 5:
+ return F.getName() == "__atomic_exchange";
+ case 6:
+ return F.getName() == "__atomic_compare_exchange";
+ default:
+ return false;
+ }
+}
+
} // end anonymous namespace
DataFlowSanitizer::DataFlowSanitizer(
@@ -982,13 +1041,55 @@ void DFSanFunction::addConditionalCallbacksIfEnabled(Instruction &I,
}
IRBuilder<> IRB(&I);
Value *CondShadow = getShadow(Condition);
+ CallInst *CI;
if (DFS.shouldTrackOrigins()) {
Value *CondOrigin = getOrigin(Condition);
- IRB.CreateCall(DFS.DFSanConditionalCallbackOriginFn,
- {CondShadow, CondOrigin});
+ CI = IRB.CreateCall(DFS.DFSanConditionalCallbackOriginFn,
+ {CondShadow, CondOrigin});
+ } else {
+ CI = IRB.CreateCall(DFS.DFSanConditionalCallbackFn, {CondShadow});
+ }
+ CI->addParamAttr(0, Attribute::ZExt);
+}
+
+void DFSanFunction::addReachesFunctionCallbacksIfEnabled(IRBuilder<> &IRB,
+ Instruction &I,
+ Value *Data) {
+ if (!ClReachesFunctionCallbacks) {
+ return;
+ }
+ const DebugLoc &dbgloc = I.getDebugLoc();
+ Value *DataShadow = collapseToPrimitiveShadow(getShadow(Data), IRB);
+ ConstantInt *CILine;
+ llvm::Value *FilePathPtr;
+
+ if (dbgloc.get() == nullptr) {
+ CILine = llvm::ConstantInt::get(I.getContext(), llvm::APInt(32, 0));
+ FilePathPtr = IRB.CreateGlobalStringPtr(
+ I.getFunction()->getParent()->getSourceFileName());
} else {
- IRB.CreateCall(DFS.DFSanConditionalCallbackFn, {CondShadow});
+ CILine = llvm::ConstantInt::get(I.getContext(),
+ llvm::APInt(32, dbgloc.getLine()));
+ FilePathPtr =
+ IRB.CreateGlobalStringPtr(dbgloc->getFilename());
}
+
+ llvm::Value *FunctionNamePtr =
+ IRB.CreateGlobalStringPtr(I.getFunction()->getName());
+
+ CallInst *CB;
+ std::vector<Value *> args;
+
+ if (DFS.shouldTrackOrigins()) {
+ Value *DataOrigin = getOrigin(Data);
+ args = { DataShadow, DataOrigin, FilePathPtr, CILine, FunctionNamePtr };
+ CB = IRB.CreateCall(DFS.DFSanReachesFunctionCallbackOriginFn, args);
+ } else {
+ args = { DataShadow, FilePathPtr, CILine, FunctionNamePtr };
+ CB = IRB.CreateCall(DFS.DFSanReachesFunctionCallbackFn, args);
+ }
+ CB->addParamAttr(0, Attribute::ZExt);
+ CB->setDebugLoc(dbgloc);
}
Type *DataFlowSanitizer::getShadowTy(Type *OrigTy) {
@@ -1020,9 +1121,16 @@ bool DataFlowSanitizer::initializeModule(Module &M) {
if (TargetTriple.getOS() != Triple::Linux)
report_fatal_error("unsupported operating system");
- if (TargetTriple.getArch() != Triple::x86_64)
+ switch (TargetTriple.getArch()) {
+ case Triple::aarch64:
+ MapParams = &Linux_AArch64_MemoryMapParams;
+ break;
+ case Triple::x86_64:
+ MapParams = &Linux_X86_64_MemoryMapParams;
+ break;
+ default:
report_fatal_error("unsupported architecture");
- MapParams = &Linux_X86_64_MemoryMapParams;
+ }
Mod = &M;
Ctx = &M.getContext();
@@ -1052,8 +1160,8 @@ bool DataFlowSanitizer::initializeModule(Module &M) {
Type::getInt8PtrTy(*Ctx), IntptrTy};
DFSanSetLabelFnTy = FunctionType::get(Type::getVoidTy(*Ctx),
DFSanSetLabelArgs, /*isVarArg=*/false);
- DFSanNonzeroLabelFnTy =
- FunctionType::get(Type::getVoidTy(*Ctx), None, /*isVarArg=*/false);
+ DFSanNonzeroLabelFnTy = FunctionType::get(Type::getVoidTy(*Ctx), std::nullopt,
+ /*isVarArg=*/false);
DFSanVarargWrapperFnTy = FunctionType::get(
Type::getVoidTy(*Ctx), Type::getInt8PtrTy(*Ctx), /*isVarArg=*/false);
DFSanConditionalCallbackFnTy =
@@ -1063,6 +1171,16 @@ bool DataFlowSanitizer::initializeModule(Module &M) {
DFSanConditionalCallbackOriginFnTy = FunctionType::get(
Type::getVoidTy(*Ctx), DFSanConditionalCallbackOriginArgs,
/*isVarArg=*/false);
+ Type *DFSanReachesFunctionCallbackArgs[4] = {PrimitiveShadowTy, Int8Ptr,
+ OriginTy, Int8Ptr};
+ DFSanReachesFunctionCallbackFnTy =
+ FunctionType::get(Type::getVoidTy(*Ctx), DFSanReachesFunctionCallbackArgs,
+ /*isVarArg=*/false);
+ Type *DFSanReachesFunctionCallbackOriginArgs[5] = {
+ PrimitiveShadowTy, OriginTy, Int8Ptr, OriginTy, Int8Ptr};
+ DFSanReachesFunctionCallbackOriginFnTy = FunctionType::get(
+ Type::getVoidTy(*Ctx), DFSanReachesFunctionCallbackOriginArgs,
+ /*isVarArg=*/false);
DFSanCmpCallbackFnTy =
FunctionType::get(Type::getVoidTy(*Ctx), PrimitiveShadowTy,
/*isVarArg=*/false);
@@ -1078,6 +1196,15 @@ bool DataFlowSanitizer::initializeModule(Module &M) {
Type *DFSanMemOriginTransferArgs[3] = {Int8Ptr, Int8Ptr, IntptrTy};
DFSanMemOriginTransferFnTy = FunctionType::get(
Type::getVoidTy(*Ctx), DFSanMemOriginTransferArgs, /*isVarArg=*/false);
+ Type *DFSanMemShadowOriginTransferArgs[3] = {Int8Ptr, Int8Ptr, IntptrTy};
+ DFSanMemShadowOriginTransferFnTy =
+ FunctionType::get(Type::getVoidTy(*Ctx), DFSanMemShadowOriginTransferArgs,
+ /*isVarArg=*/false);
+ Type *DFSanMemShadowOriginConditionalExchangeArgs[5] = {
+ IntegerType::get(*Ctx, 8), Int8Ptr, Int8Ptr, Int8Ptr, IntptrTy};
+ DFSanMemShadowOriginConditionalExchangeFnTy = FunctionType::get(
+ Type::getVoidTy(*Ctx), DFSanMemShadowOriginConditionalExchangeArgs,
+ /*isVarArg=*/false);
Type *DFSanLoadStoreCallbackArgs[2] = {PrimitiveShadowTy, Int8Ptr};
DFSanLoadStoreCallbackFnTy =
FunctionType::get(Type::getVoidTy(*Ctx), DFSanLoadStoreCallbackArgs,
@@ -1146,7 +1273,7 @@ void DataFlowSanitizer::buildExternWeakCheckIfNeeded(IRBuilder<> &IRB,
// but replacing with a known-to-not-be-null wrapper can break this check.
// When replacing uses of the extern weak function with the wrapper we try
// to avoid replacing uses in conditionals, but this is not perfect.
- // In the case where we fail, and accidentially optimize out a null check
+ // In the case where we fail, and accidentally optimize out a null check
// for a extern weak function, add a check here to help identify the issue.
if (GlobalValue::isExternalWeakLinkage(F->getLinkage())) {
std::vector<Value *> Args;
@@ -1190,19 +1317,22 @@ DataFlowSanitizer::buildWrapperFunction(Function *F, StringRef NewFName,
// Initialize DataFlowSanitizer runtime functions and declare them in the module
void DataFlowSanitizer::initializeRuntimeFunctions(Module &M) {
+ LLVMContext &C = M.getContext();
{
AttributeList AL;
- AL = AL.addFnAttribute(M.getContext(), Attribute::NoUnwind);
- AL = AL.addFnAttribute(M.getContext(), Attribute::ReadOnly);
- AL = AL.addRetAttribute(M.getContext(), Attribute::ZExt);
+ AL = AL.addFnAttribute(C, Attribute::NoUnwind);
+ AL = AL.addFnAttribute(
+ C, Attribute::getWithMemoryEffects(C, MemoryEffects::readOnly()));
+ AL = AL.addRetAttribute(C, Attribute::ZExt);
DFSanUnionLoadFn =
Mod->getOrInsertFunction("__dfsan_union_load", DFSanUnionLoadFnTy, AL);
}
{
AttributeList AL;
- AL = AL.addFnAttribute(M.getContext(), Attribute::NoUnwind);
- AL = AL.addFnAttribute(M.getContext(), Attribute::ReadOnly);
- AL = AL.addRetAttribute(M.getContext(), Attribute::ZExt);
+ AL = AL.addFnAttribute(C, Attribute::NoUnwind);
+ AL = AL.addFnAttribute(
+ C, Attribute::getWithMemoryEffects(C, MemoryEffects::readOnly()));
+ AL = AL.addRetAttribute(C, Attribute::ZExt);
DFSanLoadLabelAndOriginFn = Mod->getOrInsertFunction(
"__dfsan_load_label_and_origin", DFSanLoadLabelAndOriginFnTy, AL);
}
@@ -1239,6 +1369,13 @@ void DataFlowSanitizer::initializeRuntimeFunctions(Module &M) {
DFSanMemOriginTransferFn = Mod->getOrInsertFunction(
"__dfsan_mem_origin_transfer", DFSanMemOriginTransferFnTy);
+ DFSanMemShadowOriginTransferFn = Mod->getOrInsertFunction(
+ "__dfsan_mem_shadow_origin_transfer", DFSanMemShadowOriginTransferFnTy);
+
+ DFSanMemShadowOriginConditionalExchangeFn =
+ Mod->getOrInsertFunction("__dfsan_mem_shadow_origin_conditional_exchange",
+ DFSanMemShadowOriginConditionalExchangeFnTy);
+
{
AttributeList AL;
AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt);
@@ -1272,6 +1409,10 @@ void DataFlowSanitizer::initializeRuntimeFunctions(Module &M) {
DFSanRuntimeFunctions.insert(
DFSanConditionalCallbackOriginFn.getCallee()->stripPointerCasts());
DFSanRuntimeFunctions.insert(
+ DFSanReachesFunctionCallbackFn.getCallee()->stripPointerCasts());
+ DFSanRuntimeFunctions.insert(
+ DFSanReachesFunctionCallbackOriginFn.getCallee()->stripPointerCasts());
+ DFSanRuntimeFunctions.insert(
DFSanCmpCallbackFn.getCallee()->stripPointerCasts());
DFSanRuntimeFunctions.insert(
DFSanChainOriginFn.getCallee()->stripPointerCasts());
@@ -1280,48 +1421,67 @@ void DataFlowSanitizer::initializeRuntimeFunctions(Module &M) {
DFSanRuntimeFunctions.insert(
DFSanMemOriginTransferFn.getCallee()->stripPointerCasts());
DFSanRuntimeFunctions.insert(
+ DFSanMemShadowOriginTransferFn.getCallee()->stripPointerCasts());
+ DFSanRuntimeFunctions.insert(
+ DFSanMemShadowOriginConditionalExchangeFn.getCallee()
+ ->stripPointerCasts());
+ DFSanRuntimeFunctions.insert(
DFSanMaybeStoreOriginFn.getCallee()->stripPointerCasts());
}
// Initializes event callback functions and declare them in the module
void DataFlowSanitizer::initializeCallbackFunctions(Module &M) {
- DFSanLoadCallbackFn = Mod->getOrInsertFunction("__dfsan_load_callback",
- DFSanLoadStoreCallbackFnTy);
- DFSanStoreCallbackFn = Mod->getOrInsertFunction("__dfsan_store_callback",
- DFSanLoadStoreCallbackFnTy);
+ {
+ AttributeList AL;
+ AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt);
+ DFSanLoadCallbackFn = Mod->getOrInsertFunction(
+ "__dfsan_load_callback", DFSanLoadStoreCallbackFnTy, AL);
+ }
+ {
+ AttributeList AL;
+ AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt);
+ DFSanStoreCallbackFn = Mod->getOrInsertFunction(
+ "__dfsan_store_callback", DFSanLoadStoreCallbackFnTy, AL);
+ }
DFSanMemTransferCallbackFn = Mod->getOrInsertFunction(
"__dfsan_mem_transfer_callback", DFSanMemTransferCallbackFnTy);
- DFSanCmpCallbackFn =
- Mod->getOrInsertFunction("__dfsan_cmp_callback", DFSanCmpCallbackFnTy);
-
- DFSanConditionalCallbackFn = Mod->getOrInsertFunction(
- "__dfsan_conditional_callback", DFSanConditionalCallbackFnTy);
- DFSanConditionalCallbackOriginFn =
- Mod->getOrInsertFunction("__dfsan_conditional_callback_origin",
- DFSanConditionalCallbackOriginFnTy);
-}
-
-void DataFlowSanitizer::injectMetadataGlobals(Module &M) {
- // These variables can be used:
- // - by the runtime (to discover what the shadow width was, during
- // compilation)
- // - in testing (to avoid hardcoding the shadow width and type but instead
- // extract them by pattern matching)
- Type *IntTy = Type::getInt32Ty(*Ctx);
- (void)Mod->getOrInsertGlobal("__dfsan_shadow_width_bits", IntTy, [&] {
- return new GlobalVariable(
- M, IntTy, /*isConstant=*/true, GlobalValue::WeakODRLinkage,
- ConstantInt::get(IntTy, ShadowWidthBits), "__dfsan_shadow_width_bits");
- });
- (void)Mod->getOrInsertGlobal("__dfsan_shadow_width_bytes", IntTy, [&] {
- return new GlobalVariable(M, IntTy, /*isConstant=*/true,
- GlobalValue::WeakODRLinkage,
- ConstantInt::get(IntTy, ShadowWidthBytes),
- "__dfsan_shadow_width_bytes");
- });
+ {
+ AttributeList AL;
+ AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt);
+ DFSanCmpCallbackFn = Mod->getOrInsertFunction("__dfsan_cmp_callback",
+ DFSanCmpCallbackFnTy, AL);
+ }
+ {
+ AttributeList AL;
+ AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt);
+ DFSanConditionalCallbackFn = Mod->getOrInsertFunction(
+ "__dfsan_conditional_callback", DFSanConditionalCallbackFnTy, AL);
+ }
+ {
+ AttributeList AL;
+ AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt);
+ DFSanConditionalCallbackOriginFn =
+ Mod->getOrInsertFunction("__dfsan_conditional_callback_origin",
+ DFSanConditionalCallbackOriginFnTy, AL);
+ }
+ {
+ AttributeList AL;
+ AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt);
+ DFSanReachesFunctionCallbackFn =
+ Mod->getOrInsertFunction("__dfsan_reaches_function_callback",
+ DFSanReachesFunctionCallbackFnTy, AL);
+ }
+ {
+ AttributeList AL;
+ AL = AL.addParamAttribute(M.getContext(), 0, Attribute::ZExt);
+ DFSanReachesFunctionCallbackOriginFn =
+ Mod->getOrInsertFunction("__dfsan_reaches_function_callback_origin",
+ DFSanReachesFunctionCallbackOriginFnTy, AL);
+ }
}
-bool DataFlowSanitizer::runImpl(Module &M) {
+bool DataFlowSanitizer::runImpl(
+ Module &M, llvm::function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
initializeModule(M);
if (ABIList.isIn(M, "skip"))
@@ -1362,8 +1522,6 @@ bool DataFlowSanitizer::runImpl(Module &M) {
"__dfsan_track_origins");
});
- injectMetadataGlobals(M);
-
initializeCallbackFunctions(M);
initializeRuntimeFunctions(M);
@@ -1372,7 +1530,8 @@ bool DataFlowSanitizer::runImpl(Module &M) {
SmallPtrSet<Function *, 2> FnsWithForceZeroLabel;
SmallPtrSet<Constant *, 1> PersonalityFns;
for (Function &F : M)
- if (!F.isIntrinsic() && !DFSanRuntimeFunctions.contains(&F)) {
+ if (!F.isIntrinsic() && !DFSanRuntimeFunctions.contains(&F) &&
+ !LibAtomicFunction(F)) {
FnsToInstrument.push_back(&F);
if (F.hasPersonalityFn())
PersonalityFns.insert(F.getPersonalityFn()->stripPointerCasts());
@@ -1383,9 +1542,7 @@ bool DataFlowSanitizer::runImpl(Module &M) {
assert(isa<Function>(C) && "Personality routine is not a function!");
Function *F = cast<Function>(C);
if (!isInstrumented(F))
- FnsToInstrument.erase(
- std::remove(FnsToInstrument.begin(), FnsToInstrument.end(), F),
- FnsToInstrument.end());
+ llvm::erase_value(FnsToInstrument, F);
}
}
@@ -1414,8 +1571,8 @@ bool DataFlowSanitizer::runImpl(Module &M) {
}
}
- ReadOnlyNoneAttrs.addAttribute(Attribute::ReadOnly)
- .addAttribute(Attribute::ReadNone);
+ // TODO: This could be more precise.
+ ReadOnlyNoneAttrs.addAttribute(Attribute::Memory);
// First, change the ABI of every function in the module. ABI-listed
// functions keep their original ABI and get a wrapper function.
@@ -1464,8 +1621,8 @@ bool DataFlowSanitizer::runImpl(Module &M) {
// br i1 icmp ne (i8 (i8)* @my_func, i8 (i8)* null), label %use_my_func,
// label %avoid_my_func
// The @"dfsw$my_func" wrapper is never null, so if we replace this use
- // in the comparision, the icmp will simplify to false and we have
- // accidentially optimized away a null check that is necessary.
+ // in the comparison, the icmp will simplify to false and we have
+ // accidentally optimized away a null check that is necessary.
// This can lead to a crash when the null extern_weak my_func is called.
//
// To prevent (the most common pattern of) this problem,
@@ -1525,7 +1682,32 @@ bool DataFlowSanitizer::runImpl(Module &M) {
removeUnreachableBlocks(*F);
DFSanFunction DFSF(*this, F, FnsWithNativeABI.count(F),
- FnsWithForceZeroLabel.count(F));
+ FnsWithForceZeroLabel.count(F), GetTLI(*F));
+
+ if (ClReachesFunctionCallbacks) {
+ // Add callback for arguments reaching this function.
+ for (auto &FArg : F->args()) {
+ Instruction *Next = &F->getEntryBlock().front();
+ Value *FArgShadow = DFSF.getShadow(&FArg);
+ if (isZeroShadow(FArgShadow))
+ continue;
+ if (Instruction *FArgShadowInst = dyn_cast<Instruction>(FArgShadow)) {
+ Next = FArgShadowInst->getNextNode();
+ }
+ if (shouldTrackOrigins()) {
+ if (Instruction *Origin =
+ dyn_cast<Instruction>(DFSF.getOrigin(&FArg))) {
+ // Ensure IRB insertion point is after loads for shadow and origin.
+ Instruction *OriginNext = Origin->getNextNode();
+ if (Next->comesBefore(OriginNext)) {
+ Next = OriginNext;
+ }
+ }
+ }
+ IRBuilder<> IRB(Next);
+ DFSF.addReachesFunctionCallbacksIfEnabled(IRB, *Next, &FArg);
+ }
+ }
// DFSanVisitor may create new basic blocks, which confuses df_iterator.
// Build a copy of the list before iterating over it.
@@ -2209,6 +2391,7 @@ void DFSanVisitor::visitLoadInst(LoadInst &LI) {
if (LI.isAtomic())
LI.setOrdering(addAcquireOrdering(LI.getOrdering()));
+ Instruction *AfterLi = LI.getNextNode();
Instruction *Pos = LI.isAtomic() ? LI.getNextNode() : &LI;
std::vector<Value *> Shadows;
std::vector<Value *> Origins;
@@ -2244,8 +2427,13 @@ void DFSanVisitor::visitLoadInst(LoadInst &LI) {
if (ClEventCallbacks) {
IRBuilder<> IRB(Pos);
Value *Addr8 = IRB.CreateBitCast(LI.getPointerOperand(), DFSF.DFS.Int8Ptr);
- IRB.CreateCall(DFSF.DFS.DFSanLoadCallbackFn, {PrimitiveShadow, Addr8});
+ CallInst *CI =
+ IRB.CreateCall(DFSF.DFS.DFSanLoadCallbackFn, {PrimitiveShadow, Addr8});
+ CI->addParamAttr(0, Attribute::ZExt);
}
+
+ IRBuilder<> IRB(AfterLi);
+ DFSF.addReachesFunctionCallbacksIfEnabled(IRB, LI, &LI);
}
Value *DFSanFunction::updateOriginIfTainted(Value *Shadow, Value *Origin,
@@ -2406,7 +2594,7 @@ void DFSanFunction::storePrimitiveShadowOrigin(Value *Addr, uint64_t Size,
if (LeftSize >= ShadowVecSize) {
auto *ShadowVecTy =
FixedVectorType::get(DFS.PrimitiveShadowTy, ShadowVecSize);
- Value *ShadowVec = UndefValue::get(ShadowVecTy);
+ Value *ShadowVec = PoisonValue::get(ShadowVecTy);
for (unsigned I = 0; I != ShadowVecSize; ++I) {
ShadowVec = IRB.CreateInsertElement(
ShadowVec, PrimitiveShadow,
@@ -2501,7 +2689,9 @@ void DFSanVisitor::visitStoreInst(StoreInst &SI) {
if (ClEventCallbacks) {
IRBuilder<> IRB(&SI);
Value *Addr8 = IRB.CreateBitCast(SI.getPointerOperand(), DFSF.DFS.Int8Ptr);
- IRB.CreateCall(DFSF.DFS.DFSanStoreCallbackFn, {PrimitiveShadow, Addr8});
+ CallInst *CI =
+ IRB.CreateCall(DFSF.DFS.DFSanStoreCallbackFn, {PrimitiveShadow, Addr8});
+ CI->addParamAttr(0, Attribute::ZExt);
}
}
@@ -2563,7 +2753,9 @@ void DFSanVisitor::visitCmpInst(CmpInst &CI) {
if (ClEventCallbacks) {
IRBuilder<> IRB(&CI);
Value *CombinedShadow = DFSF.getShadow(&CI);
- IRB.CreateCall(DFSF.DFS.DFSanCmpCallbackFn, CombinedShadow);
+ CallInst *CallI =
+ IRB.CreateCall(DFSF.DFS.DFSanCmpCallbackFn, CombinedShadow);
+ CallI->addParamAttr(0, Attribute::ZExt);
}
}
@@ -2983,6 +3175,146 @@ bool DFSanVisitor::visitWrappedCallBase(Function &F, CallBase &CB) {
return false;
}
+Value *DFSanVisitor::makeAddAcquireOrderingTable(IRBuilder<> &IRB) {
+ constexpr int NumOrderings = (int)AtomicOrderingCABI::seq_cst + 1;
+ uint32_t OrderingTable[NumOrderings] = {};
+
+ OrderingTable[(int)AtomicOrderingCABI::relaxed] =
+ OrderingTable[(int)AtomicOrderingCABI::acquire] =
+ OrderingTable[(int)AtomicOrderingCABI::consume] =
+ (int)AtomicOrderingCABI::acquire;
+ OrderingTable[(int)AtomicOrderingCABI::release] =
+ OrderingTable[(int)AtomicOrderingCABI::acq_rel] =
+ (int)AtomicOrderingCABI::acq_rel;
+ OrderingTable[(int)AtomicOrderingCABI::seq_cst] =
+ (int)AtomicOrderingCABI::seq_cst;
+
+ return ConstantDataVector::get(IRB.getContext(),
+ ArrayRef(OrderingTable, NumOrderings));
+}
+
+void DFSanVisitor::visitLibAtomicLoad(CallBase &CB) {
+ // Since we use getNextNode here, we can't have CB terminate the BB.
+ assert(isa<CallInst>(CB));
+
+ IRBuilder<> IRB(&CB);
+ Value *Size = CB.getArgOperand(0);
+ Value *SrcPtr = CB.getArgOperand(1);
+ Value *DstPtr = CB.getArgOperand(2);
+ Value *Ordering = CB.getArgOperand(3);
+ // Convert the call to have at least Acquire ordering to make sure
+ // the shadow operations aren't reordered before it.
+ Value *NewOrdering =
+ IRB.CreateExtractElement(makeAddAcquireOrderingTable(IRB), Ordering);
+ CB.setArgOperand(3, NewOrdering);
+
+ IRBuilder<> NextIRB(CB.getNextNode());
+ NextIRB.SetCurrentDebugLocation(CB.getDebugLoc());
+
+ // TODO: Support ClCombinePointerLabelsOnLoad
+ // TODO: Support ClEventCallbacks
+
+ NextIRB.CreateCall(DFSF.DFS.DFSanMemShadowOriginTransferFn,
+ {NextIRB.CreatePointerCast(DstPtr, NextIRB.getInt8PtrTy()),
+ NextIRB.CreatePointerCast(SrcPtr, NextIRB.getInt8PtrTy()),
+ NextIRB.CreateIntCast(Size, DFSF.DFS.IntptrTy, false)});
+}
+
+Value *DFSanVisitor::makeAddReleaseOrderingTable(IRBuilder<> &IRB) {
+ constexpr int NumOrderings = (int)AtomicOrderingCABI::seq_cst + 1;
+ uint32_t OrderingTable[NumOrderings] = {};
+
+ OrderingTable[(int)AtomicOrderingCABI::relaxed] =
+ OrderingTable[(int)AtomicOrderingCABI::release] =
+ (int)AtomicOrderingCABI::release;
+ OrderingTable[(int)AtomicOrderingCABI::consume] =
+ OrderingTable[(int)AtomicOrderingCABI::acquire] =
+ OrderingTable[(int)AtomicOrderingCABI::acq_rel] =
+ (int)AtomicOrderingCABI::acq_rel;
+ OrderingTable[(int)AtomicOrderingCABI::seq_cst] =
+ (int)AtomicOrderingCABI::seq_cst;
+
+ return ConstantDataVector::get(IRB.getContext(),
+ ArrayRef(OrderingTable, NumOrderings));
+}
+
+void DFSanVisitor::visitLibAtomicStore(CallBase &CB) {
+ IRBuilder<> IRB(&CB);
+ Value *Size = CB.getArgOperand(0);
+ Value *SrcPtr = CB.getArgOperand(1);
+ Value *DstPtr = CB.getArgOperand(2);
+ Value *Ordering = CB.getArgOperand(3);
+ // Convert the call to have at least Release ordering to make sure
+ // the shadow operations aren't reordered after it.
+ Value *NewOrdering =
+ IRB.CreateExtractElement(makeAddReleaseOrderingTable(IRB), Ordering);
+ CB.setArgOperand(3, NewOrdering);
+
+ // TODO: Support ClCombinePointerLabelsOnStore
+ // TODO: Support ClEventCallbacks
+
+ IRB.CreateCall(DFSF.DFS.DFSanMemShadowOriginTransferFn,
+ {IRB.CreatePointerCast(DstPtr, IRB.getInt8PtrTy()),
+ IRB.CreatePointerCast(SrcPtr, IRB.getInt8PtrTy()),
+ IRB.CreateIntCast(Size, DFSF.DFS.IntptrTy, false)});
+}
+
+void DFSanVisitor::visitLibAtomicExchange(CallBase &CB) {
+ // void __atomic_exchange(size_t size, void *ptr, void *val, void *ret, int
+ // ordering)
+ IRBuilder<> IRB(&CB);
+ Value *Size = CB.getArgOperand(0);
+ Value *TargetPtr = CB.getArgOperand(1);
+ Value *SrcPtr = CB.getArgOperand(2);
+ Value *DstPtr = CB.getArgOperand(3);
+
+ // This operation is not atomic for the shadow and origin memory.
+ // This could result in DFSan false positives or false negatives.
+ // For now we will assume these operations are rare, and
+ // the additional complexity to address this is not warrented.
+
+ // Current Target to Dest
+ IRB.CreateCall(DFSF.DFS.DFSanMemShadowOriginTransferFn,
+ {IRB.CreatePointerCast(DstPtr, IRB.getInt8PtrTy()),
+ IRB.CreatePointerCast(TargetPtr, IRB.getInt8PtrTy()),
+ IRB.CreateIntCast(Size, DFSF.DFS.IntptrTy, false)});
+
+ // Current Src to Target (overriding)
+ IRB.CreateCall(DFSF.DFS.DFSanMemShadowOriginTransferFn,
+ {IRB.CreatePointerCast(TargetPtr, IRB.getInt8PtrTy()),
+ IRB.CreatePointerCast(SrcPtr, IRB.getInt8PtrTy()),
+ IRB.CreateIntCast(Size, DFSF.DFS.IntptrTy, false)});
+}
+
+void DFSanVisitor::visitLibAtomicCompareExchange(CallBase &CB) {
+ // bool __atomic_compare_exchange(size_t size, void *ptr, void *expected, void
+ // *desired, int success_order, int failure_order)
+ Value *Size = CB.getArgOperand(0);
+ Value *TargetPtr = CB.getArgOperand(1);
+ Value *ExpectedPtr = CB.getArgOperand(2);
+ Value *DesiredPtr = CB.getArgOperand(3);
+
+ // This operation is not atomic for the shadow and origin memory.
+ // This could result in DFSan false positives or false negatives.
+ // For now we will assume these operations are rare, and
+ // the additional complexity to address this is not warrented.
+
+ IRBuilder<> NextIRB(CB.getNextNode());
+ NextIRB.SetCurrentDebugLocation(CB.getDebugLoc());
+
+ DFSF.setShadow(&CB, DFSF.DFS.getZeroShadow(&CB));
+
+ // If original call returned true, copy Desired to Target.
+ // If original call returned false, copy Target to Expected.
+ NextIRB.CreateCall(
+ DFSF.DFS.DFSanMemShadowOriginConditionalExchangeFn,
+ {NextIRB.CreateIntCast(&CB, NextIRB.getInt8Ty(), false),
+ NextIRB.CreatePointerCast(TargetPtr, NextIRB.getInt8PtrTy()),
+ NextIRB.CreatePointerCast(ExpectedPtr, NextIRB.getInt8PtrTy()),
+ NextIRB.CreatePointerCast(DesiredPtr, NextIRB.getInt8PtrTy()),
+ NextIRB.CreateIntCast(Size, DFSF.DFS.IntptrTy, false)});
+}
+
void DFSanVisitor::visitCallBase(CallBase &CB) {
Function *F = CB.getCalledFunction();
if ((F && F->isIntrinsic()) || CB.isInlineAsm()) {
@@ -2995,6 +3327,40 @@ void DFSanVisitor::visitCallBase(CallBase &CB) {
if (F == DFSF.DFS.DFSanVarargWrapperFn.getCallee()->stripPointerCasts())
return;
+ LibFunc LF;
+ if (DFSF.TLI.getLibFunc(CB, LF)) {
+ // libatomic.a functions need to have special handling because there isn't
+ // a good way to intercept them or compile the library with
+ // instrumentation.
+ switch (LF) {
+ case LibFunc_atomic_load:
+ if (!isa<CallInst>(CB)) {
+ llvm::errs() << "DFSAN -- cannot instrument invoke of libatomic load. "
+ "Ignoring!\n";
+ break;
+ }
+ visitLibAtomicLoad(CB);
+ return;
+ case LibFunc_atomic_store:
+ visitLibAtomicStore(CB);
+ return;
+ default:
+ break;
+ }
+ }
+
+ // TODO: These are not supported by TLI? They are not in the enum.
+ if (F && F->hasName() && !F->isVarArg()) {
+ if (F->getName() == "__atomic_exchange") {
+ visitLibAtomicExchange(CB);
+ return;
+ }
+ if (F->getName() == "__atomic_compare_exchange") {
+ visitLibAtomicCompareExchange(CB);
+ return;
+ }
+ }
+
DenseMap<Value *, Function *>::iterator UnwrappedFnIt =
DFSF.DFS.UnwrappedFnMap.find(CB.getCalledOperand());
if (UnwrappedFnIt != DFSF.DFS.UnwrappedFnMap.end())
@@ -3071,6 +3437,8 @@ void DFSanVisitor::visitCallBase(CallBase &CB) {
DFSF.SkipInsts.insert(LI);
DFSF.setOrigin(&CB, LI);
}
+
+ DFSF.addReachesFunctionCallbacksIfEnabled(NextIRB, CB, &CB);
}
}
@@ -3099,38 +3467,20 @@ void DFSanVisitor::visitPHINode(PHINode &PN) {
DFSF.PHIFixups.push_back({&PN, ShadowPN, OriginPN});
}
-namespace {
-class DataFlowSanitizerLegacyPass : public ModulePass {
-private:
- std::vector<std::string> ABIListFiles;
-
-public:
- static char ID;
-
- DataFlowSanitizerLegacyPass(
- const std::vector<std::string> &ABIListFiles = std::vector<std::string>())
- : ModulePass(ID), ABIListFiles(ABIListFiles) {}
-
- bool runOnModule(Module &M) override {
- return DataFlowSanitizer(ABIListFiles).runImpl(M);
- }
-};
-} // namespace
-
-char DataFlowSanitizerLegacyPass::ID;
-
-INITIALIZE_PASS(DataFlowSanitizerLegacyPass, "dfsan",
- "DataFlowSanitizer: dynamic data flow analysis.", false, false)
-
-ModulePass *llvm::createDataFlowSanitizerLegacyPassPass(
- const std::vector<std::string> &ABIListFiles) {
- return new DataFlowSanitizerLegacyPass(ABIListFiles);
-}
-
PreservedAnalyses DataFlowSanitizerPass::run(Module &M,
ModuleAnalysisManager &AM) {
- if (DataFlowSanitizer(ABIListFiles).runImpl(M)) {
- return PreservedAnalyses::none();
- }
- return PreservedAnalyses::all();
+ auto GetTLI = [&](Function &F) -> TargetLibraryInfo & {
+ auto &FAM =
+ AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
+ return FAM.getResult<TargetLibraryAnalysis>(F);
+ };
+ if (!DataFlowSanitizer(ABIListFiles).runImpl(M, GetTLI))
+ return PreservedAnalyses::all();
+
+ PreservedAnalyses PA = PreservedAnalyses::none();
+ // GlobalsAA is considered stateless and does not get invalidated unless
+ // explicitly invalidated; PreservedAnalyses::none() is not enough. Sanitizers
+ // make changes that require GlobalsAA to be invalidated.
+ PA.abandon<GlobalsAA>();
+ return PA;
}