Coverage Report

Created: 2019-07-24 05:18

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