xref: /minix3/external/bsd/llvm/dist/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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