1 //=======- VirtualCallChecker.cpp --------------------------------*- 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines a checker that checks virtual method calls during 10 // construction or destruction of C++ objects. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15 #include "clang/AST/DeclCXX.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 18 #include "clang/StaticAnalyzer/Core/Checker.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" 23 24 using namespace clang; 25 using namespace ento; 26 27 namespace { 28 enum class ObjectState : bool { CtorCalled, DtorCalled }; 29 } // end namespace 30 // FIXME: Ascending over StackFrameContext maybe another method. 31 32 namespace llvm { 33 template <> struct FoldingSetTrait<ObjectState> { 34 static inline void Profile(ObjectState X, FoldingSetNodeID &ID) { 35 ID.AddInteger(static_cast<int>(X)); 36 } 37 }; 38 } // end namespace llvm 39 40 namespace { 41 class VirtualCallChecker 42 : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> { 43 public: 44 // These are going to be null if the respective check is disabled. 45 mutable std::unique_ptr<BugType> BT_Pure, BT_Impure; 46 47 void checkBeginFunction(CheckerContext &C) const; 48 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; 49 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 50 51 private: 52 void registerCtorDtorCallInState(bool IsBeginFunction, 53 CheckerContext &C) const; 54 }; 55 } // end namespace 56 57 // GDM (generic data map) to the memregion of this for the ctor and dtor. 58 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState) 59 60 // The function to check if a callexpr is a virtual method call. 61 static bool isVirtualCall(const CallExpr *CE) { 62 bool CallIsNonVirtual = false; 63 64 if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { 65 // The member access is fully qualified (i.e., X::F). 66 // Treat this as a non-virtual call and do not warn. 67 if (CME->getQualifier()) 68 CallIsNonVirtual = true; 69 70 if (const Expr *Base = CME->getBase()) { 71 // The most derived class is marked final. 72 if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>()) 73 CallIsNonVirtual = true; 74 } 75 } 76 77 const CXXMethodDecl *MD = 78 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee()); 79 if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() && 80 !MD->getParent()->hasAttr<FinalAttr>()) 81 return true; 82 return false; 83 } 84 85 // The BeginFunction callback when enter a constructor or a destructor. 86 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const { 87 registerCtorDtorCallInState(true, C); 88 } 89 90 // The EndFunction callback when leave a constructor or a destructor. 91 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS, 92 CheckerContext &C) const { 93 registerCtorDtorCallInState(false, C); 94 } 95 96 void VirtualCallChecker::checkPreCall(const CallEvent &Call, 97 CheckerContext &C) const { 98 const auto MC = dyn_cast<CXXMemberCall>(&Call); 99 if (!MC) 100 return; 101 102 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 103 if (!MD) 104 return; 105 106 ProgramStateRef State = C.getState(); 107 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 108 if (!isVirtualCall(CE)) 109 return; 110 111 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion(); 112 const ObjectState *ObState = State->get<CtorDtorMap>(Reg); 113 if (!ObState) 114 return; 115 116 bool IsPure = MD->isPure(); 117 118 // At this point we're sure that we're calling a virtual method 119 // during construction or destruction, so we'll emit a report. 120 SmallString<128> Msg; 121 llvm::raw_svector_ostream OS(Msg); 122 OS << "Call to "; 123 if (IsPure) 124 OS << "pure "; 125 OS << "virtual method '" << MD->getParent()->getNameAsString() 126 << "::" << MD->getNameAsString() << "' during "; 127 if (*ObState == ObjectState::CtorCalled) 128 OS << "construction "; 129 else 130 OS << "destruction "; 131 if (IsPure) 132 OS << "has undefined behavior"; 133 else 134 OS << "bypasses virtual dispatch"; 135 136 ExplodedNode *N = 137 IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode(); 138 if (!N) 139 return; 140 141 const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure; 142 if (!BT) { 143 // The respective check is disabled. 144 return; 145 } 146 147 auto Report = std::make_unique<BugReport>(*BT, OS.str(), N); 148 C.emitReport(std::move(Report)); 149 } 150 151 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, 152 CheckerContext &C) const { 153 const auto *LCtx = C.getLocationContext(); 154 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl()); 155 if (!MD) 156 return; 157 158 ProgramStateRef State = C.getState(); 159 auto &SVB = C.getSValBuilder(); 160 161 // Enter a constructor, set the corresponding memregion be true. 162 if (isa<CXXConstructorDecl>(MD)) { 163 auto ThiSVal = 164 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); 165 const MemRegion *Reg = ThiSVal.getAsRegion(); 166 if (IsBeginFunction) 167 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled); 168 else 169 State = State->remove<CtorDtorMap>(Reg); 170 171 C.addTransition(State); 172 return; 173 } 174 175 // Enter a Destructor, set the corresponding memregion be true. 176 if (isa<CXXDestructorDecl>(MD)) { 177 auto ThiSVal = 178 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); 179 const MemRegion *Reg = ThiSVal.getAsRegion(); 180 if (IsBeginFunction) 181 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled); 182 else 183 State = State->remove<CtorDtorMap>(Reg); 184 185 C.addTransition(State); 186 return; 187 } 188 } 189 190 void ento::registerVirtualCallModeling(CheckerManager &Mgr) { 191 Mgr.registerChecker<VirtualCallChecker>(); 192 } 193 194 void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) { 195 auto *Chk = Mgr.getChecker<VirtualCallChecker>(); 196 Chk->BT_Pure = std::make_unique<BugType>( 197 Mgr.getCurrentCheckName(), "Pure virtual method call", 198 categories::CXXObjectLifecycle); 199 } 200 201 void ento::registerVirtualCallChecker(CheckerManager &Mgr) { 202 auto *Chk = Mgr.getChecker<VirtualCallChecker>(); 203 if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption( 204 Mgr.getCurrentCheckName(), "PureOnly")) { 205 Chk->BT_Impure = std::make_unique<BugType>( 206 Mgr.getCurrentCheckName(), "Unexpected loss of virtual dispatch", 207 categories::CXXObjectLifecycle); 208 } 209 } 210 211 bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) { 212 return LO.CPlusPlus; 213 } 214 215 bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) { 216 return LO.CPlusPlus; 217 } 218 219 bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) { 220 return LO.CPlusPlus; 221 } 222