A write to a stack object of the function is not observable outside of it as long as the object has not escaped. Let such writes and memsets stay on a clean path. A write after the object may have been captured, a volatile or atomic one and a write to other memory still make the path dirty. Signed-off-by: Anton Protopopov --- scripts/llvm-plugins/bpf_scan_ir.cpp | 85 +++++++++++++++++++++------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/scripts/llvm-plugins/bpf_scan_ir.cpp b/scripts/llvm-plugins/bpf_scan_ir.cpp index 3684918eb7fa..c45fb97d1b93 100644 --- a/scripts/llvm-plugins/bpf_scan_ir.cpp +++ b/scripts/llvm-plugins/bpf_scan_ir.cpp @@ -4,13 +4,17 @@ #include "../bpf/fmodret_metadata.h" #include +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/CaptureTracking.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/Config/llvm-config.h" #include "llvm/IR/Analysis.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" @@ -50,41 +54,76 @@ static bool errnoConstant(const Value *V, int64_t &N) return C && isErrno(C->getValue(), N); } -static bool cleanInstruction(const Instruction &I) -{ - if (auto *Intrinsic = dyn_cast(&I)) { - switch (Intrinsic->getIntrinsicID()) { - case Intrinsic::assume: - case Intrinsic::lifetime_start: - case Intrinsic::lifetime_end: - case Intrinsic::invariant_start: - case Intrinsic::invariant_end: - return true; - default: - break; +class CleanInstructions { + DominatorTree &DT; + DenseMap PrivateWrites; + + bool privateDestination(const Value *Pointer, const Instruction &I) + { + auto It = PrivateWrites.find(&I); + if (It != PrivateWrites.end()) + return It->second; + auto *Allocation = dyn_cast(getUnderlyingObject(Pointer)); + bool Private = false; + + if (Allocation && Allocation->getFunction() == I.getFunction()) { + // A later call may receive the initialized object. Only captures + // that can precede this write make the write externally visible. +#if LLVM_VERSION_MAJOR >= 21 + Private = !PointerMayBeCapturedBefore(Allocation, true, &I, &DT, true, 256); +#else + Private = !PointerMayBeCapturedBefore(Allocation, true, true, &I, &DT, true, 256); +#endif } + PrivateWrites[&I] = Private; + return Private; } - return !I.mayHaveSideEffects(); -} -static bool cleanBB(BasicBlock *BB) +public: + explicit CleanInstructions(DominatorTree &DT) : DT(DT) {} + + bool isClean(const Instruction &I) + { + if (auto *Store = dyn_cast(&I)) + return !Store->isVolatile() && !Store->isAtomic() && + privateDestination(Store->getPointerOperand(), I); + if (auto *Memory = dyn_cast(&I)) + return !Memory->isVolatile() && + privateDestination(Memory->getRawDest(), I); + if (auto *Intrinsic = dyn_cast(&I)) { + switch (Intrinsic->getIntrinsicID()) { + case Intrinsic::assume: + case Intrinsic::lifetime_start: + case Intrinsic::lifetime_end: + case Intrinsic::invariant_start: + case Intrinsic::invariant_end: + return true; + default: + break; + } + } + return !I.mayHaveSideEffects(); + } +}; + +static bool cleanBB(BasicBlock *BB, CleanInstructions &Clean) { for (Instruction &I : *BB) { - if (!cleanInstruction(I)) + if (!Clean.isClean(I)) return false; } return true; } /* Add the errors which clean paths of F are known to return to VALUES */ -static void cleanReturnValues(Function &F, ReturnValues &Values) +static void cleanReturnValues(Function &F, CleanInstructions &Clean, ReturnValues &Values) { SmallPtrSet CleanBBs; SmallVector Worklist; bool Changed; /* The clean blocks which the entry reaches through clean blocks */ - if (cleanBB(&F.getEntryBlock())) { + if (cleanBB(&F.getEntryBlock(), Clean)) { CleanBBs.insert(&F.getEntryBlock()); Worklist.push_back(&F.getEntryBlock()); } @@ -92,7 +131,7 @@ static void cleanReturnValues(Function &F, ReturnValues &Values) BasicBlock *BB = Worklist.pop_back_val(); for (BasicBlock *Successor : successors(BB)) { - if (!CleanBBs.contains(Successor) && cleanBB(Successor)) { + if (!CleanBBs.contains(Successor) && cleanBB(Successor, Clean)) { CleanBBs.insert(Successor); Worklist.push_back(Successor); } @@ -128,7 +167,7 @@ static void cleanReturnValues(Function &F, ReturnValues &Values) bool Entered = CleanBBs.contains(&BB); int64_t N; - if (!Return || !Return->getReturnValue() || !cleanBB(&BB)) + if (!Return || !Return->getReturnValue() || !cleanBB(&BB, Clean)) continue; for (BasicBlock *Pred : predecessors(&BB)) Entered |= CleanBBs.contains(Pred); @@ -188,6 +227,7 @@ public: return PreservedAnalyses::all(); SmallVector Descriptions; + auto &FAM = MAM.getResult(M).getManager(); for (Function &F : M) { if (F.isIntrinsic() || F.isDeclaration() || F.hasAvailableExternallyLinkage()) @@ -202,7 +242,10 @@ public: Flags |= FMODRET_FN_WEAK; if (Ret->isIntegerTy(64)) Flags |= FMODRET_FN_RET64; - cleanReturnValues(F, Values); + auto &DT = FAM.getResult(F); + CleanInstructions Clean(DT); + + cleanReturnValues(F, Clean, Values); /* A weak definition always has a record, see fmodret_metadata.h */ if (!Values.empty() || (Flags & FMODRET_FN_WEAK)) Descriptions.push_back({&F, Flags, std::move(Values)}); -- 2.43.0