================ @@ -0,0 +1,365 @@ +//===- AssignmentQuery.cpp - C++ Lifetime Safety Checker --------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the LifetimeChecker, which detects use-after-free +// errors by checking if live origins hold loans that have expired. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/LifetimeSafety/AssignmentQuery.h" +#include "clang/AST/Decl.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h" +#include "clang/Analysis/Analyses/LifetimeSafety/Origins.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "llvm/ADT/SmallPtrSet.h" +#include <cstddef> + +namespace { + +using namespace clang; +using namespace clang::lifetimes; +using namespace clang::lifetimes::internal; + +std::optional<const Expr *> GetPureSrcExpr(const Expr *TargetExpr) { + if (!TargetExpr) + return std::nullopt; + const Expr *SExpr = TargetExpr->IgnoreParenCasts(); + if (!SExpr) + return std::nullopt; + + if (llvm::isa<DeclRefExpr, CXXTemporaryObjectExpr, ConditionalOperator, + CXXConstructExpr>(SExpr) && + !SExpr->getExprLoc().isInvalid()) + return SExpr; + + if (const auto *SCExpr = llvm::dyn_cast<CallExpr>(SExpr); + SCExpr && !SCExpr->getExprLoc().isInvalid() && + !SCExpr->getCallee()->IgnoreParenCasts()->getExprLoc().isInvalid()) + return SCExpr; + + if (const auto *SMExpr = llvm::dyn_cast<MemberExpr>(SExpr)) + return GetPureSrcExpr(SMExpr->getBase()); + if (const auto *SCExpr = llvm::dyn_cast<CXXMemberCallExpr>(SExpr)) + return GetPureSrcExpr(SCExpr->getCallee()); + if (const auto *SUOExpr = llvm::dyn_cast<UnaryOperator>(SExpr)) + return GetPureSrcExpr(SUOExpr->getSubExpr()); + if (const auto *SCBExpr = llvm::dyn_cast<CXXBindTemporaryExpr>(SExpr)) + return GetPureSrcExpr(SCBExpr->getSubExpr()); + + return std::nullopt; +} + +/// Specifically handles assignments involving a FieldDecl. +/// +/// Since we currently only store the FieldDecl without its corresponding +/// LHS expression, this function attempts to recover or resolve the LHS +/// context by analyzing the RHS. +const MemberExpr *getFieldFromAssignmentExpr(const Expr *RHS, + const ParentMap &CurrParentMap) { + + const Stmt *CurrStmt = CurrParentMap.getParent(RHS); + if (!CurrStmt) + return nullptr; + if (const auto *BinaryOp = llvm::dyn_cast<BinaryOperator>(CurrStmt)) + return llvm::dyn_cast<MemberExpr>(BinaryOp->getLHS()); + if (const auto *CXXOp = llvm::dyn_cast<CXXOperatorCallExpr>(CurrStmt); + CXXOp && CXXOp->getOperator() == OO_Equal && CXXOp->getNumArgs() == 2) + return llvm::dyn_cast<MemberExpr>(CXXOp->getArg(0)); + return nullptr; +} + +const DeclRefExpr *getLHSExpr(const UseFact *UF, const OriginID OID) { + for (const OriginList *Cur = UF->getUsedOrigins(); Cur; + Cur = Cur->peelOuterOrigin()) { + if (Cur->getOuterOriginID() != OID || !UF->isWritten()) + continue; + std::optional<const Expr *> UExpr = GetPureSrcExpr(UF->getUseExpr()); + if (UExpr) { + if (const auto *UDExpr = llvm::dyn_cast<DeclRefExpr>(UExpr.value())) { + return UDExpr; + } + } + } + return nullptr; +} + +std::optional<OriginDestExpr> +getLHSDeclOrExpr(const AssignmentQueryContext &Context, + const OriginFlowFact *OFF) { + const Origin TargetOrigin = + Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID()); + if (const ValueDecl *DVecl = TargetOrigin.getDecl(); + DVecl && !DVecl->getLocation().isInvalid()) { + if (llvm::isa<FieldDecl>(DVecl)) { + const Expr *CurrExpr = Context.FactMgr.getOriginMgr() + .getOrigin(OFF->getSrcOriginID()) + .getExpr(); + if (CurrExpr) + return getFieldFromAssignmentExpr(CurrExpr, Context.ADC.getParentMap()); + } else { + return DVecl; + } + } + return std::nullopt; +} + +std::optional<const Expr *> +getRHSDeclOrExpr(const AssignmentQueryContext &Context, + const OriginFlowFact *OFF) { + const Origin TargetOrigin = + Context.FactMgr.getOriginMgr().getOrigin(OFF->getDestOriginID()); + std::optional<const Expr *> SExpr = GetPureSrcExpr(TargetOrigin.getExpr()); + if (!SExpr) { + const Origin SrcOrigin = + Context.FactMgr.getOriginMgr().getOrigin(OFF->getSrcOriginID()); + SExpr = GetPureSrcExpr(SrcOrigin.getExpr()); + } + + return SExpr; +} + +AliasAssignmentSearchResult getAliasListCore( + const AssignmentQueryContext &Context, + llvm::SmallVectorImpl<AssignmentPair> &AssignmentList, + const CFGBlock *Block, const LoanID EndLoanID, OriginID *TargetOID, + const std::optional<OriginDestExpr> LastDestExpr = std::nullopt, + const std::optional<OriginID> LastOriginID = std::nullopt) { + llvm::ArrayRef<const Fact *> Facts = Context.FactMgr.getFacts(Block); + std::optional<OriginID> IssueOriginID = LastOriginID; + std::optional<OriginDestExpr> CurrDestExpr = LastDestExpr; + std::optional<OriginID> CurrOrigin = std::nullopt; + + const auto InsertAssignmentList = [&](const OriginFlowFact *OFF) { + if (!CurrDestExpr) { + std::optional<OriginDestExpr> DestExpr = getLHSDeclOrExpr(Context, OFF); + if (DestExpr) { + if (llvm::isa<const ValueDecl *>(DestExpr.value())) + CurrOrigin = *TargetOID; + CurrDestExpr = DestExpr; + } + } else { + std::optional<const Expr *> CurrSrcExpr = getRHSDeclOrExpr(Context, OFF); + if (CurrSrcExpr) { + AssignmentList.push_back({CurrDestExpr.value(), CurrSrcExpr.value()}); + CurrDestExpr = std::nullopt; + CurrOrigin = std::nullopt; + } + } + }; + + for (const Fact *F : llvm::reverse(Facts)) { + if (const auto *OFF = F->getAs<OriginFlowFact>()) { + if (IssueOriginID && OFF->getDestOriginID() == IssueOriginID.value()) + return {true, CurrDestExpr, IssueOriginID}; + if (OFF->getDestOriginID() == *TargetOID && + Context.LoanPropagation.getLoans(OFF->getSrcOriginID(), OFF) + .contains(EndLoanID)) { + InsertAssignmentList(OFF); + *TargetOID = OFF->getSrcOriginID(); + } + } else if (const auto *IF = F->getAs<IssueFact>()) { + if (IF->getLoanID() == EndLoanID) + IssueOriginID = IF->getOriginID(); + } else if (const auto *UF = F->getAs<UseFact>()) { + if (CurrOrigin) { + const DeclRefExpr *LHSExpr = getLHSExpr(UF, CurrOrigin.value()); + if (LHSExpr) + CurrDestExpr = LHSExpr; + } + } + } + + return {false, CurrDestExpr, IssueOriginID}; +} + +void getAliasListInMultiBlock( + const AssignmentQueryContext &Context, + llvm::SmallVectorImpl<AssignmentPair> &AssignmentList, + const CFGBlock *StartBlock, const LoanID EndLoanID, OriginID *StartOID) { + std::optional<OriginDestExpr> LastDestDecl = std::nullopt; + llvm::SmallVector<const CFGBlock *> PendingBlocks; + std::optional<AssignmentPair> StartStmt = std::nullopt; + std::optional<AssignmentPair> EndStmt = std::nullopt; + std::optional<OriginID> LastOriginID = std::nullopt; + llvm::SmallPtrSet<const CFGBlock *, 32> VistedBlocks; + llvm::DenseMap<AssignmentPair, AssignmentPair> VistedExprs; + + const auto AliasStmtFilter = [&VistedExprs, + &AssignmentList](const AssignmentPair StartStmt, + const AssignmentPair EndStmt) { + llvm::SmallVector<AssignmentPair> AliasStmts; + for (AssignmentPair Stmt = StartStmt; Stmt != EndStmt; + Stmt = VistedExprs.at(Stmt)) + AssignmentList.push_back(Stmt); + AssignmentList.push_back(EndStmt); + return AliasStmts; + }; + + PendingBlocks.push_back(StartBlock); + + for (size_t i = 0; i < PendingBlocks.size(); ++i) { ---------------- Xazax-hun wrote:
Is this correct? Like the size of `PendingBlocks` can increase in the loop, like you are pushing back to it below. Could you skip processing those? https://github.com/llvm/llvm-project/pull/188467 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
