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