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 function 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 mutable std::unique_ptr<BugType> BT; 44 45 public: 46 // The flag to determine if pure virtual functions should be issued only. 47 DefaultBool IsPureOnly; 48 49 void checkBeginFunction(CheckerContext &C) const; 50 void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; 51 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 52 53 private: 54 void registerCtorDtorCallInState(bool IsBeginFunction, 55 CheckerContext &C) const; 56 void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg, 57 CheckerContext &C) const; 58 59 class VirtualBugVisitor : public BugReporterVisitor { 60 private: 61 const MemRegion *ObjectRegion; 62 bool Found; 63 64 public: 65 VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {} 66 67 void Profile(llvm::FoldingSetNodeID &ID) const override { 68 static int X = 0; 69 ID.AddPointer(&X); 70 ID.AddPointer(ObjectRegion); 71 } 72 73 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, 74 BugReporterContext &BRC, 75 BugReport &BR) override; 76 }; 77 }; 78 } // end namespace 79 80 // GDM (generic data map) to the memregion of this for the ctor and dtor. 81 REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState) 82 83 PathDiagnosticPieceRef VirtualCallChecker::VirtualBugVisitor::VisitNode( 84 const ExplodedNode *N, BugReporterContext &BRC, BugReport &) { 85 // We need the last ctor/dtor which call the virtual function. 86 // The visitor walks the ExplodedGraph backwards. 87 if (Found) 88 return nullptr; 89 90 ProgramStateRef State = N->getState(); 91 const LocationContext *LCtx = N->getLocationContext(); 92 const CXXConstructorDecl *CD = 93 dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl()); 94 const CXXDestructorDecl *DD = 95 dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl()); 96 97 if (!CD && !DD) 98 return nullptr; 99 100 ProgramStateManager &PSM = State->getStateManager(); 101 auto &SVB = PSM.getSValBuilder(); 102 const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl()); 103 if (!MD) 104 return nullptr; 105 auto ThiSVal = 106 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); 107 const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion(); 108 if (!Reg) 109 return nullptr; 110 if (Reg != ObjectRegion) 111 return nullptr; 112 113 const Stmt *S = PathDiagnosticLocation::getStmt(N); 114 if (!S) 115 return nullptr; 116 Found = true; 117 118 std::string InfoText; 119 if (CD) 120 InfoText = "This constructor of an object of type '" + 121 CD->getNameAsString() + 122 "' has not returned when the virtual method was called"; 123 else 124 InfoText = "This destructor of an object of type '" + 125 DD->getNameAsString() + 126 "' has not returned when the virtual method was called"; 127 128 // Generate the extra diagnostic. 129 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 130 N->getLocationContext()); 131 return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true); 132 } 133 134 // The function to check if a callexpr is a virtual function. 135 static bool isVirtualCall(const CallExpr *CE) { 136 bool CallIsNonVirtual = false; 137 138 if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { 139 // The member access is fully qualified (i.e., X::F). 140 // Treat this as a non-virtual call and do not warn. 141 if (CME->getQualifier()) 142 CallIsNonVirtual = true; 143 144 if (const Expr *Base = CME->getBase()) { 145 // The most derived class is marked final. 146 if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>()) 147 CallIsNonVirtual = true; 148 } 149 } 150 151 const CXXMethodDecl *MD = 152 dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee()); 153 if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() && 154 !MD->getParent()->hasAttr<FinalAttr>()) 155 return true; 156 return false; 157 } 158 159 // The BeginFunction callback when enter a constructor or a destructor. 160 void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const { 161 registerCtorDtorCallInState(true, C); 162 } 163 164 // The EndFunction callback when leave a constructor or a destructor. 165 void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS, 166 CheckerContext &C) const { 167 registerCtorDtorCallInState(false, C); 168 } 169 170 void VirtualCallChecker::checkPreCall(const CallEvent &Call, 171 CheckerContext &C) const { 172 const auto MC = dyn_cast<CXXMemberCall>(&Call); 173 if (!MC) 174 return; 175 176 const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); 177 if (!MD) 178 return; 179 ProgramStateRef State = C.getState(); 180 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 181 182 if (IsPureOnly && !MD->isPure()) 183 return; 184 if (!isVirtualCall(CE)) 185 return; 186 187 const MemRegion *Reg = MC->getCXXThisVal().getAsRegion(); 188 const ObjectState *ObState = State->get<CtorDtorMap>(Reg); 189 if (!ObState) 190 return; 191 // Check if a virtual method is called. 192 // The GDM of constructor and destructor should be true. 193 if (*ObState == ObjectState::CtorCalled) { 194 if (IsPureOnly && MD->isPure()) 195 reportBug("Call to pure virtual function during construction", true, Reg, 196 C); 197 else if (!MD->isPure()) 198 reportBug("Call to virtual function during construction", false, Reg, C); 199 else 200 reportBug("Call to pure virtual function during construction", false, Reg, 201 C); 202 } 203 204 if (*ObState == ObjectState::DtorCalled) { 205 if (IsPureOnly && MD->isPure()) 206 reportBug("Call to pure virtual function during destruction", true, Reg, 207 C); 208 else if (!MD->isPure()) 209 reportBug("Call to virtual function during destruction", false, Reg, C); 210 else 211 reportBug("Call to pure virtual function during construction", false, Reg, 212 C); 213 } 214 } 215 216 void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, 217 CheckerContext &C) const { 218 const auto *LCtx = C.getLocationContext(); 219 const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl()); 220 if (!MD) 221 return; 222 223 ProgramStateRef State = C.getState(); 224 auto &SVB = C.getSValBuilder(); 225 226 // Enter a constructor, set the corresponding memregion be true. 227 if (isa<CXXConstructorDecl>(MD)) { 228 auto ThiSVal = 229 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); 230 const MemRegion *Reg = ThiSVal.getAsRegion(); 231 if (IsBeginFunction) 232 State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled); 233 else 234 State = State->remove<CtorDtorMap>(Reg); 235 236 C.addTransition(State); 237 return; 238 } 239 240 // Enter a Destructor, set the corresponding memregion be true. 241 if (isa<CXXDestructorDecl>(MD)) { 242 auto ThiSVal = 243 State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); 244 const MemRegion *Reg = ThiSVal.getAsRegion(); 245 if (IsBeginFunction) 246 State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled); 247 else 248 State = State->remove<CtorDtorMap>(Reg); 249 250 C.addTransition(State); 251 return; 252 } 253 } 254 255 void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink, 256 const MemRegion *Reg, 257 CheckerContext &C) const { 258 ExplodedNode *N; 259 if (IsSink) 260 N = C.generateErrorNode(); 261 else 262 N = C.generateNonFatalErrorNode(); 263 264 if (!N) 265 return; 266 if (!BT) 267 BT.reset(new BugType( 268 this, "Call to virtual function during construction or destruction", 269 "C++ Object Lifecycle")); 270 271 auto Reporter = std::make_unique<BugReport>(*BT, Msg, N); 272 Reporter->addVisitor(std::make_unique<VirtualBugVisitor>(Reg)); 273 C.emitReport(std::move(Reporter)); 274 } 275 276 void ento::registerVirtualCallChecker(CheckerManager &mgr) { 277 VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>(); 278 279 checker->IsPureOnly = 280 mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "PureOnly"); 281 } 282 283 bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) { 284 return true; 285 } 286