xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
10b57cec5SDimitry Andric //=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
9a7dea167SDimitry Andric //  This file defines a checker that checks virtual method calls during
100b57cec5SDimitry Andric //  construction or destruction of C++ objects.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
14480093f4SDimitry Andric #include "clang/AST/Attr.h"
150b57cec5SDimitry Andric #include "clang/AST/DeclCXX.h"
16480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
170b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric using namespace clang;
260b57cec5SDimitry Andric using namespace ento;
270b57cec5SDimitry Andric 
280b57cec5SDimitry Andric namespace {
290b57cec5SDimitry Andric enum class ObjectState : bool { CtorCalled, DtorCalled };
300b57cec5SDimitry Andric } // end namespace
310b57cec5SDimitry Andric   // FIXME: Ascending over StackFrameContext maybe another method.
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric namespace llvm {
340b57cec5SDimitry Andric template <> struct FoldingSetTrait<ObjectState> {
Profilellvm::FoldingSetTrait350b57cec5SDimitry Andric   static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
360b57cec5SDimitry Andric     ID.AddInteger(static_cast<int>(X));
370b57cec5SDimitry Andric   }
380b57cec5SDimitry Andric };
390b57cec5SDimitry Andric } // end namespace llvm
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric namespace {
420b57cec5SDimitry Andric class VirtualCallChecker
430b57cec5SDimitry Andric     : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
440b57cec5SDimitry Andric public:
45a7dea167SDimitry Andric   // These are going to be null if the respective check is disabled.
46a7dea167SDimitry Andric   mutable std::unique_ptr<BugType> BT_Pure, BT_Impure;
47a7dea167SDimitry Andric   bool ShowFixIts = false;
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric   void checkBeginFunction(CheckerContext &C) const;
500b57cec5SDimitry Andric   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
510b57cec5SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
520b57cec5SDimitry Andric 
530b57cec5SDimitry Andric private:
540b57cec5SDimitry Andric   void registerCtorDtorCallInState(bool IsBeginFunction,
550b57cec5SDimitry Andric                                    CheckerContext &C) const;
560b57cec5SDimitry Andric };
570b57cec5SDimitry Andric } // end namespace
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric // GDM (generic data map) to the memregion of this for the ctor and dtor.
REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap,const MemRegion *,ObjectState)600b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
610b57cec5SDimitry Andric 
62a7dea167SDimitry Andric // The function to check if a callexpr is a virtual method call.
630b57cec5SDimitry Andric static bool isVirtualCall(const CallExpr *CE) {
640b57cec5SDimitry Andric   bool CallIsNonVirtual = false;
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
670b57cec5SDimitry Andric     // The member access is fully qualified (i.e., X::F).
680b57cec5SDimitry Andric     // Treat this as a non-virtual call and do not warn.
690b57cec5SDimitry Andric     if (CME->getQualifier())
700b57cec5SDimitry Andric       CallIsNonVirtual = true;
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric     if (const Expr *Base = CME->getBase()) {
730b57cec5SDimitry Andric       // The most derived class is marked final.
740b57cec5SDimitry Andric       if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
750b57cec5SDimitry Andric         CallIsNonVirtual = true;
760b57cec5SDimitry Andric     }
770b57cec5SDimitry Andric   }
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric   const CXXMethodDecl *MD =
800b57cec5SDimitry Andric       dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
810b57cec5SDimitry Andric   if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
820b57cec5SDimitry Andric       !MD->getParent()->hasAttr<FinalAttr>())
830b57cec5SDimitry Andric     return true;
840b57cec5SDimitry Andric   return false;
850b57cec5SDimitry Andric }
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric // The BeginFunction callback when enter a constructor or a destructor.
checkBeginFunction(CheckerContext & C) const880b57cec5SDimitry Andric void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
890b57cec5SDimitry Andric   registerCtorDtorCallInState(true, C);
900b57cec5SDimitry Andric }
910b57cec5SDimitry Andric 
920b57cec5SDimitry Andric // The EndFunction callback when leave a constructor or a destructor.
checkEndFunction(const ReturnStmt * RS,CheckerContext & C) const930b57cec5SDimitry Andric void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
940b57cec5SDimitry Andric                                           CheckerContext &C) const {
950b57cec5SDimitry Andric   registerCtorDtorCallInState(false, C);
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric 
checkPreCall(const CallEvent & Call,CheckerContext & C) const980b57cec5SDimitry Andric void VirtualCallChecker::checkPreCall(const CallEvent &Call,
990b57cec5SDimitry Andric                                       CheckerContext &C) const {
1000b57cec5SDimitry Andric   const auto MC = dyn_cast<CXXMemberCall>(&Call);
1010b57cec5SDimitry Andric   if (!MC)
1020b57cec5SDimitry Andric     return;
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
1050b57cec5SDimitry Andric   if (!MD)
1060b57cec5SDimitry Andric     return;
1070b57cec5SDimitry Andric 
108a7dea167SDimitry Andric   ProgramStateRef State = C.getState();
109a7dea167SDimitry Andric   // Member calls are always represented by a call-expression.
110a7dea167SDimitry Andric   const auto *CE = cast<CallExpr>(Call.getOriginExpr());
1110b57cec5SDimitry Andric   if (!isVirtualCall(CE))
1120b57cec5SDimitry Andric     return;
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric   const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
1150b57cec5SDimitry Andric   const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
1160b57cec5SDimitry Andric   if (!ObState)
1170b57cec5SDimitry Andric     return;
118a7dea167SDimitry Andric 
119*7a6dacacSDimitry Andric   bool IsPure = MD->isPureVirtual();
120a7dea167SDimitry Andric 
121a7dea167SDimitry Andric   // At this point we're sure that we're calling a virtual method
122a7dea167SDimitry Andric   // during construction or destruction, so we'll emit a report.
123a7dea167SDimitry Andric   SmallString<128> Msg;
124a7dea167SDimitry Andric   llvm::raw_svector_ostream OS(Msg);
125a7dea167SDimitry Andric   OS << "Call to ";
126a7dea167SDimitry Andric   if (IsPure)
127a7dea167SDimitry Andric     OS << "pure ";
128e8d8bef9SDimitry Andric   OS << "virtual method '" << MD->getParent()->getDeclName()
129e8d8bef9SDimitry Andric      << "::" << MD->getDeclName() << "' during ";
130a7dea167SDimitry Andric   if (*ObState == ObjectState::CtorCalled)
131a7dea167SDimitry Andric     OS << "construction ";
1320b57cec5SDimitry Andric   else
133a7dea167SDimitry Andric     OS << "destruction ";
134a7dea167SDimitry Andric   if (IsPure)
135a7dea167SDimitry Andric     OS << "has undefined behavior";
136a7dea167SDimitry Andric   else
137a7dea167SDimitry Andric     OS << "bypasses virtual dispatch";
138a7dea167SDimitry Andric 
139a7dea167SDimitry Andric   ExplodedNode *N =
140a7dea167SDimitry Andric       IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode();
141a7dea167SDimitry Andric   if (!N)
142a7dea167SDimitry Andric     return;
143a7dea167SDimitry Andric 
144a7dea167SDimitry Andric   const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure;
145a7dea167SDimitry Andric   if (!BT) {
146a7dea167SDimitry Andric     // The respective check is disabled.
147a7dea167SDimitry Andric     return;
1480b57cec5SDimitry Andric   }
1490b57cec5SDimitry Andric 
150a7dea167SDimitry Andric   auto Report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
151a7dea167SDimitry Andric 
152a7dea167SDimitry Andric   if (ShowFixIts && !IsPure) {
153a7dea167SDimitry Andric     // FIXME: These hints are valid only when the virtual call is made
154a7dea167SDimitry Andric     // directly from the constructor/destructor. Otherwise the dispatch
155a7dea167SDimitry Andric     // will work just fine from other callees, and the fix may break
156a7dea167SDimitry Andric     // the otherwise correct program.
157a7dea167SDimitry Andric     FixItHint Fixit = FixItHint::CreateInsertion(
158a7dea167SDimitry Andric         CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");
159a7dea167SDimitry Andric     Report->addFixItHint(Fixit);
1600b57cec5SDimitry Andric   }
161a7dea167SDimitry Andric 
162a7dea167SDimitry Andric   C.emitReport(std::move(Report));
1630b57cec5SDimitry Andric }
1640b57cec5SDimitry Andric 
registerCtorDtorCallInState(bool IsBeginFunction,CheckerContext & C) const1650b57cec5SDimitry Andric void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
1660b57cec5SDimitry Andric                                                      CheckerContext &C) const {
1670b57cec5SDimitry Andric   const auto *LCtx = C.getLocationContext();
1680b57cec5SDimitry Andric   const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
1690b57cec5SDimitry Andric   if (!MD)
1700b57cec5SDimitry Andric     return;
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric   ProgramStateRef State = C.getState();
1730b57cec5SDimitry Andric   auto &SVB = C.getSValBuilder();
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric   // Enter a constructor, set the corresponding memregion be true.
1760b57cec5SDimitry Andric   if (isa<CXXConstructorDecl>(MD)) {
1770b57cec5SDimitry Andric     auto ThiSVal =
1780b57cec5SDimitry Andric         State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
1790b57cec5SDimitry Andric     const MemRegion *Reg = ThiSVal.getAsRegion();
1800b57cec5SDimitry Andric     if (IsBeginFunction)
1810b57cec5SDimitry Andric       State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
1820b57cec5SDimitry Andric     else
1830b57cec5SDimitry Andric       State = State->remove<CtorDtorMap>(Reg);
1840b57cec5SDimitry Andric 
1850b57cec5SDimitry Andric     C.addTransition(State);
1860b57cec5SDimitry Andric     return;
1870b57cec5SDimitry Andric   }
1880b57cec5SDimitry Andric 
1890b57cec5SDimitry Andric   // Enter a Destructor, set the corresponding memregion be true.
1900b57cec5SDimitry Andric   if (isa<CXXDestructorDecl>(MD)) {
1910b57cec5SDimitry Andric     auto ThiSVal =
1920b57cec5SDimitry Andric         State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
1930b57cec5SDimitry Andric     const MemRegion *Reg = ThiSVal.getAsRegion();
1940b57cec5SDimitry Andric     if (IsBeginFunction)
1950b57cec5SDimitry Andric       State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
1960b57cec5SDimitry Andric     else
1970b57cec5SDimitry Andric       State = State->remove<CtorDtorMap>(Reg);
1980b57cec5SDimitry Andric 
1990b57cec5SDimitry Andric     C.addTransition(State);
2000b57cec5SDimitry Andric     return;
2010b57cec5SDimitry Andric   }
2020b57cec5SDimitry Andric }
2030b57cec5SDimitry Andric 
registerVirtualCallModeling(CheckerManager & Mgr)204a7dea167SDimitry Andric void ento::registerVirtualCallModeling(CheckerManager &Mgr) {
205a7dea167SDimitry Andric   Mgr.registerChecker<VirtualCallChecker>();
2060b57cec5SDimitry Andric }
2070b57cec5SDimitry Andric 
registerPureVirtualCallChecker(CheckerManager & Mgr)208a7dea167SDimitry Andric void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) {
209a7dea167SDimitry Andric   auto *Chk = Mgr.getChecker<VirtualCallChecker>();
210a7dea167SDimitry Andric   Chk->BT_Pure = std::make_unique<BugType>(Mgr.getCurrentCheckerName(),
211a7dea167SDimitry Andric                                            "Pure virtual method call",
212a7dea167SDimitry Andric                                            categories::CXXObjectLifecycle);
213a7dea167SDimitry Andric }
2140b57cec5SDimitry Andric 
registerVirtualCallChecker(CheckerManager & Mgr)215a7dea167SDimitry Andric void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
216a7dea167SDimitry Andric   auto *Chk = Mgr.getChecker<VirtualCallChecker>();
217a7dea167SDimitry Andric   if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption(
218a7dea167SDimitry Andric           Mgr.getCurrentCheckerName(), "PureOnly")) {
219a7dea167SDimitry Andric     Chk->BT_Impure = std::make_unique<BugType>(
220a7dea167SDimitry Andric         Mgr.getCurrentCheckerName(), "Unexpected loss of virtual dispatch",
221a7dea167SDimitry Andric         categories::CXXObjectLifecycle);
222a7dea167SDimitry Andric     Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
223a7dea167SDimitry Andric         Mgr.getCurrentCheckerName(), "ShowFixIts");
224a7dea167SDimitry Andric   }
225a7dea167SDimitry Andric }
226a7dea167SDimitry Andric 
shouldRegisterVirtualCallModeling(const CheckerManager & mgr)2275ffd83dbSDimitry Andric bool ento::shouldRegisterVirtualCallModeling(const CheckerManager &mgr) {
2285ffd83dbSDimitry Andric   const LangOptions &LO = mgr.getLangOpts();
229a7dea167SDimitry Andric   return LO.CPlusPlus;
230a7dea167SDimitry Andric }
231a7dea167SDimitry Andric 
shouldRegisterPureVirtualCallChecker(const CheckerManager & mgr)2325ffd83dbSDimitry Andric bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &mgr) {
2335ffd83dbSDimitry Andric   const LangOptions &LO = mgr.getLangOpts();
234a7dea167SDimitry Andric   return LO.CPlusPlus;
2350b57cec5SDimitry Andric }
2360b57cec5SDimitry Andric 
shouldRegisterVirtualCallChecker(const CheckerManager & mgr)2375ffd83dbSDimitry Andric bool ento::shouldRegisterVirtualCallChecker(const CheckerManager &mgr) {
2385ffd83dbSDimitry Andric   const LangOptions &LO = mgr.getLangOpts();
239a7dea167SDimitry Andric   return LO.CPlusPlus;
2400b57cec5SDimitry Andric }
241