10
10
//
11
11
// ===----------------------------------------------------------------------===//
12
12
13
+ #include " NoOwnershipChangeVisitor.h"
14
+ #include " clang/ASTMatchers/ASTMatchFinder.h"
15
+ #include " clang/ASTMatchers/ASTMatchers.h"
13
16
#include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14
17
#include " clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15
18
#include " clang/StaticAnalyzer/Core/Checker.h"
@@ -74,6 +77,12 @@ struct StreamErrorState {
74
77
// / Returns if the StreamErrorState is a valid object.
75
78
operator bool () const { return NoError || FEof || FError; }
76
79
80
+ LLVM_DUMP_METHOD void dump () const { dumpToStream (llvm::errs ()); }
81
+ LLVM_DUMP_METHOD void dumpToStream (llvm::raw_ostream &os) const {
82
+ os << " NoError: " << NoError << " , FEof: " << FEof
83
+ << " , FError: " << FError;
84
+ }
85
+
77
86
void Profile (llvm::FoldingSetNodeID &ID) const {
78
87
ID.AddBoolean (NoError);
79
88
ID.AddBoolean (FEof);
@@ -98,6 +107,18 @@ struct StreamState {
98
107
OpenFailed // / The last open operation has failed.
99
108
} State;
100
109
110
+ StringRef getKindStr () const {
111
+ switch (State) {
112
+ case Opened:
113
+ return " Opened" ;
114
+ case Closed:
115
+ return " Closed" ;
116
+ case OpenFailed:
117
+ return " OpenFailed" ;
118
+ }
119
+ llvm_unreachable (" Unknown StreamState!" );
120
+ }
121
+
101
122
// / State of the error flags.
102
123
// / Ignored in non-opened stream state but must be NoError.
103
124
StreamErrorState const ErrorState;
@@ -146,6 +167,9 @@ struct StreamState {
146
167
return StreamState{L, OpenFailed, {}, false };
147
168
}
148
169
170
+ LLVM_DUMP_METHOD void dump () const { dumpToStream (llvm::errs ()); }
171
+ LLVM_DUMP_METHOD void dumpToStream (llvm::raw_ostream &os) const ;
172
+
149
173
void Profile (llvm::FoldingSetNodeID &ID) const {
150
174
ID.AddPointer (LastOperation);
151
175
ID.AddInteger (State);
@@ -183,6 +207,14 @@ struct FnDescription {
183
207
ArgNoTy StreamArgNo;
184
208
};
185
209
210
+ LLVM_DUMP_METHOD void StreamState::dumpToStream (llvm::raw_ostream &os) const {
211
+ os << " {Kind: " << getKindStr () << " , Last operation: " << LastOperation
212
+ << " , ErrorState: " ;
213
+ ErrorState.dumpToStream (os);
214
+ os << " , FilePos: " << (FilePositionIndeterminate ? " Indeterminate" : " OK" )
215
+ << ' }' ;
216
+ }
217
+
186
218
// / Get the value of the stream argument out of the passed call event.
187
219
// / The call should contain a function that is described by Desc.
188
220
SVal getStreamArg (const FnDescription *Desc, const CallEvent &Call) {
@@ -300,6 +332,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
300
332
// / If true, generate failure branches for cases that are often not checked.
301
333
bool PedanticMode = false ;
302
334
335
+ const CallDescription FCloseDesc = {CDM::CLibrary, {" fclose" }, 1 };
336
+
303
337
private:
304
338
CallDescriptionMap<FnDescription> FnDescriptions = {
305
339
{{CDM::CLibrary, {" fopen" }, 2 },
@@ -310,8 +344,7 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
310
344
{&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2 }},
311
345
{{CDM::CLibrary, {" tmpfile" }, 0 },
312
346
{nullptr , &StreamChecker::evalFopen, ArgNone}},
313
- {{CDM::CLibrary, {" fclose" }, 1 },
314
- {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0 }},
347
+ {FCloseDesc, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0 }},
315
348
{{CDM::CLibrary, {" fread" }, 4 },
316
349
{&StreamChecker::preRead,
317
350
std::bind (&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true ), 3 }},
@@ -696,6 +729,62 @@ struct StreamOperationEvaluator {
696
729
697
730
} // end anonymous namespace
698
731
732
+ // ===----------------------------------------------------------------------===//
733
+ // Definition of NoStreamStateChangeVisitor.
734
+ // ===----------------------------------------------------------------------===//
735
+
736
+ namespace {
737
+ class NoStreamStateChangeVisitor final : public NoOwnershipChangeVisitor {
738
+ protected:
739
+ // / Syntactically checks whether the callee is a closing function. Since
740
+ // / we have no path-sensitive information on this call (we would need a
741
+ // / CallEvent instead of a CallExpr for that), its possible that a
742
+ // / closing function was called indirectly through a function pointer,
743
+ // / but we are not able to tell, so this is a best effort analysis.
744
+ bool isClosingCallAsWritten (const CallExpr &Call) const {
745
+ const auto *StreamChk = static_cast <const StreamChecker *>(&Checker);
746
+ return StreamChk->FCloseDesc .matchesAsWritten (Call);
747
+ }
748
+
749
+ bool doesFnIntendToHandleOwnership (const Decl *Callee,
750
+ ASTContext &ACtx) final {
751
+ using namespace clang ::ast_matchers;
752
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);
753
+
754
+ auto Matches =
755
+ match (findAll (callExpr ().bind (" call" )), *FD->getBody (), ACtx);
756
+ for (BoundNodes Match : Matches) {
757
+ if (const auto *Call = Match.getNodeAs <CallExpr>(" call" ))
758
+ if (isClosingCallAsWritten (*Call))
759
+ return true ;
760
+ }
761
+ // TODO: Ownership might change with an attempt to store stream object, not
762
+ // only through closing it. Check for attempted stores as well.
763
+ return false ;
764
+ }
765
+
766
+ bool hasResourceStateChanged (ProgramStateRef CallEnterState,
767
+ ProgramStateRef CallExitEndState) final {
768
+ return CallEnterState->get <StreamMap>(Sym) !=
769
+ CallExitEndState->get <StreamMap>(Sym);
770
+ }
771
+
772
+ PathDiagnosticPieceRef emitNote (const ExplodedNode *N) override {
773
+ PathDiagnosticLocation L = PathDiagnosticLocation::create (
774
+ N->getLocation (),
775
+ N->getState ()->getStateManager ().getContext ().getSourceManager ());
776
+ return std::make_shared<PathDiagnosticEventPiece>(
777
+ L, " Returning without closing stream object or storing it for later "
778
+ " release" );
779
+ }
780
+
781
+ public:
782
+ NoStreamStateChangeVisitor (SymbolRef Sym, const StreamChecker *Checker)
783
+ : NoOwnershipChangeVisitor(Sym, Checker) {}
784
+ };
785
+
786
+ } // end anonymous namespace
787
+
699
788
const ExplodedNode *StreamChecker::getAcquisitionSite (const ExplodedNode *N,
700
789
SymbolRef StreamSym,
701
790
CheckerContext &C) {
@@ -1872,6 +1961,7 @@ StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1872
1961
LocUsedForUniqueing,
1873
1962
StreamOpenNode->getLocationContext ()->getDecl ());
1874
1963
R->markInteresting (LeakSym);
1964
+ R->addVisitor <NoStreamStateChangeVisitor>(LeakSym, this );
1875
1965
C.emitReport (std::move (R));
1876
1966
}
1877
1967
0 commit comments