1f4a2713aSLionel Sambuc //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
2f4a2713aSLionel Sambuc //
3f4a2713aSLionel Sambuc // The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc //
5f4a2713aSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc // License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc //
8f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
9f4a2713aSLionel Sambuc //
10f4a2713aSLionel Sambuc // This file defines checkers that model and check stream handling functions.
11f4a2713aSLionel Sambuc //
12f4a2713aSLionel Sambuc //===----------------------------------------------------------------------===//
13f4a2713aSLionel Sambuc
14f4a2713aSLionel Sambuc #include "ClangSACheckers.h"
15f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/Checker.h"
17f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/CheckerManager.h"
18f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
20f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21f4a2713aSLionel Sambuc #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
22f4a2713aSLionel Sambuc #include "llvm/ADT/ImmutableMap.h"
23f4a2713aSLionel Sambuc
24f4a2713aSLionel Sambuc using namespace clang;
25f4a2713aSLionel Sambuc using namespace ento;
26f4a2713aSLionel Sambuc
27f4a2713aSLionel Sambuc namespace {
28f4a2713aSLionel Sambuc
29f4a2713aSLionel Sambuc struct StreamState {
30f4a2713aSLionel Sambuc enum Kind { Opened, Closed, OpenFailed, Escaped } K;
31f4a2713aSLionel Sambuc const Stmt *S;
32f4a2713aSLionel Sambuc
StreamState__anon86c4d4e20111::StreamState33f4a2713aSLionel Sambuc StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
34f4a2713aSLionel Sambuc
isOpened__anon86c4d4e20111::StreamState35f4a2713aSLionel Sambuc bool isOpened() const { return K == Opened; }
isClosed__anon86c4d4e20111::StreamState36f4a2713aSLionel Sambuc bool isClosed() const { return K == Closed; }
37f4a2713aSLionel Sambuc //bool isOpenFailed() const { return K == OpenFailed; }
38f4a2713aSLionel Sambuc //bool isEscaped() const { return K == Escaped; }
39f4a2713aSLionel Sambuc
operator ==__anon86c4d4e20111::StreamState40f4a2713aSLionel Sambuc bool operator==(const StreamState &X) const {
41f4a2713aSLionel Sambuc return K == X.K && S == X.S;
42f4a2713aSLionel Sambuc }
43f4a2713aSLionel Sambuc
getOpened__anon86c4d4e20111::StreamState44f4a2713aSLionel Sambuc static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
getClosed__anon86c4d4e20111::StreamState45f4a2713aSLionel Sambuc static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
getOpenFailed__anon86c4d4e20111::StreamState46f4a2713aSLionel Sambuc static StreamState getOpenFailed(const Stmt *s) {
47f4a2713aSLionel Sambuc return StreamState(OpenFailed, s);
48f4a2713aSLionel Sambuc }
getEscaped__anon86c4d4e20111::StreamState49f4a2713aSLionel Sambuc static StreamState getEscaped(const Stmt *s) {
50f4a2713aSLionel Sambuc return StreamState(Escaped, s);
51f4a2713aSLionel Sambuc }
52f4a2713aSLionel Sambuc
Profile__anon86c4d4e20111::StreamState53f4a2713aSLionel Sambuc void Profile(llvm::FoldingSetNodeID &ID) const {
54f4a2713aSLionel Sambuc ID.AddInteger(K);
55f4a2713aSLionel Sambuc ID.AddPointer(S);
56f4a2713aSLionel Sambuc }
57f4a2713aSLionel Sambuc };
58f4a2713aSLionel Sambuc
59f4a2713aSLionel Sambuc class StreamChecker : public Checker<eval::Call,
60f4a2713aSLionel Sambuc check::DeadSymbols > {
61f4a2713aSLionel Sambuc mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
62f4a2713aSLionel Sambuc *II_fwrite,
63f4a2713aSLionel Sambuc *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
64f4a2713aSLionel Sambuc *II_clearerr, *II_feof, *II_ferror, *II_fileno;
65*0a6a1f1dSLionel Sambuc mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
66f4a2713aSLionel Sambuc BT_doubleclose, BT_ResourceLeak;
67f4a2713aSLionel Sambuc
68f4a2713aSLionel Sambuc public:
StreamChecker()69f4a2713aSLionel Sambuc StreamChecker()
70*0a6a1f1dSLionel Sambuc : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
71*0a6a1f1dSLionel Sambuc II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
72*0a6a1f1dSLionel Sambuc II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
73*0a6a1f1dSLionel Sambuc II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr),
74*0a6a1f1dSLionel Sambuc II_ferror(nullptr), II_fileno(nullptr) {}
75f4a2713aSLionel Sambuc
76f4a2713aSLionel Sambuc bool evalCall(const CallExpr *CE, CheckerContext &C) const;
77f4a2713aSLionel Sambuc void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
78f4a2713aSLionel Sambuc
79f4a2713aSLionel Sambuc private:
80f4a2713aSLionel Sambuc void Fopen(CheckerContext &C, const CallExpr *CE) const;
81f4a2713aSLionel Sambuc void Tmpfile(CheckerContext &C, const CallExpr *CE) const;
82f4a2713aSLionel Sambuc void Fclose(CheckerContext &C, const CallExpr *CE) const;
83f4a2713aSLionel Sambuc void Fread(CheckerContext &C, const CallExpr *CE) const;
84f4a2713aSLionel Sambuc void Fwrite(CheckerContext &C, const CallExpr *CE) const;
85f4a2713aSLionel Sambuc void Fseek(CheckerContext &C, const CallExpr *CE) const;
86f4a2713aSLionel Sambuc void Ftell(CheckerContext &C, const CallExpr *CE) const;
87f4a2713aSLionel Sambuc void Rewind(CheckerContext &C, const CallExpr *CE) const;
88f4a2713aSLionel Sambuc void Fgetpos(CheckerContext &C, const CallExpr *CE) const;
89f4a2713aSLionel Sambuc void Fsetpos(CheckerContext &C, const CallExpr *CE) const;
90f4a2713aSLionel Sambuc void Clearerr(CheckerContext &C, const CallExpr *CE) const;
91f4a2713aSLionel Sambuc void Feof(CheckerContext &C, const CallExpr *CE) const;
92f4a2713aSLionel Sambuc void Ferror(CheckerContext &C, const CallExpr *CE) const;
93f4a2713aSLionel Sambuc void Fileno(CheckerContext &C, const CallExpr *CE) const;
94f4a2713aSLionel Sambuc
95f4a2713aSLionel Sambuc void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
96f4a2713aSLionel Sambuc
97f4a2713aSLionel Sambuc ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
98f4a2713aSLionel Sambuc CheckerContext &C) const;
99f4a2713aSLionel Sambuc ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
100f4a2713aSLionel Sambuc CheckerContext &C) const;
101f4a2713aSLionel Sambuc };
102f4a2713aSLionel Sambuc
103f4a2713aSLionel Sambuc } // end anonymous namespace
104f4a2713aSLionel Sambuc
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap,SymbolRef,StreamState)105f4a2713aSLionel Sambuc REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
106f4a2713aSLionel Sambuc
107f4a2713aSLionel Sambuc
108f4a2713aSLionel Sambuc bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
109f4a2713aSLionel Sambuc const FunctionDecl *FD = C.getCalleeDecl(CE);
110f4a2713aSLionel Sambuc if (!FD || FD->getKind() != Decl::Function)
111f4a2713aSLionel Sambuc return false;
112f4a2713aSLionel Sambuc
113f4a2713aSLionel Sambuc ASTContext &Ctx = C.getASTContext();
114f4a2713aSLionel Sambuc if (!II_fopen)
115f4a2713aSLionel Sambuc II_fopen = &Ctx.Idents.get("fopen");
116f4a2713aSLionel Sambuc if (!II_tmpfile)
117f4a2713aSLionel Sambuc II_tmpfile = &Ctx.Idents.get("tmpfile");
118f4a2713aSLionel Sambuc if (!II_fclose)
119f4a2713aSLionel Sambuc II_fclose = &Ctx.Idents.get("fclose");
120f4a2713aSLionel Sambuc if (!II_fread)
121f4a2713aSLionel Sambuc II_fread = &Ctx.Idents.get("fread");
122f4a2713aSLionel Sambuc if (!II_fwrite)
123f4a2713aSLionel Sambuc II_fwrite = &Ctx.Idents.get("fwrite");
124f4a2713aSLionel Sambuc if (!II_fseek)
125f4a2713aSLionel Sambuc II_fseek = &Ctx.Idents.get("fseek");
126f4a2713aSLionel Sambuc if (!II_ftell)
127f4a2713aSLionel Sambuc II_ftell = &Ctx.Idents.get("ftell");
128f4a2713aSLionel Sambuc if (!II_rewind)
129f4a2713aSLionel Sambuc II_rewind = &Ctx.Idents.get("rewind");
130f4a2713aSLionel Sambuc if (!II_fgetpos)
131f4a2713aSLionel Sambuc II_fgetpos = &Ctx.Idents.get("fgetpos");
132f4a2713aSLionel Sambuc if (!II_fsetpos)
133f4a2713aSLionel Sambuc II_fsetpos = &Ctx.Idents.get("fsetpos");
134f4a2713aSLionel Sambuc if (!II_clearerr)
135f4a2713aSLionel Sambuc II_clearerr = &Ctx.Idents.get("clearerr");
136f4a2713aSLionel Sambuc if (!II_feof)
137f4a2713aSLionel Sambuc II_feof = &Ctx.Idents.get("feof");
138f4a2713aSLionel Sambuc if (!II_ferror)
139f4a2713aSLionel Sambuc II_ferror = &Ctx.Idents.get("ferror");
140f4a2713aSLionel Sambuc if (!II_fileno)
141f4a2713aSLionel Sambuc II_fileno = &Ctx.Idents.get("fileno");
142f4a2713aSLionel Sambuc
143f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_fopen) {
144f4a2713aSLionel Sambuc Fopen(C, CE);
145f4a2713aSLionel Sambuc return true;
146f4a2713aSLionel Sambuc }
147f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_tmpfile) {
148f4a2713aSLionel Sambuc Tmpfile(C, CE);
149f4a2713aSLionel Sambuc return true;
150f4a2713aSLionel Sambuc }
151f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_fclose) {
152f4a2713aSLionel Sambuc Fclose(C, CE);
153f4a2713aSLionel Sambuc return true;
154f4a2713aSLionel Sambuc }
155f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_fread) {
156f4a2713aSLionel Sambuc Fread(C, CE);
157f4a2713aSLionel Sambuc return true;
158f4a2713aSLionel Sambuc }
159f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_fwrite) {
160f4a2713aSLionel Sambuc Fwrite(C, CE);
161f4a2713aSLionel Sambuc return true;
162f4a2713aSLionel Sambuc }
163f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_fseek) {
164f4a2713aSLionel Sambuc Fseek(C, CE);
165f4a2713aSLionel Sambuc return true;
166f4a2713aSLionel Sambuc }
167f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_ftell) {
168f4a2713aSLionel Sambuc Ftell(C, CE);
169f4a2713aSLionel Sambuc return true;
170f4a2713aSLionel Sambuc }
171f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_rewind) {
172f4a2713aSLionel Sambuc Rewind(C, CE);
173f4a2713aSLionel Sambuc return true;
174f4a2713aSLionel Sambuc }
175f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_fgetpos) {
176f4a2713aSLionel Sambuc Fgetpos(C, CE);
177f4a2713aSLionel Sambuc return true;
178f4a2713aSLionel Sambuc }
179f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_fsetpos) {
180f4a2713aSLionel Sambuc Fsetpos(C, CE);
181f4a2713aSLionel Sambuc return true;
182f4a2713aSLionel Sambuc }
183f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_clearerr) {
184f4a2713aSLionel Sambuc Clearerr(C, CE);
185f4a2713aSLionel Sambuc return true;
186f4a2713aSLionel Sambuc }
187f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_feof) {
188f4a2713aSLionel Sambuc Feof(C, CE);
189f4a2713aSLionel Sambuc return true;
190f4a2713aSLionel Sambuc }
191f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_ferror) {
192f4a2713aSLionel Sambuc Ferror(C, CE);
193f4a2713aSLionel Sambuc return true;
194f4a2713aSLionel Sambuc }
195f4a2713aSLionel Sambuc if (FD->getIdentifier() == II_fileno) {
196f4a2713aSLionel Sambuc Fileno(C, CE);
197f4a2713aSLionel Sambuc return true;
198f4a2713aSLionel Sambuc }
199f4a2713aSLionel Sambuc
200f4a2713aSLionel Sambuc return false;
201f4a2713aSLionel Sambuc }
202f4a2713aSLionel Sambuc
Fopen(CheckerContext & C,const CallExpr * CE) const203f4a2713aSLionel Sambuc void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const {
204f4a2713aSLionel Sambuc OpenFileAux(C, CE);
205f4a2713aSLionel Sambuc }
206f4a2713aSLionel Sambuc
Tmpfile(CheckerContext & C,const CallExpr * CE) const207f4a2713aSLionel Sambuc void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
208f4a2713aSLionel Sambuc OpenFileAux(C, CE);
209f4a2713aSLionel Sambuc }
210f4a2713aSLionel Sambuc
OpenFileAux(CheckerContext & C,const CallExpr * CE) const211f4a2713aSLionel Sambuc void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
212f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
213f4a2713aSLionel Sambuc SValBuilder &svalBuilder = C.getSValBuilder();
214f4a2713aSLionel Sambuc const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
215*0a6a1f1dSLionel Sambuc DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
216*0a6a1f1dSLionel Sambuc C.blockCount())
217f4a2713aSLionel Sambuc .castAs<DefinedSVal>();
218f4a2713aSLionel Sambuc state = state->BindExpr(CE, C.getLocationContext(), RetVal);
219f4a2713aSLionel Sambuc
220f4a2713aSLionel Sambuc ConstraintManager &CM = C.getConstraintManager();
221f4a2713aSLionel Sambuc // Bifurcate the state into two: one with a valid FILE* pointer, the other
222f4a2713aSLionel Sambuc // with a NULL.
223f4a2713aSLionel Sambuc ProgramStateRef stateNotNull, stateNull;
224*0a6a1f1dSLionel Sambuc std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
225f4a2713aSLionel Sambuc
226f4a2713aSLionel Sambuc if (SymbolRef Sym = RetVal.getAsSymbol()) {
227f4a2713aSLionel Sambuc // if RetVal is not NULL, set the symbol's state to Opened.
228f4a2713aSLionel Sambuc stateNotNull =
229f4a2713aSLionel Sambuc stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE));
230f4a2713aSLionel Sambuc stateNull =
231f4a2713aSLionel Sambuc stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE));
232f4a2713aSLionel Sambuc
233f4a2713aSLionel Sambuc C.addTransition(stateNotNull);
234f4a2713aSLionel Sambuc C.addTransition(stateNull);
235f4a2713aSLionel Sambuc }
236f4a2713aSLionel Sambuc }
237f4a2713aSLionel Sambuc
Fclose(CheckerContext & C,const CallExpr * CE) const238f4a2713aSLionel Sambuc void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
239f4a2713aSLionel Sambuc ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C);
240f4a2713aSLionel Sambuc if (state)
241f4a2713aSLionel Sambuc C.addTransition(state);
242f4a2713aSLionel Sambuc }
243f4a2713aSLionel Sambuc
Fread(CheckerContext & C,const CallExpr * CE) const244f4a2713aSLionel Sambuc void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
245f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
246f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
247f4a2713aSLionel Sambuc state, C))
248f4a2713aSLionel Sambuc return;
249f4a2713aSLionel Sambuc }
250f4a2713aSLionel Sambuc
Fwrite(CheckerContext & C,const CallExpr * CE) const251f4a2713aSLionel Sambuc void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
252f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
253f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
254f4a2713aSLionel Sambuc state, C))
255f4a2713aSLionel Sambuc return;
256f4a2713aSLionel Sambuc }
257f4a2713aSLionel Sambuc
Fseek(CheckerContext & C,const CallExpr * CE) const258f4a2713aSLionel Sambuc void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
259f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
260f4a2713aSLionel Sambuc if (!(state = CheckNullStream(state->getSVal(CE->getArg(0),
261f4a2713aSLionel Sambuc C.getLocationContext()), state, C)))
262f4a2713aSLionel Sambuc return;
263f4a2713aSLionel Sambuc // Check the legality of the 'whence' argument of 'fseek'.
264f4a2713aSLionel Sambuc SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
265f4a2713aSLionel Sambuc Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>();
266f4a2713aSLionel Sambuc
267f4a2713aSLionel Sambuc if (!CI)
268f4a2713aSLionel Sambuc return;
269f4a2713aSLionel Sambuc
270f4a2713aSLionel Sambuc int64_t x = CI->getValue().getSExtValue();
271f4a2713aSLionel Sambuc if (x >= 0 && x <= 2)
272f4a2713aSLionel Sambuc return;
273f4a2713aSLionel Sambuc
274f4a2713aSLionel Sambuc if (ExplodedNode *N = C.addTransition(state)) {
275f4a2713aSLionel Sambuc if (!BT_illegalwhence)
276*0a6a1f1dSLionel Sambuc BT_illegalwhence.reset(
277*0a6a1f1dSLionel Sambuc new BuiltinBug(this, "Illegal whence argument",
278f4a2713aSLionel Sambuc "The whence argument to fseek() should be "
279f4a2713aSLionel Sambuc "SEEK_SET, SEEK_END, or SEEK_CUR."));
280f4a2713aSLionel Sambuc BugReport *R = new BugReport(*BT_illegalwhence,
281f4a2713aSLionel Sambuc BT_illegalwhence->getDescription(), N);
282f4a2713aSLionel Sambuc C.emitReport(R);
283f4a2713aSLionel Sambuc }
284f4a2713aSLionel Sambuc }
285f4a2713aSLionel Sambuc
Ftell(CheckerContext & C,const CallExpr * CE) const286f4a2713aSLionel Sambuc void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
287f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
288f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
289f4a2713aSLionel Sambuc state, C))
290f4a2713aSLionel Sambuc return;
291f4a2713aSLionel Sambuc }
292f4a2713aSLionel Sambuc
Rewind(CheckerContext & C,const CallExpr * CE) const293f4a2713aSLionel Sambuc void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
294f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
295f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
296f4a2713aSLionel Sambuc state, C))
297f4a2713aSLionel Sambuc return;
298f4a2713aSLionel Sambuc }
299f4a2713aSLionel Sambuc
Fgetpos(CheckerContext & C,const CallExpr * CE) const300f4a2713aSLionel Sambuc void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
301f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
302f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
303f4a2713aSLionel Sambuc state, C))
304f4a2713aSLionel Sambuc return;
305f4a2713aSLionel Sambuc }
306f4a2713aSLionel Sambuc
Fsetpos(CheckerContext & C,const CallExpr * CE) const307f4a2713aSLionel Sambuc void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
308f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
309f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
310f4a2713aSLionel Sambuc state, C))
311f4a2713aSLionel Sambuc return;
312f4a2713aSLionel Sambuc }
313f4a2713aSLionel Sambuc
Clearerr(CheckerContext & C,const CallExpr * CE) const314f4a2713aSLionel Sambuc void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
315f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
316f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
317f4a2713aSLionel Sambuc state, C))
318f4a2713aSLionel Sambuc return;
319f4a2713aSLionel Sambuc }
320f4a2713aSLionel Sambuc
Feof(CheckerContext & C,const CallExpr * CE) const321f4a2713aSLionel Sambuc void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
322f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
323f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
324f4a2713aSLionel Sambuc state, C))
325f4a2713aSLionel Sambuc return;
326f4a2713aSLionel Sambuc }
327f4a2713aSLionel Sambuc
Ferror(CheckerContext & C,const CallExpr * CE) const328f4a2713aSLionel Sambuc void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
329f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
330f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
331f4a2713aSLionel Sambuc state, C))
332f4a2713aSLionel Sambuc return;
333f4a2713aSLionel Sambuc }
334f4a2713aSLionel Sambuc
Fileno(CheckerContext & C,const CallExpr * CE) const335f4a2713aSLionel Sambuc void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
336f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
337f4a2713aSLionel Sambuc if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
338f4a2713aSLionel Sambuc state, C))
339f4a2713aSLionel Sambuc return;
340f4a2713aSLionel Sambuc }
341f4a2713aSLionel Sambuc
CheckNullStream(SVal SV,ProgramStateRef state,CheckerContext & C) const342f4a2713aSLionel Sambuc ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
343f4a2713aSLionel Sambuc CheckerContext &C) const {
344f4a2713aSLionel Sambuc Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>();
345f4a2713aSLionel Sambuc if (!DV)
346*0a6a1f1dSLionel Sambuc return nullptr;
347f4a2713aSLionel Sambuc
348f4a2713aSLionel Sambuc ConstraintManager &CM = C.getConstraintManager();
349f4a2713aSLionel Sambuc ProgramStateRef stateNotNull, stateNull;
350*0a6a1f1dSLionel Sambuc std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
351f4a2713aSLionel Sambuc
352f4a2713aSLionel Sambuc if (!stateNotNull && stateNull) {
353f4a2713aSLionel Sambuc if (ExplodedNode *N = C.generateSink(stateNull)) {
354f4a2713aSLionel Sambuc if (!BT_nullfp)
355*0a6a1f1dSLionel Sambuc BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
356f4a2713aSLionel Sambuc "Stream pointer might be NULL."));
357f4a2713aSLionel Sambuc BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N);
358f4a2713aSLionel Sambuc C.emitReport(R);
359f4a2713aSLionel Sambuc }
360*0a6a1f1dSLionel Sambuc return nullptr;
361f4a2713aSLionel Sambuc }
362f4a2713aSLionel Sambuc return stateNotNull;
363f4a2713aSLionel Sambuc }
364f4a2713aSLionel Sambuc
CheckDoubleClose(const CallExpr * CE,ProgramStateRef state,CheckerContext & C) const365f4a2713aSLionel Sambuc ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
366f4a2713aSLionel Sambuc ProgramStateRef state,
367f4a2713aSLionel Sambuc CheckerContext &C) const {
368f4a2713aSLionel Sambuc SymbolRef Sym =
369f4a2713aSLionel Sambuc state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol();
370f4a2713aSLionel Sambuc if (!Sym)
371f4a2713aSLionel Sambuc return state;
372f4a2713aSLionel Sambuc
373f4a2713aSLionel Sambuc const StreamState *SS = state->get<StreamMap>(Sym);
374f4a2713aSLionel Sambuc
375f4a2713aSLionel Sambuc // If the file stream is not tracked, return.
376f4a2713aSLionel Sambuc if (!SS)
377f4a2713aSLionel Sambuc return state;
378f4a2713aSLionel Sambuc
379f4a2713aSLionel Sambuc // Check: Double close a File Descriptor could cause undefined behaviour.
380f4a2713aSLionel Sambuc // Conforming to man-pages
381f4a2713aSLionel Sambuc if (SS->isClosed()) {
382f4a2713aSLionel Sambuc ExplodedNode *N = C.generateSink();
383f4a2713aSLionel Sambuc if (N) {
384f4a2713aSLionel Sambuc if (!BT_doubleclose)
385*0a6a1f1dSLionel Sambuc BT_doubleclose.reset(new BuiltinBug(
386*0a6a1f1dSLionel Sambuc this, "Double fclose", "Try to close a file Descriptor already"
387f4a2713aSLionel Sambuc " closed. Cause undefined behaviour."));
388f4a2713aSLionel Sambuc BugReport *R = new BugReport(*BT_doubleclose,
389f4a2713aSLionel Sambuc BT_doubleclose->getDescription(), N);
390f4a2713aSLionel Sambuc C.emitReport(R);
391f4a2713aSLionel Sambuc }
392*0a6a1f1dSLionel Sambuc return nullptr;
393f4a2713aSLionel Sambuc }
394f4a2713aSLionel Sambuc
395f4a2713aSLionel Sambuc // Close the File Descriptor.
396f4a2713aSLionel Sambuc return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
397f4a2713aSLionel Sambuc }
398f4a2713aSLionel Sambuc
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const399f4a2713aSLionel Sambuc void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
400f4a2713aSLionel Sambuc CheckerContext &C) const {
401f4a2713aSLionel Sambuc // TODO: Clean up the state.
402f4a2713aSLionel Sambuc for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(),
403f4a2713aSLionel Sambuc E = SymReaper.dead_end(); I != E; ++I) {
404f4a2713aSLionel Sambuc SymbolRef Sym = *I;
405f4a2713aSLionel Sambuc ProgramStateRef state = C.getState();
406f4a2713aSLionel Sambuc const StreamState *SS = state->get<StreamMap>(Sym);
407f4a2713aSLionel Sambuc if (!SS)
408f4a2713aSLionel Sambuc continue;
409f4a2713aSLionel Sambuc
410f4a2713aSLionel Sambuc if (SS->isOpened()) {
411f4a2713aSLionel Sambuc ExplodedNode *N = C.generateSink();
412f4a2713aSLionel Sambuc if (N) {
413f4a2713aSLionel Sambuc if (!BT_ResourceLeak)
414*0a6a1f1dSLionel Sambuc BT_ResourceLeak.reset(new BuiltinBug(
415*0a6a1f1dSLionel Sambuc this, "Resource Leak",
416f4a2713aSLionel Sambuc "Opened File never closed. Potential Resource leak."));
417f4a2713aSLionel Sambuc BugReport *R = new BugReport(*BT_ResourceLeak,
418f4a2713aSLionel Sambuc BT_ResourceLeak->getDescription(), N);
419f4a2713aSLionel Sambuc C.emitReport(R);
420f4a2713aSLionel Sambuc }
421f4a2713aSLionel Sambuc }
422f4a2713aSLionel Sambuc }
423f4a2713aSLionel Sambuc }
424f4a2713aSLionel Sambuc
registerStreamChecker(CheckerManager & mgr)425f4a2713aSLionel Sambuc void ento::registerStreamChecker(CheckerManager &mgr) {
426f4a2713aSLionel Sambuc mgr.registerChecker<StreamChecker>();
427f4a2713aSLionel Sambuc }
428