Home | History | Annotate | Download | only in Checkers
      1 //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 //  This file defines a checker that checks virtual function calls during
     11 //  construction or destruction of C++ objects.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "ClangSACheckers.h"
     16 #include "clang/AST/DeclCXX.h"
     17 #include "clang/AST/StmtVisitor.h"
     18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
     19 #include "clang/StaticAnalyzer/Core/Checker.h"
     20 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
     21 #include "llvm/ADT/SmallString.h"
     22 #include "llvm/Support/SaveAndRestore.h"
     23 #include "llvm/Support/raw_ostream.h"
     24 
     25 using namespace clang;
     26 using namespace ento;
     27 
     28 namespace {
     29 
     30 class WalkAST : public StmtVisitor<WalkAST> {
     31   const CheckerBase *Checker;
     32   BugReporter &BR;
     33   AnalysisDeclContext *AC;
     34 
     35   typedef const CallExpr * WorkListUnit;
     36   typedef SmallVector<WorkListUnit, 20> DFSWorkList;
     37 
     38   /// A vector representing the worklist which has a chain of CallExprs.
     39   DFSWorkList WList;
     40 
     41   // PreVisited : A CallExpr to this FunctionDecl is in the worklist, but the
     42   // body has not been visited yet.
     43   // PostVisited : A CallExpr to this FunctionDecl is in the worklist, and the
     44   // body has been visited.
     45   enum Kind { NotVisited,
     46               PreVisited,  /**< A CallExpr to this FunctionDecl is in the
     47                                 worklist, but the body has not yet been
     48                                 visited. */
     49               PostVisited  /**< A CallExpr to this FunctionDecl is in the
     50                                 worklist, and the body has been visited. */
     51   };
     52 
     53   /// A DenseMap that records visited states of FunctionDecls.
     54   llvm::DenseMap<const FunctionDecl *, Kind> VisitedFunctions;
     55 
     56   /// The CallExpr whose body is currently being visited.  This is used for
     57   /// generating bug reports.  This is null while visiting the body of a
     58   /// constructor or destructor.
     59   const CallExpr *visitingCallExpr;
     60 
     61 public:
     62   WalkAST(const CheckerBase *checker, BugReporter &br,
     63           AnalysisDeclContext *ac)
     64       : Checker(checker), BR(br), AC(ac), visitingCallExpr(nullptr) {}
     65 
     66   bool hasWork() const { return !WList.empty(); }
     67 
     68   /// This method adds a CallExpr to the worklist and marks the callee as
     69   /// being PreVisited.
     70   void Enqueue(WorkListUnit WLUnit) {
     71     const FunctionDecl *FD = WLUnit->getDirectCallee();
     72     if (!FD || !FD->getBody())
     73       return;
     74     Kind &K = VisitedFunctions[FD];
     75     if (K != NotVisited)
     76       return;
     77     K = PreVisited;
     78     WList.push_back(WLUnit);
     79   }
     80 
     81   /// This method returns an item from the worklist without removing it.
     82   WorkListUnit Dequeue() {
     83     assert(!WList.empty());
     84     return WList.back();
     85   }
     86 
     87   void Execute() {
     88     while (hasWork()) {
     89       WorkListUnit WLUnit = Dequeue();
     90       const FunctionDecl *FD = WLUnit->getDirectCallee();
     91       assert(FD && FD->getBody());
     92 
     93       if (VisitedFunctions[FD] == PreVisited) {
     94         // If the callee is PreVisited, walk its body.
     95         // Visit the body.
     96         SaveAndRestore<const CallExpr *> SaveCall(visitingCallExpr, WLUnit);
     97         Visit(FD->getBody());
     98 
     99         // Mark the function as being PostVisited to indicate we have
    100         // scanned the body.
    101         VisitedFunctions[FD] = PostVisited;
    102         continue;
    103       }
    104 
    105       // Otherwise, the callee is PostVisited.
    106       // Remove it from the worklist.
    107       assert(VisitedFunctions[FD] == PostVisited);
    108       WList.pop_back();
    109     }
    110   }
    111 
    112   // Stmt visitor methods.
    113   void VisitCallExpr(CallExpr *CE);
    114   void VisitCXXMemberCallExpr(CallExpr *CE);
    115   void VisitStmt(Stmt *S) { VisitChildren(S); }
    116   void VisitChildren(Stmt *S);
    117 
    118   void ReportVirtualCall(const CallExpr *CE, bool isPure);
    119 
    120 };
    121 } // end anonymous namespace
    122 
    123 //===----------------------------------------------------------------------===//
    124 // AST walking.
    125 //===----------------------------------------------------------------------===//
    126 
    127 void WalkAST::VisitChildren(Stmt *S) {
    128   for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I)
    129     if (Stmt *child = *I)
    130       Visit(child);
    131 }
    132 
    133 void WalkAST::VisitCallExpr(CallExpr *CE) {
    134   VisitChildren(CE);
    135   Enqueue(CE);
    136 }
    137 
    138 void WalkAST::VisitCXXMemberCallExpr(CallExpr *CE) {
    139   VisitChildren(CE);
    140   bool callIsNonVirtual = false;
    141 
    142   // Several situations to elide for checking.
    143   if (MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
    144     // If the member access is fully qualified (i.e., X::F), then treat
    145     // this as a non-virtual call and do not warn.
    146     if (CME->getQualifier())
    147       callIsNonVirtual = true;
    148 
    149     // Elide analyzing the call entirely if the base pointer is not 'this'.
    150     if (Expr *base = CME->getBase()->IgnoreImpCasts())
    151       if (!isa<CXXThisExpr>(base))
    152         return;
    153   }
    154 
    155   // Get the callee.
    156   const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(CE->getDirectCallee());
    157   if (MD && MD->isVirtual() && !callIsNonVirtual)
    158     ReportVirtualCall(CE, MD->isPure());
    159 
    160   Enqueue(CE);
    161 }
    162 
    163 void WalkAST::ReportVirtualCall(const CallExpr *CE, bool isPure) {
    164   SmallString<100> buf;
    165   llvm::raw_svector_ostream os(buf);
    166 
    167   os << "Call Path : ";
    168   // Name of current visiting CallExpr.
    169   os << *CE->getDirectCallee();
    170 
    171   // Name of the CallExpr whose body is current walking.
    172   if (visitingCallExpr)
    173     os << " <-- " << *visitingCallExpr->getDirectCallee();
    174   // Names of FunctionDecls in worklist with state PostVisited.
    175   for (SmallVectorImpl<const CallExpr *>::iterator I = WList.end(),
    176          E = WList.begin(); I != E; --I) {
    177     const FunctionDecl *FD = (*(I-1))->getDirectCallee();
    178     assert(FD);
    179     if (VisitedFunctions[FD] == PostVisited)
    180       os << " <-- " << *FD;
    181   }
    182 
    183   PathDiagnosticLocation CELoc =
    184     PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
    185   SourceRange R = CE->getCallee()->getSourceRange();
    186 
    187   if (isPure) {
    188     os << "\n" <<  "Call pure virtual functions during construction or "
    189        << "destruction may leads undefined behaviour";
    190     BR.EmitBasicReport(AC->getDecl(), Checker,
    191                        "Call pure virtual function during construction or "
    192                        "Destruction",
    193                        "Cplusplus", os.str(), CELoc, R);
    194     return;
    195   }
    196   else {
    197     os << "\n" << "Call virtual functions during construction or "
    198        << "destruction will never go to a more derived class";
    199     BR.EmitBasicReport(AC->getDecl(), Checker,
    200                        "Call virtual function during construction or "
    201                        "Destruction",
    202                        "Cplusplus", os.str(), CELoc, R);
    203     return;
    204   }
    205 }
    206 
    207 //===----------------------------------------------------------------------===//
    208 // VirtualCallChecker
    209 //===----------------------------------------------------------------------===//
    210 
    211 namespace {
    212 class VirtualCallChecker : public Checker<check::ASTDecl<CXXRecordDecl> > {
    213 public:
    214   void checkASTDecl(const CXXRecordDecl *RD, AnalysisManager& mgr,
    215                     BugReporter &BR) const {
    216     WalkAST walker(this, BR, mgr.getAnalysisDeclContext(RD));
    217 
    218     // Check the constructors.
    219     for (const auto *I : RD->ctors()) {
    220       if (!I->isCopyOrMoveConstructor())
    221         if (Stmt *Body = I->getBody()) {
    222           walker.Visit(Body);
    223           walker.Execute();
    224         }
    225     }
    226 
    227     // Check the destructor.
    228     if (CXXDestructorDecl *DD = RD->getDestructor())
    229       if (Stmt *Body = DD->getBody()) {
    230         walker.Visit(Body);
    231         walker.Execute();
    232       }
    233   }
    234 };
    235 }
    236 
    237 void ento::registerVirtualCallChecker(CheckerManager &mgr) {
    238   mgr.registerChecker<VirtualCallChecker>();
    239 }
    240