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