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