Skip to content

Commit 9b7c43d

Browse files
committed
[Analyzer][StreamChecker] Report every leak, clean up state.
Summary: Report resource leaks with non-fatal error. Report every resource leak. Stream state is cleaned up at `checkDeadSymbols`. Reviewers: Szelethus, baloghadamsoftware, NoQ Reviewed By: Szelethus Subscribers: rnkovacs, xazax.hun, baloghadamsoftware, szepet, a.sidorin, mikhail.ramalho, Szelethus, donat.nagy, dkrupp, gamesh411, Charusso, martong, ASDenysPetrov, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D82845
1 parent df952cb commit 9b7c43d

File tree

2 files changed

+77
-24
lines changed

2 files changed

+77
-24
lines changed

clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,12 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
337337
/// to ensure uniform handling.
338338
void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const;
339339

340+
/// Emit resource leak warnings for the given symbols.
341+
/// Createn a non-fatal error node for these, and returns it (if any warnings
342+
/// were generated). Return value is non-null.
343+
ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
344+
CheckerContext &C, ExplodedNode *Pred) const;
345+
340346
/// Find the description data of the function called by a call event.
341347
/// Returns nullptr if no function is recognized.
342348
const FnDescription *lookupFn(const CallEvent &Call) const {
@@ -956,28 +962,19 @@ void StreamChecker::reportFEofWarning(CheckerContext &C,
956962
C.addTransition(State);
957963
}
958964

959-
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
960-
CheckerContext &C) const {
961-
ProgramStateRef State = C.getState();
962-
963-
// TODO: Clean up the state.
964-
const StreamMapTy &Map = State->get<StreamMap>();
965-
for (const auto &I : Map) {
966-
SymbolRef Sym = I.first;
967-
const StreamState &SS = I.second;
968-
if (!SymReaper.isDead(Sym) || !SS.isOpened())
969-
continue;
970-
971-
ExplodedNode *N = C.generateErrorNode();
972-
if (!N)
973-
continue;
965+
ExplodedNode *
966+
StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
967+
CheckerContext &C, ExplodedNode *Pred) const {
968+
// Do not warn for non-closed stream at program exit.
969+
// FIXME: Use BugType::SuppressOnSink instead.
970+
if (Pred && Pred->getCFGBlock() && Pred->getCFGBlock()->hasNoReturnElement())
971+
return Pred;
974972

975-
// Do not warn for non-closed stream at program exit.
976-
ExplodedNode *Pred = C.getPredecessor();
977-
if (Pred && Pred->getCFGBlock() &&
978-
Pred->getCFGBlock()->hasNoReturnElement())
979-
continue;
973+
ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
974+
if (!Err)
975+
return Pred;
980976

977+
for (SymbolRef LeakSym : LeakedSyms) {
981978
// Resource leaks can result in multiple warning that describe the same kind
982979
// of programming error:
983980
// void f() {
@@ -989,8 +986,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
989986
// from a different kinds of errors), the reduction in redundant reports
990987
// makes this a worthwhile heuristic.
991988
// FIXME: Add a checker option to turn this uniqueing feature off.
992-
993-
const ExplodedNode *StreamOpenNode = getAcquisitionSite(N, Sym, C);
989+
const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
994990
assert(StreamOpenNode && "Could not find place of stream opening.");
995991
PathDiagnosticLocation LocUsedForUniqueing =
996992
PathDiagnosticLocation::createBegin(
@@ -1000,12 +996,38 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1000996
std::unique_ptr<PathSensitiveBugReport> R =
1001997
std::make_unique<PathSensitiveBugReport>(
1002998
BT_ResourceLeak,
1003-
"Opened stream never closed. Potential resource leak.", N,
999+
"Opened stream never closed. Potential resource leak.", Err,
10041000
LocUsedForUniqueing,
10051001
StreamOpenNode->getLocationContext()->getDecl());
1006-
R->markInteresting(Sym);
1002+
R->markInteresting(LeakSym);
10071003
C.emitReport(std::move(R));
10081004
}
1005+
1006+
return Err;
1007+
}
1008+
1009+
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1010+
CheckerContext &C) const {
1011+
ProgramStateRef State = C.getState();
1012+
1013+
llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1014+
1015+
const StreamMapTy &Map = State->get<StreamMap>();
1016+
for (const auto &I : Map) {
1017+
SymbolRef Sym = I.first;
1018+
const StreamState &SS = I.second;
1019+
if (!SymReaper.isDead(Sym))
1020+
continue;
1021+
if (SS.isOpened())
1022+
LeakedSyms.push_back(Sym);
1023+
State = State->remove<StreamMap>(Sym);
1024+
}
1025+
1026+
ExplodedNode *N = C.getPredecessor();
1027+
if (!LeakedSyms.empty())
1028+
N = reportLeaks(LeakedSyms, C, N);
1029+
1030+
C.addTransition(State, N);
10091031
}
10101032

10111033
ProgramStateRef StreamChecker::checkPointerEscape(

clang/test/Analysis/stream-note.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,34 @@ void check_note_freopen() {
4646
}
4747
// expected-warning@-1 {{Opened stream never closed. Potential resource leak}}
4848
// expected-note@-2 {{Opened stream never closed. Potential resource leak}}
49+
50+
void check_note_leak_2(int c) {
51+
FILE *F1 = fopen("foo1.c", "r"); // expected-note {{Stream opened here}}
52+
if (!F1)
53+
// expected-note@-1 {{'F1' is non-null}}
54+
// expected-note@-2 {{Taking false branch}}
55+
// expected-note@-3 {{'F1' is non-null}}
56+
// expected-note@-4 {{Taking false branch}}
57+
return;
58+
FILE *F2 = fopen("foo2.c", "r"); // expected-note {{Stream opened here}}
59+
if (!F2) {
60+
// expected-note@-1 {{'F2' is non-null}}
61+
// expected-note@-2 {{Taking false branch}}
62+
// expected-note@-3 {{'F2' is non-null}}
63+
// expected-note@-4 {{Taking false branch}}
64+
fclose(F1);
65+
return;
66+
}
67+
if (c)
68+
// expected-note@-1 {{Assuming 'c' is not equal to 0}}
69+
// expected-note@-2 {{Taking true branch}}
70+
// expected-note@-3 {{Assuming 'c' is not equal to 0}}
71+
// expected-note@-4 {{Taking true branch}}
72+
return;
73+
// expected-warning@-1 {{Opened stream never closed. Potential resource leak}}
74+
// expected-note@-2 {{Opened stream never closed. Potential resource leak}}
75+
// expected-warning@-3 {{Opened stream never closed. Potential resource leak}}
76+
// expected-note@-4 {{Opened stream never closed. Potential resource leak}}
77+
fclose(F1);
78+
fclose(F2);
79+
}

0 commit comments

Comments
 (0)