================ @@ -912,24 +965,129 @@ class ExpiredLoansAnalysis Lattice transfer(Lattice In, const IssueFact &F) { return Lattice(Factory.remove(In.Expired, F.getLoanID())); } + + ExpiredLoanMap getExpiredLoans(ProgramPoint P) { return getState(P).Expired; } }; // ========================================================================= // -// TODO: -// - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)` -// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)` -// - Using the above three to perform the final error reporting. +// Lifetime checker and Error reporter // ========================================================================= // +/// Struct to store the complete context for a potential lifetime violation. +struct PendingWarning { + const Expr *IssueExpr; // Where the loan was originally issued. + SourceLocation ExpiryLoc; // Where the loan expired. + const Expr *UseExpr; // Where the origin holding this loan was used. + Confidence Level; +}; + +class LifetimeChecker { +private: + llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap; + LoanPropagationAnalysis &LoanPropagation; + ExpiredLoansAnalysis &ExpiredLoans; + FactManager &FactMgr; + AnalysisDeclContext &ADC; + LifetimeSafetyReporter *Reporter; + +public: + LifetimeChecker(LoanPropagationAnalysis &LPA, ExpiredLoansAnalysis &ELA, + FactManager &FM, AnalysisDeclContext &ADC, + LifetimeSafetyReporter *Reporter) + : LoanPropagation(LPA), ExpiredLoans(ELA), FactMgr(FM), ADC(ADC), + Reporter(Reporter) {} + + void run() { + llvm::TimeTraceScope TimeProfile("LifetimeChecker"); + for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>()) + for (const Fact *F : FactMgr.getFacts(B)) + if (const auto *UF = F->getAs<UseFact>()) + checkUse(UF); + issuePendingWarnings(); + } + + /// Checks for use-after-free errors for a given use of an Origin. + /// + /// This method is called for each 'UseFact' identified in the control flow + /// graph. It determines if the loans held by the used origin have expired + /// at the point of use. + void checkUse(const UseFact *UF) { + + OriginID O = UF->getUsedOrigin(); + + // Get the set of loans that the origin might hold at this program point. + LoanSet HeldLoans = LoanPropagation.getLoans(O, UF); + + // Get the set of all loans that have expired at this program point. + ExpiredLoanMap AllExpiredLoans = ExpiredLoans.getExpiredLoans(UF); + + // If the pointer holds no loans or no loans have expired, there's nothing + // to check. + if (HeldLoans.isEmpty() || AllExpiredLoans.isEmpty()) + return; + + // Identify loans that which have expired but are held by the pointer. Using + // them is a use-after-free. + llvm::SmallVector<LoanID> DefaultedLoans; + // A definite UaF error occurs if all loans the origin might hold have + // expired. + bool IsDefiniteError = true; + for (LoanID L : HeldLoans) { + if (AllExpiredLoans.contains(L)) + DefaultedLoans.push_back(L); + else + // If at least one loan is not expired, this use is not a definite UaF. + IsDefiniteError = false; + } + // If there are no defaulted loans, the use is safe. + if (DefaultedLoans.empty()) + return; + + // Determine the confidence level of the error (definite or maybe). + Confidence CurrentConfidence = + IsDefiniteError ? Confidence::Definite : Confidence::Maybe; + + // For each expired loan, create a pending warning. + for (LoanID DefaultedLoan : DefaultedLoans) { + // If we already have a warning for this loan with a higher or equal + // confidence, skip this one. + if (FinalWarningsMap.count(DefaultedLoan) && + CurrentConfidence <= FinalWarningsMap[DefaultedLoan].Level) + continue; + + const Loan &L = FactMgr.getLoanMgr().getLoan(DefaultedLoan); + auto *EF = AllExpiredLoans.lookup(DefaultedLoan); + assert(EF && "Could not find ExpireFact for an expired loan."); + + const Expr *IssueExpr = L.IssueExpr; + SourceLocation ExpiryLoc = dyn_cast<ExpireFact>(*EF)->getExpiryLoc(); + + FinalWarningsMap[DefaultedLoan] = {IssueExpr, ExpiryLoc, UF->getUseExpr(), ---------------- Xazax-hun wrote:
Why do we do this later instead of issuing the warning here? Could we pick `IssueExpr` non-deterministically if there are multiple options? Could this introduce non-determinism into the diagnostics? https://github.com/llvm/llvm-project/pull/149731 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits