6
6
//
7
7
// ===----------------------------------------------------------------------===//
8
8
//
9
- // This file defines a checker that checks virtual function calls during
9
+ // This file defines a checker that checks virtual method calls during
10
10
// construction or destruction of C++ objects.
11
11
//
12
12
// ===----------------------------------------------------------------------===//
@@ -40,11 +40,9 @@ template <> struct FoldingSetTrait<ObjectState> {
40
40
namespace {
41
41
class VirtualCallChecker
42
42
: public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
43
- mutable std::unique_ptr<BugType> BT;
44
-
45
43
public:
46
- // The flag to determine if pure virtual functions should be issued only .
47
- DefaultBool IsPureOnly ;
44
+ // These are going to be null if the respective check is disabled .
45
+ mutable std::unique_ptr<BugType> BT_Pure, BT_Impure ;
48
46
49
47
void checkBeginFunction (CheckerContext &C) const ;
50
48
void checkEndFunction (const ReturnStmt *RS, CheckerContext &C) const ;
@@ -53,85 +51,13 @@ class VirtualCallChecker
53
51
private:
54
52
void registerCtorDtorCallInState (bool IsBeginFunction,
55
53
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
- VirtualBugVisitor (const MemRegion *R) : ObjectRegion(R), Found(false ) {}
66
-
67
- void Profile (llvm::FoldingSetNodeID &ID) const override {
68
- static int X = 0 ;
69
- ID.AddPointer (&X);
70
- ID.AddPointer (ObjectRegion);
71
- }
72
-
73
- PathDiagnosticPieceRef VisitNode (const ExplodedNode *N,
74
- BugReporterContext &BRC,
75
- BugReport &BR) override ;
76
- };
77
54
};
78
55
} // end namespace
79
56
80
57
// GDM (generic data map) to the memregion of this for the ctor and dtor.
81
58
REGISTER_MAP_WITH_PROGRAMSTATE (CtorDtorMap, const MemRegion *, ObjectState)
82
59
83
- PathDiagnosticPieceRef VirtualCallChecker::VirtualBugVisitor::VisitNode(
84
- const ExplodedNode *N, BugReporterContext &BRC, BugReport &) {
85
- // We need the last ctor/dtor which call the virtual function.
86
- // The visitor walks the ExplodedGraph backwards.
87
- if (Found)
88
- return nullptr ;
89
-
90
- ProgramStateRef State = N->getState ();
91
- const LocationContext *LCtx = N->getLocationContext ();
92
- const CXXConstructorDecl *CD =
93
- dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl ());
94
- const CXXDestructorDecl *DD =
95
- dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl ());
96
-
97
- if (!CD && !DD)
98
- return nullptr ;
99
-
100
- ProgramStateManager &PSM = State->getStateManager ();
101
- auto &SVB = PSM.getSValBuilder ();
102
- const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl ());
103
- if (!MD)
104
- return nullptr ;
105
- auto ThiSVal =
106
- State->getSVal (SVB.getCXXThis (MD, LCtx->getStackFrame ()));
107
- const MemRegion *Reg = ThiSVal.castAs <loc::MemRegionVal>().getRegion ();
108
- if (!Reg)
109
- return nullptr ;
110
- if (Reg != ObjectRegion)
111
- return nullptr ;
112
-
113
- const Stmt *S = PathDiagnosticLocation::getStmt (N);
114
- if (!S)
115
- return nullptr ;
116
- Found = true ;
117
-
118
- std::string InfoText;
119
- if (CD)
120
- InfoText = " This constructor of an object of type '" +
121
- CD->getNameAsString () +
122
- " ' has not returned when the virtual method was called" ;
123
- else
124
- InfoText = " This destructor of an object of type '" +
125
- DD->getNameAsString () +
126
- " ' has not returned when the virtual method was called" ;
127
-
128
- // Generate the extra diagnostic.
129
- PathDiagnosticLocation Pos (S, BRC.getSourceManager (),
130
- N->getLocationContext ());
131
- return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true );
132
- }
133
-
134
- // The function to check if a callexpr is a virtual function.
60
+ // The function to check if a callexpr is a virtual method call.
135
61
static bool isVirtualCall(const CallExpr *CE) {
136
62
bool CallIsNonVirtual = false ;
137
63
@@ -176,41 +102,50 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call,
176
102
const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl ());
177
103
if (!MD)
178
104
return ;
105
+
179
106
ProgramStateRef State = C.getState ();
180
107
const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr ());
181
-
182
- if (IsPureOnly && !MD->isPure ())
183
- return ;
184
108
if (!isVirtualCall (CE))
185
109
return ;
186
110
187
111
const MemRegion *Reg = MC->getCXXThisVal ().getAsRegion ();
188
112
const ObjectState *ObState = State->get <CtorDtorMap>(Reg);
189
113
if (!ObState)
190
114
return ;
191
- // Check if a virtual method is called.
192
- // The GDM of constructor and destructor should be true.
193
- if (*ObState == ObjectState::CtorCalled) {
194
- if (IsPureOnly && MD->isPure ())
195
- reportBug (" Call to pure virtual function during construction" , true , Reg,
196
- C);
197
- else if (!MD->isPure ())
198
- reportBug (" Call to virtual function during construction" , false , Reg, C);
199
- else
200
- reportBug (" Call to pure virtual function during construction" , false , Reg,
201
- C);
202
- }
203
115
204
- if (*ObState == ObjectState::DtorCalled) {
205
- if (IsPureOnly && MD->isPure ())
206
- reportBug (" Call to pure virtual function during destruction" , true , Reg,
207
- C);
208
- else if (!MD->isPure ())
209
- reportBug (" Call to virtual function during destruction" , false , Reg, C);
210
- else
211
- reportBug (" Call to pure virtual function during construction" , false , Reg,
212
- C);
116
+ bool IsPure = MD->isPure ();
117
+
118
+ // At this point we're sure that we're calling a virtual method
119
+ // during construction or destruction, so we'll emit a report.
120
+ SmallString<128 > Msg;
121
+ llvm::raw_svector_ostream OS (Msg);
122
+ OS << " Call to " ;
123
+ if (IsPure)
124
+ OS << " pure " ;
125
+ OS << " virtual method '" << MD->getParent ()->getNameAsString ()
126
+ << " ::" << MD->getNameAsString () << " ' during " ;
127
+ if (*ObState == ObjectState::CtorCalled)
128
+ OS << " construction " ;
129
+ else
130
+ OS << " destruction " ;
131
+ if (IsPure)
132
+ OS << " has undefined behavior" ;
133
+ else
134
+ OS << " bypasses virtual dispatch" ;
135
+
136
+ ExplodedNode *N =
137
+ IsPure ? C.generateErrorNode () : C.generateNonFatalErrorNode ();
138
+ if (!N)
139
+ return ;
140
+
141
+ const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure;
142
+ if (!BT) {
143
+ // The respective check is disabled.
144
+ return ;
213
145
}
146
+
147
+ auto Report = std::make_unique<BugReport>(*BT, OS.str (), N);
148
+ C.emitReport (std::move (Report));
214
149
}
215
150
216
151
void VirtualCallChecker::registerCtorDtorCallInState (bool IsBeginFunction,
@@ -252,34 +187,35 @@ void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
252
187
}
253
188
}
254
189
255
- void VirtualCallChecker::reportBug (StringRef Msg, bool IsSink,
256
- const MemRegion *Reg,
257
- CheckerContext &C) const {
258
- ExplodedNode *N;
259
- if (IsSink)
260
- N = C.generateErrorNode ();
261
- else
262
- N = C.generateNonFatalErrorNode ();
190
+ void ento::registerVirtualCallModeling (CheckerManager &Mgr) {
191
+ Mgr.registerChecker <VirtualCallChecker>();
192
+ }
263
193
264
- if (!N)
265
- return ;
266
- if (!BT)
267
- BT.reset (new BugType (
268
- this , " Call to virtual function during construction or destruction" ,
269
- " C++ Object Lifecycle" ));
270
-
271
- auto Reporter = std::make_unique<BugReport>(*BT, Msg, N);
272
- Reporter->addVisitor (std::make_unique<VirtualBugVisitor>(Reg));
273
- C.emitReport (std::move (Reporter));
194
+ void ento::registerPureVirtualCallChecker (CheckerManager &Mgr) {
195
+ auto *Chk = Mgr.getChecker <VirtualCallChecker>();
196
+ Chk->BT_Pure = std::make_unique<BugType>(
197
+ Mgr.getCurrentCheckName (), " Pure virtual method call" ,
198
+ categories::CXXObjectLifecycle);
274
199
}
275
200
276
- void ento::registerVirtualCallChecker (CheckerManager &mgr) {
277
- VirtualCallChecker *checker = mgr.registerChecker <VirtualCallChecker>();
201
+ void ento::registerVirtualCallChecker (CheckerManager &Mgr) {
202
+ auto *Chk = Mgr.getChecker <VirtualCallChecker>();
203
+ if (!Mgr.getAnalyzerOptions ().getCheckerBooleanOption (
204
+ Mgr.getCurrentCheckName (), " PureOnly" )) {
205
+ Chk->BT_Impure = std::make_unique<BugType>(
206
+ Mgr.getCurrentCheckName (), " Unexpected loss of virtual dispatch" ,
207
+ categories::CXXObjectLifecycle);
208
+ }
209
+ }
210
+
211
+ bool ento::shouldRegisterVirtualCallModeling (const LangOptions &LO) {
212
+ return LO.CPlusPlus ;
213
+ }
278
214
279
- checker-> IsPureOnly =
280
- mgr. getAnalyzerOptions (). getCheckerBooleanOption (checker, " PureOnly " ) ;
215
+ bool ento::shouldRegisterPureVirtualCallChecker ( const LangOptions &LO) {
216
+ return LO. CPlusPlus ;
281
217
}
282
218
283
219
bool ento::shouldRegisterVirtualCallChecker (const LangOptions &LO) {
284
- return true ;
220
+ return LO. CPlusPlus ;
285
221
}
0 commit comments