147 lines · cpp
1//===- Checker.cpp - C++ Lifetime Safety Checker ----------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the LifetimeChecker, which detects use-after-free10// errors by checking if live origins hold loans that have expired.11//12//===----------------------------------------------------------------------===//13 14#include "clang/Analysis/Analyses/LifetimeSafety/Checker.h"15#include "clang/AST/Expr.h"16#include "clang/Analysis/Analyses/LifetimeSafety/Facts.h"17#include "clang/Analysis/Analyses/LifetimeSafety/LiveOrigins.h"18#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"19#include "clang/Analysis/Analyses/LifetimeSafety/Loans.h"20#include "clang/Analysis/Analyses/PostOrderCFGView.h"21#include "clang/Analysis/AnalysisDeclContext.h"22#include "clang/Basic/SourceLocation.h"23#include "llvm/ADT/DenseMap.h"24#include "llvm/Support/ErrorHandling.h"25#include "llvm/Support/TimeProfiler.h"26 27namespace clang::lifetimes::internal {28 29static Confidence livenessKindToConfidence(LivenessKind K) {30 switch (K) {31 case LivenessKind::Must:32 return Confidence::Definite;33 case LivenessKind::Maybe:34 return Confidence::Maybe;35 case LivenessKind::Dead:36 return Confidence::None;37 }38 llvm_unreachable("unknown liveness kind");39}40 41namespace {42 43/// Struct to store the complete context for a potential lifetime violation.44struct PendingWarning {45 SourceLocation ExpiryLoc; // Where the loan expired.46 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *> CausingFact;47 Confidence ConfidenceLevel;48};49 50class LifetimeChecker {51private:52 llvm::DenseMap<LoanID, PendingWarning> FinalWarningsMap;53 const LoanPropagationAnalysis &LoanPropagation;54 const LiveOriginsAnalysis &LiveOrigins;55 const FactManager &FactMgr;56 LifetimeSafetyReporter *Reporter;57 58public:59 LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation,60 const LiveOriginsAnalysis &LiveOrigins, const FactManager &FM,61 AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter)62 : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM),63 Reporter(Reporter) {64 for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>())65 for (const Fact *F : FactMgr.getFacts(B))66 if (const auto *EF = F->getAs<ExpireFact>())67 checkExpiry(EF);68 issuePendingWarnings();69 }70 71 /// Checks for use-after-free & use-after-return errors when a loan expires.72 ///73 /// This method examines all live origins at the expiry point and determines74 /// if any of them hold the expiring loan. If so, it creates a pending75 /// warning with the appropriate confidence level based on the liveness76 /// information. The confidence reflects whether the origin is definitely77 /// or maybe live at this point.78 ///79 /// Note: This implementation considers only the confidence of origin80 /// liveness. Future enhancements could also consider the confidence of loan81 /// propagation (e.g., a loan may only be held on some execution paths).82 void checkExpiry(const ExpireFact *EF) {83 LoanID ExpiredLoan = EF->getLoanID();84 LivenessMap Origins = LiveOrigins.getLiveOriginsAt(EF);85 Confidence CurConfidence = Confidence::None;86 // The UseFact or OriginEscapesFact most indicative of a lifetime error,87 // prioritized by earlier source location.88 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>89 BestCausingFact = nullptr;90 91 for (auto &[OID, LiveInfo] : Origins) {92 LoanSet HeldLoans = LoanPropagation.getLoans(OID, EF);93 if (!HeldLoans.contains(ExpiredLoan))94 continue;95 // Loan is defaulted.96 Confidence NewConfidence = livenessKindToConfidence(LiveInfo.Kind);97 if (CurConfidence < NewConfidence) {98 CurConfidence = NewConfidence;99 BestCausingFact = LiveInfo.CausingFact;100 }101 }102 if (!BestCausingFact)103 return;104 // We have a use-after-free.105 Confidence LastConf = FinalWarningsMap.lookup(ExpiredLoan).ConfidenceLevel;106 if (LastConf >= CurConfidence)107 return;108 FinalWarningsMap[ExpiredLoan] = {/*ExpiryLoc=*/EF->getExpiryLoc(),109 /*BestCausingFact=*/BestCausingFact,110 /*ConfidenceLevel=*/CurConfidence};111 }112 113 void issuePendingWarnings() {114 if (!Reporter)115 return;116 for (const auto &[LID, Warning] : FinalWarningsMap) {117 const Loan &L = FactMgr.getLoanMgr().getLoan(LID);118 const Expr *IssueExpr = L.IssueExpr;119 llvm::PointerUnion<const UseFact *, const OriginEscapesFact *>120 CausingFact = Warning.CausingFact;121 Confidence Confidence = Warning.ConfidenceLevel;122 SourceLocation ExpiryLoc = Warning.ExpiryLoc;123 124 if (const auto *UF = CausingFact.dyn_cast<const UseFact *>())125 Reporter->reportUseAfterFree(IssueExpr, UF->getUseExpr(), ExpiryLoc,126 Confidence);127 else if (const auto *OEF =128 CausingFact.dyn_cast<const OriginEscapesFact *>())129 Reporter->reportUseAfterReturn(IssueExpr, OEF->getEscapeExpr(),130 ExpiryLoc, Confidence);131 else132 llvm_unreachable("Unhandled CausingFact type");133 }134 }135};136} // namespace137 138void runLifetimeChecker(const LoanPropagationAnalysis &LP,139 const LiveOriginsAnalysis &LO,140 const FactManager &FactMgr, AnalysisDeclContext &ADC,141 LifetimeSafetyReporter *Reporter) {142 llvm::TimeTraceScope TimeProfile("LifetimeChecker");143 LifetimeChecker Checker(LP, LO, FactMgr, ADC, Reporter);144}145 146} // namespace clang::lifetimes::internal147