Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
Line
Count
Source (jump to first uncovered line)
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 method calls during
10
//  construction or destruction of C++ objects.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "clang/AST/Attr.h"
15
#include "clang/AST/DeclCXX.h"
16
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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
2.87k
  static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
36
2.87k
    ID.AddInteger(static_cast<int>(X));
37
2.87k
  }
38
};
39
} // end namespace llvm
40
41
namespace {
42
class VirtualCallChecker
43
    : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
44
public:
45
  // These are going to be null if the respective check is disabled.
46
  mutable std::unique_ptr<BugType> BT_Pure, BT_Impure;
47
  bool ShowFixIts = false;
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
};
57
} // end namespace
58
59
// GDM (generic data map) to the memregion of this for the ctor and dtor.
60
REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
61
62
// The function to check if a callexpr is a virtual method call.
63
3.72k
static bool isVirtualCall(const CallExpr *CE) {
64
3.72k
  bool CallIsNonVirtual = false;
65
66
3.72k
  if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
67
    // The member access is fully qualified (i.e., X::F).
68
    // Treat this as a non-virtual call and do not warn.
69
3.71k
    if (CME->getQualifier())
70
31
      CallIsNonVirtual = true;
71
72
3.71k
    if (const Expr *Base = CME->getBase()) {
73
      // The most derived class is marked final.
74
3.71k
      if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
75
10
        CallIsNonVirtual = true;
76
3.71k
    }
77
3.71k
  }
78
79
3.72k
  const CXXMethodDecl *MD =
80
3.72k
      dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
81
3.72k
  if (MD && 
MD->isVirtual()3.71k
&&
!CallIsNonVirtual202
&&
!MD->hasAttr<FinalAttr>()161
&&
82
3.72k
      
!MD->getParent()->hasAttr<FinalAttr>()146
)
83
146
    return true;
84
3.58k
  return false;
85
3.72k
}
86
87
// The BeginFunction callback when enter a constructor or a destructor.
88
9.51k
void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
89
9.51k
  registerCtorDtorCallInState(true, C);
90
9.51k
}
91
92
// The EndFunction callback when leave a constructor or a destructor.
93
void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
94
15.7k
                                          CheckerContext &C) const {
95
15.7k
  registerCtorDtorCallInState(false, C);
96
15.7k
}
97
98
void VirtualCallChecker::checkPreCall(const CallEvent &Call,
99
31.4k
                                      CheckerContext &C) const {
100
31.4k
  const auto MC = dyn_cast<CXXMemberCall>(&Call);
101
31.4k
  if (!MC)
102
27.6k
    return;
103
104
3.72k
  const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
105
3.72k
  if (!MD)
106
0
    return;
107
108
3.72k
  ProgramStateRef State = C.getState();
109
  // Member calls are always represented by a call-expression.
110
3.72k
  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
111
3.72k
  if (!isVirtualCall(CE))
112
3.58k
    return;
113
114
146
  const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
115
146
  const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
116
146
  if (!ObState)
117
35
    return;
118
119
111
  bool IsPure = MD->isPure();
120
121
  // At this point we're sure that we're calling a virtual method
122
  // during construction or destruction, so we'll emit a report.
123
111
  SmallString<128> Msg;
124
111
  llvm::raw_svector_ostream OS(Msg);
125
111
  OS << "Call to ";
126
111
  if (IsPure)
127
8
    OS << "pure ";
128
111
  OS << "virtual method '" << MD->getParent()->getDeclName()
129
111
     << "::" << MD->getDeclName() << "' during ";
130
111
  if (*ObState == ObjectState::CtorCalled)
131
78
    OS << "construction ";
132
33
  else
133
33
    OS << "destruction ";
134
111
  if (IsPure)
135
8
    OS << "has undefined behavior";
136
103
  else
137
103
    OS << "bypasses virtual dispatch";
138
139
111
  ExplodedNode *N =
140
111
      IsPure ? 
C.generateErrorNode()8
:
C.generateNonFatalErrorNode()103
;
141
111
  if (!N)
142
0
    return;
143
144
111
  const std::unique_ptr<BugType> &BT = IsPure ? 
BT_Pure8
:
BT_Impure103
;
145
111
  if (!BT) {
146
    // The respective check is disabled.
147
64
    return;
148
64
  }
149
150
47
  auto Report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
151
152
47
  if (ShowFixIts && 
!IsPure2
) {
153
    // FIXME: These hints are valid only when the virtual call is made
154
    // directly from the constructor/destructor. Otherwise the dispatch
155
    // will work just fine from other callees, and the fix may break
156
    // the otherwise correct program.
157
2
    FixItHint Fixit = FixItHint::CreateInsertion(
158
2
        CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");
159
2
    Report->addFixItHint(Fixit);
160
2
  }
161
162
47
  C.emitReport(std::move(Report));
163
47
}
164
165
void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
166
25.2k
                                                     CheckerContext &C) const {
167
25.2k
  const auto *LCtx = C.getLocationContext();
168
25.2k
  const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
169
25.2k
  if (!MD)
170
15.3k
    return;
171
172
9.92k
  ProgramStateRef State = C.getState();
173
9.92k
  auto &SVB = C.getSValBuilder();
174
175
  // Enter a constructor, set the corresponding memregion be true.
176
9.92k
  if (isa<CXXConstructorDecl>(MD)) {
177
4.39k
    auto ThiSVal =
178
4.39k
        State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
179
4.39k
    const MemRegion *Reg = ThiSVal.getAsRegion();
180
4.39k
    if (IsBeginFunction)
181
2.20k
      State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
182
2.19k
    else
183
2.19k
      State = State->remove<CtorDtorMap>(Reg);
184
185
4.39k
    C.addTransition(State);
186
4.39k
    return;
187
4.39k
  }
188
189
  // Enter a Destructor, set the corresponding memregion be true.
190
5.53k
  if (isa<CXXDestructorDecl>(MD)) {
191
603
    auto ThiSVal =
192
603
        State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
193
603
    const MemRegion *Reg = ThiSVal.getAsRegion();
194
603
    if (IsBeginFunction)
195
310
      State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
196
293
    else
197
293
      State = State->remove<CtorDtorMap>(Reg);
198
199
603
    C.addTransition(State);
200
603
    return;
201
603
  }
202
5.53k
}
203
204
73
void ento::registerVirtualCallModeling(CheckerManager &Mgr) {
205
73
  Mgr.registerChecker<VirtualCallChecker>();
206
73
}
207
208
68
void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) {
209
68
  auto *Chk = Mgr.getChecker<VirtualCallChecker>();
210
68
  Chk->BT_Pure = std::make_unique<BugType>(Mgr.getCurrentCheckerName(),
211
68
                                           "Pure virtual method call",
212
68
                                           categories::CXXObjectLifecycle);
213
68
}
214
215
8
void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
216
8
  auto *Chk = Mgr.getChecker<VirtualCallChecker>();
217
8
  if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption(
218
8
          Mgr.getCurrentCheckerName(), "PureOnly")) {
219
6
    Chk->BT_Impure = std::make_unique<BugType>(
220
6
        Mgr.getCurrentCheckerName(), "Unexpected loss of virtual dispatch",
221
6
        categories::CXXObjectLifecycle);
222
6
    Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
223
6
        Mgr.getCurrentCheckerName(), "ShowFixIts");
224
6
  }
225
8
}
226
227
282
bool ento::shouldRegisterVirtualCallModeling(const CheckerManager &mgr) {
228
282
  const LangOptions &LO = mgr.getLangOpts();
229
282
  return LO.CPlusPlus;
230
282
}
231
232
136
bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &mgr) {
233
136
  const LangOptions &LO = mgr.getLangOpts();
234
136
  return LO.CPlusPlus;
235
136
}
236
237
16
bool ento::shouldRegisterVirtualCallChecker(const CheckerManager &mgr) {
238
16
  const LangOptions &LO = mgr.getLangOpts();
239
16
  return LO.CPlusPlus;
240
16
}