1*67e74705SXin Li //===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li // Defines a checker for proper use of fopen/fclose APIs.
11*67e74705SXin Li // - If a file has been closed with fclose, it should not be accessed again.
12*67e74705SXin Li // Accessing a closed file results in undefined behavior.
13*67e74705SXin Li // - If a file was opened with fopen, it must be closed with fclose before
14*67e74705SXin Li // the execution ends. Failing to do so results in a resource leak.
15*67e74705SXin Li //
16*67e74705SXin Li //===----------------------------------------------------------------------===//
17*67e74705SXin Li
18*67e74705SXin Li #include "ClangSACheckers.h"
19*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
21*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
22*67e74705SXin Li #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23*67e74705SXin Li #include <utility>
24*67e74705SXin Li
25*67e74705SXin Li using namespace clang;
26*67e74705SXin Li using namespace ento;
27*67e74705SXin Li
28*67e74705SXin Li namespace {
29*67e74705SXin Li typedef SmallVector<SymbolRef, 2> SymbolVector;
30*67e74705SXin Li
31*67e74705SXin Li struct StreamState {
32*67e74705SXin Li private:
33*67e74705SXin Li enum Kind { Opened, Closed } K;
StreamState__anon49532eb10111::StreamState34*67e74705SXin Li StreamState(Kind InK) : K(InK) { }
35*67e74705SXin Li
36*67e74705SXin Li public:
isOpened__anon49532eb10111::StreamState37*67e74705SXin Li bool isOpened() const { return K == Opened; }
isClosed__anon49532eb10111::StreamState38*67e74705SXin Li bool isClosed() const { return K == Closed; }
39*67e74705SXin Li
getOpened__anon49532eb10111::StreamState40*67e74705SXin Li static StreamState getOpened() { return StreamState(Opened); }
getClosed__anon49532eb10111::StreamState41*67e74705SXin Li static StreamState getClosed() { return StreamState(Closed); }
42*67e74705SXin Li
operator ==__anon49532eb10111::StreamState43*67e74705SXin Li bool operator==(const StreamState &X) const {
44*67e74705SXin Li return K == X.K;
45*67e74705SXin Li }
Profile__anon49532eb10111::StreamState46*67e74705SXin Li void Profile(llvm::FoldingSetNodeID &ID) const {
47*67e74705SXin Li ID.AddInteger(K);
48*67e74705SXin Li }
49*67e74705SXin Li };
50*67e74705SXin Li
51*67e74705SXin Li class SimpleStreamChecker : public Checker<check::PostCall,
52*67e74705SXin Li check::PreCall,
53*67e74705SXin Li check::DeadSymbols,
54*67e74705SXin Li check::PointerEscape> {
55*67e74705SXin Li CallDescription OpenFn, CloseFn;
56*67e74705SXin Li
57*67e74705SXin Li std::unique_ptr<BugType> DoubleCloseBugType;
58*67e74705SXin Li std::unique_ptr<BugType> LeakBugType;
59*67e74705SXin Li
60*67e74705SXin Li void reportDoubleClose(SymbolRef FileDescSym,
61*67e74705SXin Li const CallEvent &Call,
62*67e74705SXin Li CheckerContext &C) const;
63*67e74705SXin Li
64*67e74705SXin Li void reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C,
65*67e74705SXin Li ExplodedNode *ErrNode) const;
66*67e74705SXin Li
67*67e74705SXin Li bool guaranteedNotToCloseFile(const CallEvent &Call) const;
68*67e74705SXin Li
69*67e74705SXin Li public:
70*67e74705SXin Li SimpleStreamChecker();
71*67e74705SXin Li
72*67e74705SXin Li /// Process fopen.
73*67e74705SXin Li void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
74*67e74705SXin Li /// Process fclose.
75*67e74705SXin Li void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
76*67e74705SXin Li
77*67e74705SXin Li void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
78*67e74705SXin Li
79*67e74705SXin Li /// Stop tracking addresses which escape.
80*67e74705SXin Li ProgramStateRef checkPointerEscape(ProgramStateRef State,
81*67e74705SXin Li const InvalidatedSymbols &Escaped,
82*67e74705SXin Li const CallEvent *Call,
83*67e74705SXin Li PointerEscapeKind Kind) const;
84*67e74705SXin Li };
85*67e74705SXin Li
86*67e74705SXin Li } // end anonymous namespace
87*67e74705SXin Li
88*67e74705SXin Li /// The state of the checker is a map from tracked stream symbols to their
89*67e74705SXin Li /// state. Let's store it in the ProgramState.
90*67e74705SXin Li REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
91*67e74705SXin Li
92*67e74705SXin Li namespace {
93*67e74705SXin Li class StopTrackingCallback final : public SymbolVisitor {
94*67e74705SXin Li ProgramStateRef state;
95*67e74705SXin Li public:
StopTrackingCallback(ProgramStateRef st)96*67e74705SXin Li StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
getState() const97*67e74705SXin Li ProgramStateRef getState() const { return state; }
98*67e74705SXin Li
VisitSymbol(SymbolRef sym)99*67e74705SXin Li bool VisitSymbol(SymbolRef sym) override {
100*67e74705SXin Li state = state->remove<StreamMap>(sym);
101*67e74705SXin Li return true;
102*67e74705SXin Li }
103*67e74705SXin Li };
104*67e74705SXin Li } // end anonymous namespace
105*67e74705SXin Li
SimpleStreamChecker()106*67e74705SXin Li SimpleStreamChecker::SimpleStreamChecker()
107*67e74705SXin Li : OpenFn("fopen"), CloseFn("fclose", 1) {
108*67e74705SXin Li // Initialize the bug types.
109*67e74705SXin Li DoubleCloseBugType.reset(
110*67e74705SXin Li new BugType(this, "Double fclose", "Unix Stream API Error"));
111*67e74705SXin Li
112*67e74705SXin Li LeakBugType.reset(
113*67e74705SXin Li new BugType(this, "Resource Leak", "Unix Stream API Error"));
114*67e74705SXin Li // Sinks are higher importance bugs as well as calls to assert() or exit(0).
115*67e74705SXin Li LeakBugType->setSuppressOnSink(true);
116*67e74705SXin Li }
117*67e74705SXin Li
checkPostCall(const CallEvent & Call,CheckerContext & C) const118*67e74705SXin Li void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
119*67e74705SXin Li CheckerContext &C) const {
120*67e74705SXin Li if (!Call.isGlobalCFunction())
121*67e74705SXin Li return;
122*67e74705SXin Li
123*67e74705SXin Li if (!Call.isCalled(OpenFn))
124*67e74705SXin Li return;
125*67e74705SXin Li
126*67e74705SXin Li // Get the symbolic value corresponding to the file handle.
127*67e74705SXin Li SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
128*67e74705SXin Li if (!FileDesc)
129*67e74705SXin Li return;
130*67e74705SXin Li
131*67e74705SXin Li // Generate the next transition (an edge in the exploded graph).
132*67e74705SXin Li ProgramStateRef State = C.getState();
133*67e74705SXin Li State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
134*67e74705SXin Li C.addTransition(State);
135*67e74705SXin Li }
136*67e74705SXin Li
checkPreCall(const CallEvent & Call,CheckerContext & C) const137*67e74705SXin Li void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
138*67e74705SXin Li CheckerContext &C) const {
139*67e74705SXin Li if (!Call.isGlobalCFunction())
140*67e74705SXin Li return;
141*67e74705SXin Li
142*67e74705SXin Li if (!Call.isCalled(CloseFn))
143*67e74705SXin Li return;
144*67e74705SXin Li
145*67e74705SXin Li // Get the symbolic value corresponding to the file handle.
146*67e74705SXin Li SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol();
147*67e74705SXin Li if (!FileDesc)
148*67e74705SXin Li return;
149*67e74705SXin Li
150*67e74705SXin Li // Check if the stream has already been closed.
151*67e74705SXin Li ProgramStateRef State = C.getState();
152*67e74705SXin Li const StreamState *SS = State->get<StreamMap>(FileDesc);
153*67e74705SXin Li if (SS && SS->isClosed()) {
154*67e74705SXin Li reportDoubleClose(FileDesc, Call, C);
155*67e74705SXin Li return;
156*67e74705SXin Li }
157*67e74705SXin Li
158*67e74705SXin Li // Generate the next transition, in which the stream is closed.
159*67e74705SXin Li State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
160*67e74705SXin Li C.addTransition(State);
161*67e74705SXin Li }
162*67e74705SXin Li
isLeaked(SymbolRef Sym,const StreamState & SS,bool IsSymDead,ProgramStateRef State)163*67e74705SXin Li static bool isLeaked(SymbolRef Sym, const StreamState &SS,
164*67e74705SXin Li bool IsSymDead, ProgramStateRef State) {
165*67e74705SXin Li if (IsSymDead && SS.isOpened()) {
166*67e74705SXin Li // If a symbol is NULL, assume that fopen failed on this path.
167*67e74705SXin Li // A symbol should only be considered leaked if it is non-null.
168*67e74705SXin Li ConstraintManager &CMgr = State->getConstraintManager();
169*67e74705SXin Li ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
170*67e74705SXin Li return !OpenFailed.isConstrainedTrue();
171*67e74705SXin Li }
172*67e74705SXin Li return false;
173*67e74705SXin Li }
174*67e74705SXin Li
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const175*67e74705SXin Li void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
176*67e74705SXin Li CheckerContext &C) const {
177*67e74705SXin Li ProgramStateRef State = C.getState();
178*67e74705SXin Li SymbolVector LeakedStreams;
179*67e74705SXin Li StreamMapTy TrackedStreams = State->get<StreamMap>();
180*67e74705SXin Li for (StreamMapTy::iterator I = TrackedStreams.begin(),
181*67e74705SXin Li E = TrackedStreams.end(); I != E; ++I) {
182*67e74705SXin Li SymbolRef Sym = I->first;
183*67e74705SXin Li bool IsSymDead = SymReaper.isDead(Sym);
184*67e74705SXin Li
185*67e74705SXin Li // Collect leaked symbols.
186*67e74705SXin Li if (isLeaked(Sym, I->second, IsSymDead, State))
187*67e74705SXin Li LeakedStreams.push_back(Sym);
188*67e74705SXin Li
189*67e74705SXin Li // Remove the dead symbol from the streams map.
190*67e74705SXin Li if (IsSymDead)
191*67e74705SXin Li State = State->remove<StreamMap>(Sym);
192*67e74705SXin Li }
193*67e74705SXin Li
194*67e74705SXin Li ExplodedNode *N = C.generateNonFatalErrorNode(State);
195*67e74705SXin Li if (!N)
196*67e74705SXin Li return;
197*67e74705SXin Li reportLeaks(LeakedStreams, C, N);
198*67e74705SXin Li }
199*67e74705SXin Li
reportDoubleClose(SymbolRef FileDescSym,const CallEvent & Call,CheckerContext & C) const200*67e74705SXin Li void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym,
201*67e74705SXin Li const CallEvent &Call,
202*67e74705SXin Li CheckerContext &C) const {
203*67e74705SXin Li // We reached a bug, stop exploring the path here by generating a sink.
204*67e74705SXin Li ExplodedNode *ErrNode = C.generateErrorNode();
205*67e74705SXin Li // If we've already reached this node on another path, return.
206*67e74705SXin Li if (!ErrNode)
207*67e74705SXin Li return;
208*67e74705SXin Li
209*67e74705SXin Li // Generate the report.
210*67e74705SXin Li auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType,
211*67e74705SXin Li "Closing a previously closed file stream", ErrNode);
212*67e74705SXin Li R->addRange(Call.getSourceRange());
213*67e74705SXin Li R->markInteresting(FileDescSym);
214*67e74705SXin Li C.emitReport(std::move(R));
215*67e74705SXin Li }
216*67e74705SXin Li
reportLeaks(ArrayRef<SymbolRef> LeakedStreams,CheckerContext & C,ExplodedNode * ErrNode) const217*67e74705SXin Li void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams,
218*67e74705SXin Li CheckerContext &C,
219*67e74705SXin Li ExplodedNode *ErrNode) const {
220*67e74705SXin Li // Attach bug reports to the leak node.
221*67e74705SXin Li // TODO: Identify the leaked file descriptor.
222*67e74705SXin Li for (SymbolRef LeakedStream : LeakedStreams) {
223*67e74705SXin Li auto R = llvm::make_unique<BugReport>(*LeakBugType,
224*67e74705SXin Li "Opened file is never closed; potential resource leak", ErrNode);
225*67e74705SXin Li R->markInteresting(LeakedStream);
226*67e74705SXin Li C.emitReport(std::move(R));
227*67e74705SXin Li }
228*67e74705SXin Li }
229*67e74705SXin Li
guaranteedNotToCloseFile(const CallEvent & Call) const230*67e74705SXin Li bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{
231*67e74705SXin Li // If it's not in a system header, assume it might close a file.
232*67e74705SXin Li if (!Call.isInSystemHeader())
233*67e74705SXin Li return false;
234*67e74705SXin Li
235*67e74705SXin Li // Handle cases where we know a buffer's /address/ can escape.
236*67e74705SXin Li if (Call.argumentsMayEscape())
237*67e74705SXin Li return false;
238*67e74705SXin Li
239*67e74705SXin Li // Note, even though fclose closes the file, we do not list it here
240*67e74705SXin Li // since the checker is modeling the call.
241*67e74705SXin Li
242*67e74705SXin Li return true;
243*67e74705SXin Li }
244*67e74705SXin Li
245*67e74705SXin Li // If the pointer we are tracking escaped, do not track the symbol as
246*67e74705SXin Li // we cannot reason about it anymore.
247*67e74705SXin Li ProgramStateRef
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const248*67e74705SXin Li SimpleStreamChecker::checkPointerEscape(ProgramStateRef State,
249*67e74705SXin Li const InvalidatedSymbols &Escaped,
250*67e74705SXin Li const CallEvent *Call,
251*67e74705SXin Li PointerEscapeKind Kind) const {
252*67e74705SXin Li // If we know that the call cannot close a file, there is nothing to do.
253*67e74705SXin Li if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) {
254*67e74705SXin Li return State;
255*67e74705SXin Li }
256*67e74705SXin Li
257*67e74705SXin Li for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
258*67e74705SXin Li E = Escaped.end();
259*67e74705SXin Li I != E; ++I) {
260*67e74705SXin Li SymbolRef Sym = *I;
261*67e74705SXin Li
262*67e74705SXin Li // The symbol escaped. Optimistically, assume that the corresponding file
263*67e74705SXin Li // handle will be closed somewhere else.
264*67e74705SXin Li State = State->remove<StreamMap>(Sym);
265*67e74705SXin Li }
266*67e74705SXin Li return State;
267*67e74705SXin Li }
268*67e74705SXin Li
registerSimpleStreamChecker(CheckerManager & mgr)269*67e74705SXin Li void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
270*67e74705SXin Li mgr.registerChecker<SimpleStreamChecker>();
271*67e74705SXin Li }
272