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 // Member calls are always represented by a call-expression. 108 const auto *CE = cast<CallExpr>(Call.getOriginExpr()); 109 if (!isVirtualCall(CE)) 110 return; 111 112 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion(); 113 const ObjectState *ObState = State->get<CtorDtorMap>(Reg); 114 if (!ObState) 115 return; 116 117 bool IsPure = MD->isPure(); 118 119 // At this point we're sure that we're calling a virtual method 120 // during construction or destruction, so we'll emit a report. 121 SmallString<128> Msg; 122 llvm::raw_svector_ostream OS(Msg); 123 OS << "Call to "; 124 if (IsPure) 125 OS << "pure "; 126 OS << "virtual method '" << MD->getParent()->getNameAsString() 127 << "::" << MD->getNameAsString() << "' during "; 128 if (*ObState == ObjectState::CtorCalled) 129 OS << "construction "; 130 else 131 OS << "destruction "; 132 if (IsPure) 133 OS << "has undefined behavior"; 134 else 135 OS << "bypasses virtual dispatch"; 136 137 ExplodedNode *N = 138 IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode(); 139 if (!N) 140 return; 141 142 const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure; 143 if (!BT) { 144 // The respective check is disabled. 145 return; 146 } 147 148 auto Report = std::make_unique<BugReport>(*BT, OS.str(), N); 149 C.emitReport(std::move(Report)); 150 } 151 152 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, 153 CheckerContext &C) const { 154 const auto *LCtx = C.getLocationContext(); 155 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl()); 156 if (!MD) 157 return; 158 159 ProgramStateRef State = C.getState(); 160 auto &SVB = C.getSValBuilder(); 161 162 // Enter a constructor, set the corresponding memregion be true. 163 if (isa<CXXConstructorDecl>(MD)) { 164 auto ThiSVal = 165 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); 166 const MemRegion *Reg = ThiSVal.getAsRegion(); 167 if (IsBeginFunction) 168 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled); 169 else 170 State = State->remove<CtorDtorMap>(Reg); 171 172 C.addTransition(State); 173 return; 174 } 175 176 // Enter a Destructor, set the corresponding memregion be true. 177 if (isa<CXXDestructorDecl>(MD)) { 178 auto ThiSVal = 179 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); 180 const MemRegion *Reg = ThiSVal.getAsRegion(); 181 if (IsBeginFunction) 182 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled); 183 else 184 State = State->remove<CtorDtorMap>(Reg); 185 186 C.addTransition(State); 187 return; 188 } 189 } 190 191 void ento::registerVirtualCallModeling(CheckerManager &Mgr) { 192 Mgr.registerChecker<VirtualCallChecker>(); 193 } 194 195 void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) { 196 auto *Chk = Mgr.getChecker<VirtualCallChecker>(); 197 Chk->BT_Pure = std::make_unique<BugType>( 198 Mgr.getCurrentCheckName(), "Pure virtual method call", 199 categories::CXXObjectLifecycle); 200 } 201 202 void ento::registerVirtualCallChecker(CheckerManager &Mgr) { 203 auto *Chk = Mgr.getChecker<VirtualCallChecker>(); 204 if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption( 205 Mgr.getCurrentCheckName(), "PureOnly")) { 206 Chk->BT_Impure = std::make_unique<BugType>( 207 Mgr.getCurrentCheckName(), "Unexpected loss of virtual dispatch", 208 categories::CXXObjectLifecycle); 209 } 210 } 211 212 bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) { 213 return LO.CPlusPlus; 214 } 215 216 bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) { 217 return LO.CPlusPlus; 218 } 219 220 bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) { 221 return LO.CPlusPlus; 222 } 223