xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (revision ec727ea710c91afd8ce4f788c5aaa8482b7b69b2)
1e5dd7070Spatrick //===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines checkers that model and check stream handling functions.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick 
13e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
16e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
17e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
20e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
21e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
22e5dd7070Spatrick #include <functional>
23e5dd7070Spatrick 
24e5dd7070Spatrick using namespace clang;
25e5dd7070Spatrick using namespace ento;
26e5dd7070Spatrick using namespace std::placeholders;
27e5dd7070Spatrick 
28e5dd7070Spatrick namespace {
29e5dd7070Spatrick 
30*ec727ea7Spatrick struct FnDescription;
31e5dd7070Spatrick 
32*ec727ea7Spatrick /// State of the stream error flags.
33*ec727ea7Spatrick /// Sometimes it is not known to the checker what error flags are set.
34*ec727ea7Spatrick /// This is indicated by setting more than one flag to true.
35*ec727ea7Spatrick /// This is an optimization to avoid state splits.
36*ec727ea7Spatrick /// A stream can either be in FEOF or FERROR but not both at the same time.
37*ec727ea7Spatrick /// Multiple flags are set to handle the corresponding states together.
38*ec727ea7Spatrick struct StreamErrorState {
39*ec727ea7Spatrick   /// The stream can be in state where none of the error flags set.
40*ec727ea7Spatrick   bool NoError = true;
41*ec727ea7Spatrick   /// The stream can be in state where the EOF indicator is set.
42*ec727ea7Spatrick   bool FEof = false;
43*ec727ea7Spatrick   /// The stream can be in state where the error indicator is set.
44*ec727ea7Spatrick   bool FError = false;
45e5dd7070Spatrick 
46*ec727ea7Spatrick   bool isNoError() const { return NoError && !FEof && !FError; }
47*ec727ea7Spatrick   bool isFEof() const { return !NoError && FEof && !FError; }
48*ec727ea7Spatrick   bool isFError() const { return !NoError && !FEof && FError; }
49e5dd7070Spatrick 
50*ec727ea7Spatrick   bool operator==(const StreamErrorState &ES) const {
51*ec727ea7Spatrick     return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
52*ec727ea7Spatrick   }
53e5dd7070Spatrick 
54*ec727ea7Spatrick   bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
55*ec727ea7Spatrick 
56*ec727ea7Spatrick   StreamErrorState operator|(const StreamErrorState &E) const {
57*ec727ea7Spatrick     return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
58*ec727ea7Spatrick   }
59*ec727ea7Spatrick 
60*ec727ea7Spatrick   StreamErrorState operator&(const StreamErrorState &E) const {
61*ec727ea7Spatrick     return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
62*ec727ea7Spatrick   }
63*ec727ea7Spatrick 
64*ec727ea7Spatrick   StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
65*ec727ea7Spatrick 
66*ec727ea7Spatrick   /// Returns if the StreamErrorState is a valid object.
67*ec727ea7Spatrick   operator bool() const { return NoError || FEof || FError; }
68e5dd7070Spatrick 
69e5dd7070Spatrick   void Profile(llvm::FoldingSetNodeID &ID) const {
70*ec727ea7Spatrick     ID.AddBoolean(NoError);
71*ec727ea7Spatrick     ID.AddBoolean(FEof);
72*ec727ea7Spatrick     ID.AddBoolean(FError);
73e5dd7070Spatrick   }
74e5dd7070Spatrick };
75e5dd7070Spatrick 
76*ec727ea7Spatrick const StreamErrorState ErrorNone{true, false, false};
77*ec727ea7Spatrick const StreamErrorState ErrorFEof{false, true, false};
78*ec727ea7Spatrick const StreamErrorState ErrorFError{false, false, true};
79e5dd7070Spatrick 
80*ec727ea7Spatrick /// Full state information about a stream pointer.
81*ec727ea7Spatrick struct StreamState {
82*ec727ea7Spatrick   /// The last file operation called in the stream.
83*ec727ea7Spatrick   const FnDescription *LastOperation;
84e5dd7070Spatrick 
85*ec727ea7Spatrick   /// State of a stream symbol.
86*ec727ea7Spatrick   /// FIXME: We need maybe an "escaped" state later.
87*ec727ea7Spatrick   enum KindTy {
88*ec727ea7Spatrick     Opened, /// Stream is opened.
89*ec727ea7Spatrick     Closed, /// Closed stream (an invalid stream pointer after it was closed).
90*ec727ea7Spatrick     OpenFailed /// The last open operation has failed.
91*ec727ea7Spatrick   } State;
92e5dd7070Spatrick 
93*ec727ea7Spatrick   /// State of the error flags.
94*ec727ea7Spatrick   /// Ignored in non-opened stream state but must be NoError.
95*ec727ea7Spatrick   StreamErrorState const ErrorState;
96*ec727ea7Spatrick 
97*ec727ea7Spatrick   /// Indicate if the file has an "indeterminate file position indicator".
98*ec727ea7Spatrick   /// This can be set at a failing read or write or seek operation.
99*ec727ea7Spatrick   /// If it is set no more read or write is allowed.
100*ec727ea7Spatrick   /// This value is not dependent on the stream error flags:
101*ec727ea7Spatrick   /// The error flag may be cleared with `clearerr` but the file position
102*ec727ea7Spatrick   /// remains still indeterminate.
103*ec727ea7Spatrick   /// This value applies to all error states in ErrorState except FEOF.
104*ec727ea7Spatrick   /// An EOF+indeterminate state is the same as EOF state.
105*ec727ea7Spatrick   bool const FilePositionIndeterminate = false;
106*ec727ea7Spatrick 
107*ec727ea7Spatrick   StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
108*ec727ea7Spatrick               bool IsFilePositionIndeterminate)
109*ec727ea7Spatrick       : LastOperation(L), State(S), ErrorState(ES),
110*ec727ea7Spatrick         FilePositionIndeterminate(IsFilePositionIndeterminate) {
111*ec727ea7Spatrick     assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
112*ec727ea7Spatrick            "FilePositionIndeterminate should be false in FEof case.");
113*ec727ea7Spatrick     assert((State == Opened || ErrorState.isNoError()) &&
114*ec727ea7Spatrick            "ErrorState should be None in non-opened stream state.");
115*ec727ea7Spatrick   }
116*ec727ea7Spatrick 
117*ec727ea7Spatrick   bool isOpened() const { return State == Opened; }
118*ec727ea7Spatrick   bool isClosed() const { return State == Closed; }
119*ec727ea7Spatrick   bool isOpenFailed() const { return State == OpenFailed; }
120*ec727ea7Spatrick 
121*ec727ea7Spatrick   bool operator==(const StreamState &X) const {
122*ec727ea7Spatrick     // In not opened state error state should always NoError, so comparison
123*ec727ea7Spatrick     // here is no problem.
124*ec727ea7Spatrick     return LastOperation == X.LastOperation && State == X.State &&
125*ec727ea7Spatrick            ErrorState == X.ErrorState &&
126*ec727ea7Spatrick            FilePositionIndeterminate == X.FilePositionIndeterminate;
127*ec727ea7Spatrick   }
128*ec727ea7Spatrick 
129*ec727ea7Spatrick   static StreamState getOpened(const FnDescription *L,
130*ec727ea7Spatrick                                const StreamErrorState &ES = ErrorNone,
131*ec727ea7Spatrick                                bool IsFilePositionIndeterminate = false) {
132*ec727ea7Spatrick     return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
133*ec727ea7Spatrick   }
134*ec727ea7Spatrick   static StreamState getClosed(const FnDescription *L) {
135*ec727ea7Spatrick     return StreamState{L, Closed, {}, false};
136*ec727ea7Spatrick   }
137*ec727ea7Spatrick   static StreamState getOpenFailed(const FnDescription *L) {
138*ec727ea7Spatrick     return StreamState{L, OpenFailed, {}, false};
139*ec727ea7Spatrick   }
140*ec727ea7Spatrick 
141*ec727ea7Spatrick   void Profile(llvm::FoldingSetNodeID &ID) const {
142*ec727ea7Spatrick     ID.AddPointer(LastOperation);
143*ec727ea7Spatrick     ID.AddInteger(State);
144*ec727ea7Spatrick     ID.AddInteger(ErrorState);
145*ec727ea7Spatrick     ID.AddBoolean(FilePositionIndeterminate);
146*ec727ea7Spatrick   }
147e5dd7070Spatrick };
148e5dd7070Spatrick 
149*ec727ea7Spatrick class StreamChecker;
150*ec727ea7Spatrick using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
151*ec727ea7Spatrick                                    const CallEvent &, CheckerContext &)>;
152e5dd7070Spatrick 
153*ec727ea7Spatrick using ArgNoTy = unsigned int;
154*ec727ea7Spatrick static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
155*ec727ea7Spatrick 
156*ec727ea7Spatrick struct FnDescription {
157*ec727ea7Spatrick   FnCheck PreFn;
158*ec727ea7Spatrick   FnCheck EvalFn;
159*ec727ea7Spatrick   ArgNoTy StreamArgNo;
160*ec727ea7Spatrick };
161*ec727ea7Spatrick 
162*ec727ea7Spatrick /// Get the value of the stream argument out of the passed call event.
163*ec727ea7Spatrick /// The call should contain a function that is described by Desc.
164*ec727ea7Spatrick SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
165*ec727ea7Spatrick   assert(Desc && Desc->StreamArgNo != ArgNone &&
166*ec727ea7Spatrick          "Try to get a non-existing stream argument.");
167*ec727ea7Spatrick   return Call.getArgSVal(Desc->StreamArgNo);
168*ec727ea7Spatrick }
169*ec727ea7Spatrick 
170*ec727ea7Spatrick /// Create a conjured symbol return value for a call expression.
171*ec727ea7Spatrick DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
172*ec727ea7Spatrick   assert(CE && "Expecting a call expression.");
173*ec727ea7Spatrick 
174*ec727ea7Spatrick   const LocationContext *LCtx = C.getLocationContext();
175*ec727ea7Spatrick   return C.getSValBuilder()
176*ec727ea7Spatrick       .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
177*ec727ea7Spatrick       .castAs<DefinedSVal>();
178*ec727ea7Spatrick }
179*ec727ea7Spatrick 
180*ec727ea7Spatrick ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
181*ec727ea7Spatrick                                   const CallExpr *CE) {
182*ec727ea7Spatrick   DefinedSVal RetVal = makeRetVal(C, CE);
183*ec727ea7Spatrick   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
184*ec727ea7Spatrick   State = State->assume(RetVal, true);
185*ec727ea7Spatrick   assert(State && "Assumption on new value should not fail.");
186*ec727ea7Spatrick   return State;
187*ec727ea7Spatrick }
188*ec727ea7Spatrick 
189*ec727ea7Spatrick ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
190*ec727ea7Spatrick                         CheckerContext &C, const CallExpr *CE) {
191*ec727ea7Spatrick   State = State->BindExpr(CE, C.getLocationContext(),
192*ec727ea7Spatrick                           C.getSValBuilder().makeIntVal(Value, false));
193*ec727ea7Spatrick   return State;
194*ec727ea7Spatrick }
195*ec727ea7Spatrick 
196*ec727ea7Spatrick class StreamChecker : public Checker<check::PreCall, eval::Call,
197*ec727ea7Spatrick                                      check::DeadSymbols, check::PointerEscape> {
198*ec727ea7Spatrick   BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
199*ec727ea7Spatrick   BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
200*ec727ea7Spatrick   BugType BT_UseAfterOpenFailed{this, "Invalid stream",
201*ec727ea7Spatrick                                 "Stream handling error"};
202*ec727ea7Spatrick   BugType BT_IndeterminatePosition{this, "Invalid stream state",
203*ec727ea7Spatrick                                    "Stream handling error"};
204*ec727ea7Spatrick   BugType BT_IllegalWhence{this, "Illegal whence argument",
205*ec727ea7Spatrick                            "Stream handling error"};
206*ec727ea7Spatrick   BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
207*ec727ea7Spatrick   BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error"};
208*ec727ea7Spatrick 
209*ec727ea7Spatrick public:
210*ec727ea7Spatrick   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
211*ec727ea7Spatrick   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
212*ec727ea7Spatrick   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
213*ec727ea7Spatrick   ProgramStateRef checkPointerEscape(ProgramStateRef State,
214*ec727ea7Spatrick                                      const InvalidatedSymbols &Escaped,
215*ec727ea7Spatrick                                      const CallEvent *Call,
216*ec727ea7Spatrick                                      PointerEscapeKind Kind) const;
217*ec727ea7Spatrick 
218*ec727ea7Spatrick   /// If true, evaluate special testing stream functions.
219*ec727ea7Spatrick   bool TestMode = false;
220*ec727ea7Spatrick 
221*ec727ea7Spatrick private:
222*ec727ea7Spatrick   CallDescriptionMap<FnDescription> FnDescriptions = {
223*ec727ea7Spatrick       {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
224*ec727ea7Spatrick       {{"freopen", 3},
225*ec727ea7Spatrick        {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
226*ec727ea7Spatrick       {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
227*ec727ea7Spatrick       {{"fclose", 1},
228*ec727ea7Spatrick        {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
229*ec727ea7Spatrick       {{"fread", 4},
230*ec727ea7Spatrick        {&StreamChecker::preFread,
231*ec727ea7Spatrick         std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
232*ec727ea7Spatrick       {{"fwrite", 4},
233*ec727ea7Spatrick        {&StreamChecker::preFwrite,
234*ec727ea7Spatrick         std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
235*ec727ea7Spatrick       {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
236*ec727ea7Spatrick       {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}},
237*ec727ea7Spatrick       {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}},
238*ec727ea7Spatrick       {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
239*ec727ea7Spatrick       {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
240*ec727ea7Spatrick       {{"clearerr", 1},
241*ec727ea7Spatrick        {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
242*ec727ea7Spatrick       {{"feof", 1},
243*ec727ea7Spatrick        {&StreamChecker::preDefault,
244*ec727ea7Spatrick         std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
245*ec727ea7Spatrick         0}},
246*ec727ea7Spatrick       {{"ferror", 1},
247*ec727ea7Spatrick        {&StreamChecker::preDefault,
248*ec727ea7Spatrick         std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
249*ec727ea7Spatrick         0}},
250*ec727ea7Spatrick       {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}},
251*ec727ea7Spatrick   };
252*ec727ea7Spatrick 
253*ec727ea7Spatrick   CallDescriptionMap<FnDescription> FnTestDescriptions = {
254*ec727ea7Spatrick       {{"StreamTesterChecker_make_feof_stream", 1},
255*ec727ea7Spatrick        {nullptr,
256*ec727ea7Spatrick         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
257*ec727ea7Spatrick         0}},
258*ec727ea7Spatrick       {{"StreamTesterChecker_make_ferror_stream", 1},
259*ec727ea7Spatrick        {nullptr,
260*ec727ea7Spatrick         std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
261*ec727ea7Spatrick                   ErrorFError),
262*ec727ea7Spatrick         0}},
263*ec727ea7Spatrick   };
264*ec727ea7Spatrick 
265*ec727ea7Spatrick   void evalFopen(const FnDescription *Desc, const CallEvent &Call,
266*ec727ea7Spatrick                  CheckerContext &C) const;
267*ec727ea7Spatrick 
268*ec727ea7Spatrick   void preFreopen(const FnDescription *Desc, const CallEvent &Call,
269*ec727ea7Spatrick                   CheckerContext &C) const;
270*ec727ea7Spatrick   void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
271*ec727ea7Spatrick                    CheckerContext &C) const;
272*ec727ea7Spatrick 
273*ec727ea7Spatrick   void evalFclose(const FnDescription *Desc, const CallEvent &Call,
274*ec727ea7Spatrick                   CheckerContext &C) const;
275*ec727ea7Spatrick 
276*ec727ea7Spatrick   void preFread(const FnDescription *Desc, const CallEvent &Call,
277*ec727ea7Spatrick                 CheckerContext &C) const;
278*ec727ea7Spatrick 
279*ec727ea7Spatrick   void preFwrite(const FnDescription *Desc, const CallEvent &Call,
280*ec727ea7Spatrick                  CheckerContext &C) const;
281*ec727ea7Spatrick 
282*ec727ea7Spatrick   void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
283*ec727ea7Spatrick                        CheckerContext &C, bool IsFread) const;
284*ec727ea7Spatrick 
285*ec727ea7Spatrick   void preFseek(const FnDescription *Desc, const CallEvent &Call,
286*ec727ea7Spatrick                 CheckerContext &C) const;
287*ec727ea7Spatrick   void evalFseek(const FnDescription *Desc, const CallEvent &Call,
288*ec727ea7Spatrick                  CheckerContext &C) const;
289*ec727ea7Spatrick 
290*ec727ea7Spatrick   void preDefault(const FnDescription *Desc, const CallEvent &Call,
291*ec727ea7Spatrick                   CheckerContext &C) const;
292*ec727ea7Spatrick 
293*ec727ea7Spatrick   void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
294*ec727ea7Spatrick                     CheckerContext &C) const;
295*ec727ea7Spatrick 
296*ec727ea7Spatrick   void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
297*ec727ea7Spatrick                       CheckerContext &C,
298*ec727ea7Spatrick                       const StreamErrorState &ErrorKind) const;
299*ec727ea7Spatrick 
300*ec727ea7Spatrick   void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
301*ec727ea7Spatrick                          CheckerContext &C,
302*ec727ea7Spatrick                          const StreamErrorState &ErrorKind) const;
303*ec727ea7Spatrick 
304*ec727ea7Spatrick   /// Check that the stream (in StreamVal) is not NULL.
305*ec727ea7Spatrick   /// If it can only be NULL a fatal error is emitted and nullptr returned.
306*ec727ea7Spatrick   /// Otherwise the return value is a new state where the stream is constrained
307*ec727ea7Spatrick   /// to be non-null.
308*ec727ea7Spatrick   ProgramStateRef ensureStreamNonNull(SVal StreamVal, CheckerContext &C,
309*ec727ea7Spatrick                                       ProgramStateRef State) const;
310*ec727ea7Spatrick 
311*ec727ea7Spatrick   /// Check that the stream is the opened state.
312*ec727ea7Spatrick   /// If the stream is known to be not opened an error is generated
313*ec727ea7Spatrick   /// and nullptr returned, otherwise the original state is returned.
314*ec727ea7Spatrick   ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
315*ec727ea7Spatrick                                      ProgramStateRef State) const;
316*ec727ea7Spatrick 
317*ec727ea7Spatrick   /// Check that the stream has not an invalid ("indeterminate") file position,
318*ec727ea7Spatrick   /// generate warning for it.
319*ec727ea7Spatrick   /// (EOF is not an invalid position.)
320*ec727ea7Spatrick   /// The returned state can be nullptr if a fatal error was generated.
321*ec727ea7Spatrick   /// It can return non-null state if the stream has not an invalid position or
322*ec727ea7Spatrick   /// there is execution path with non-invalid position.
323*ec727ea7Spatrick   ProgramStateRef
324*ec727ea7Spatrick   ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
325*ec727ea7Spatrick                                     ProgramStateRef State) const;
326*ec727ea7Spatrick 
327*ec727ea7Spatrick   /// Check the legality of the 'whence' argument of 'fseek'.
328*ec727ea7Spatrick   /// Generate error and return nullptr if it is found to be illegal.
329*ec727ea7Spatrick   /// Otherwise returns the state.
330*ec727ea7Spatrick   /// (State is not changed here because the "whence" value is already known.)
331*ec727ea7Spatrick   ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
332*ec727ea7Spatrick                                            ProgramStateRef State) const;
333*ec727ea7Spatrick 
334*ec727ea7Spatrick   /// Generate warning about stream in EOF state.
335*ec727ea7Spatrick   /// There will be always a state transition into the passed State,
336*ec727ea7Spatrick   /// by the new non-fatal error node or (if failed) a normal transition,
337*ec727ea7Spatrick   /// to ensure uniform handling.
338*ec727ea7Spatrick   void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const;
339*ec727ea7Spatrick 
340*ec727ea7Spatrick   /// Find the description data of the function called by a call event.
341*ec727ea7Spatrick   /// Returns nullptr if no function is recognized.
342*ec727ea7Spatrick   const FnDescription *lookupFn(const CallEvent &Call) const {
343*ec727ea7Spatrick     // Recognize "global C functions" with only integral or pointer arguments
344*ec727ea7Spatrick     // (and matching name) as stream functions.
345*ec727ea7Spatrick     if (!Call.isGlobalCFunction())
346*ec727ea7Spatrick       return nullptr;
347*ec727ea7Spatrick     for (auto P : Call.parameters()) {
348*ec727ea7Spatrick       QualType T = P->getType();
349*ec727ea7Spatrick       if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
350*ec727ea7Spatrick         return nullptr;
351*ec727ea7Spatrick     }
352*ec727ea7Spatrick 
353*ec727ea7Spatrick     return FnDescriptions.lookup(Call);
354*ec727ea7Spatrick   }
355*ec727ea7Spatrick 
356*ec727ea7Spatrick   /// Generate a message for BugReporterVisitor if the stored symbol is
357*ec727ea7Spatrick   /// marked as interesting by the actual bug report.
358*ec727ea7Spatrick   struct NoteFn {
359*ec727ea7Spatrick     const CheckerNameRef CheckerName;
360*ec727ea7Spatrick     SymbolRef StreamSym;
361*ec727ea7Spatrick     std::string Message;
362*ec727ea7Spatrick 
363*ec727ea7Spatrick     std::string operator()(PathSensitiveBugReport &BR) const {
364*ec727ea7Spatrick       if (BR.isInteresting(StreamSym) &&
365*ec727ea7Spatrick           CheckerName == BR.getBugType().getCheckerName())
366*ec727ea7Spatrick         return Message;
367*ec727ea7Spatrick 
368*ec727ea7Spatrick       return "";
369*ec727ea7Spatrick     }
370*ec727ea7Spatrick   };
371*ec727ea7Spatrick 
372*ec727ea7Spatrick   const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
373*ec727ea7Spatrick                                   const std::string &Message) const {
374*ec727ea7Spatrick     return C.getNoteTag(NoteFn{getCheckerName(), StreamSym, Message});
375*ec727ea7Spatrick   }
376*ec727ea7Spatrick 
377*ec727ea7Spatrick   /// Searches for the ExplodedNode where the file descriptor was acquired for
378*ec727ea7Spatrick   /// StreamSym.
379*ec727ea7Spatrick   static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
380*ec727ea7Spatrick                                                 SymbolRef StreamSym,
381*ec727ea7Spatrick                                                 CheckerContext &C);
382e5dd7070Spatrick };
383e5dd7070Spatrick 
384e5dd7070Spatrick } // end anonymous namespace
385e5dd7070Spatrick 
386e5dd7070Spatrick REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
387e5dd7070Spatrick 
388*ec727ea7Spatrick inline void assertStreamStateOpened(const StreamState *SS) {
389*ec727ea7Spatrick   assert(SS->isOpened() &&
390*ec727ea7Spatrick          "Previous create of error node for non-opened stream failed?");
391e5dd7070Spatrick }
392e5dd7070Spatrick 
393*ec727ea7Spatrick const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
394*ec727ea7Spatrick                                                       SymbolRef StreamSym,
395*ec727ea7Spatrick                                                       CheckerContext &C) {
396*ec727ea7Spatrick   ProgramStateRef State = N->getState();
397*ec727ea7Spatrick   // When bug type is resource leak, exploded node N may not have state info
398*ec727ea7Spatrick   // for leaked file descriptor, but predecessor should have it.
399*ec727ea7Spatrick   if (!State->get<StreamMap>(StreamSym))
400*ec727ea7Spatrick     N = N->getFirstPred();
401*ec727ea7Spatrick 
402*ec727ea7Spatrick   const ExplodedNode *Pred = N;
403*ec727ea7Spatrick   while (N) {
404*ec727ea7Spatrick     State = N->getState();
405*ec727ea7Spatrick     if (!State->get<StreamMap>(StreamSym))
406*ec727ea7Spatrick       return Pred;
407*ec727ea7Spatrick     Pred = N;
408*ec727ea7Spatrick     N = N->getFirstPred();
409*ec727ea7Spatrick   }
410*ec727ea7Spatrick 
411*ec727ea7Spatrick   return nullptr;
412*ec727ea7Spatrick }
413*ec727ea7Spatrick 
414*ec727ea7Spatrick void StreamChecker::checkPreCall(const CallEvent &Call,
415*ec727ea7Spatrick                                  CheckerContext &C) const {
416*ec727ea7Spatrick   const FnDescription *Desc = lookupFn(Call);
417*ec727ea7Spatrick   if (!Desc || !Desc->PreFn)
418*ec727ea7Spatrick     return;
419*ec727ea7Spatrick 
420*ec727ea7Spatrick   Desc->PreFn(this, Desc, Call, C);
421*ec727ea7Spatrick }
422*ec727ea7Spatrick 
423*ec727ea7Spatrick bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
424*ec727ea7Spatrick   const FnDescription *Desc = lookupFn(Call);
425*ec727ea7Spatrick   if (!Desc && TestMode)
426*ec727ea7Spatrick     Desc = FnTestDescriptions.lookup(Call);
427*ec727ea7Spatrick   if (!Desc || !Desc->EvalFn)
428e5dd7070Spatrick     return false;
429e5dd7070Spatrick 
430*ec727ea7Spatrick   Desc->EvalFn(this, Desc, Call, C);
431e5dd7070Spatrick 
432e5dd7070Spatrick   return C.isDifferent();
433e5dd7070Spatrick }
434e5dd7070Spatrick 
435*ec727ea7Spatrick void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
436*ec727ea7Spatrick                               CheckerContext &C) const {
437*ec727ea7Spatrick   ProgramStateRef State = C.getState();
438*ec727ea7Spatrick   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
439e5dd7070Spatrick   if (!CE)
440e5dd7070Spatrick     return;
441e5dd7070Spatrick 
442*ec727ea7Spatrick   DefinedSVal RetVal = makeRetVal(C, CE);
443*ec727ea7Spatrick   SymbolRef RetSym = RetVal.getAsSymbol();
444*ec727ea7Spatrick   assert(RetSym && "RetVal must be a symbol here.");
445e5dd7070Spatrick 
446*ec727ea7Spatrick   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
447*ec727ea7Spatrick 
448e5dd7070Spatrick   // Bifurcate the state into two: one with a valid FILE* pointer, the other
449e5dd7070Spatrick   // with a NULL.
450*ec727ea7Spatrick   ProgramStateRef StateNotNull, StateNull;
451*ec727ea7Spatrick   std::tie(StateNotNull, StateNull) =
452*ec727ea7Spatrick       C.getConstraintManager().assumeDual(State, RetVal);
453e5dd7070Spatrick 
454*ec727ea7Spatrick   StateNotNull =
455*ec727ea7Spatrick       StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
456*ec727ea7Spatrick   StateNull =
457*ec727ea7Spatrick       StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
458e5dd7070Spatrick 
459*ec727ea7Spatrick   C.addTransition(StateNotNull,
460*ec727ea7Spatrick                   constructNoteTag(C, RetSym, "Stream opened here"));
461*ec727ea7Spatrick   C.addTransition(StateNull);
462e5dd7070Spatrick }
463e5dd7070Spatrick 
464*ec727ea7Spatrick void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
465*ec727ea7Spatrick                                CheckerContext &C) const {
466*ec727ea7Spatrick   // Do not allow NULL as passed stream pointer but allow a closed stream.
467*ec727ea7Spatrick   ProgramStateRef State = C.getState();
468*ec727ea7Spatrick   State = ensureStreamNonNull(getStreamArg(Desc, Call), C, State);
469*ec727ea7Spatrick   if (!State)
470*ec727ea7Spatrick     return;
471*ec727ea7Spatrick 
472*ec727ea7Spatrick   C.addTransition(State);
473*ec727ea7Spatrick }
474*ec727ea7Spatrick 
475*ec727ea7Spatrick void StreamChecker::evalFreopen(const FnDescription *Desc,
476*ec727ea7Spatrick                                 const CallEvent &Call,
477e5dd7070Spatrick                                 CheckerContext &C) const {
478e5dd7070Spatrick   ProgramStateRef State = C.getState();
479e5dd7070Spatrick 
480e5dd7070Spatrick   auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
481e5dd7070Spatrick   if (!CE)
482e5dd7070Spatrick     return;
483e5dd7070Spatrick 
484*ec727ea7Spatrick   Optional<DefinedSVal> StreamVal =
485*ec727ea7Spatrick       getStreamArg(Desc, Call).getAs<DefinedSVal>();
486e5dd7070Spatrick   if (!StreamVal)
487e5dd7070Spatrick     return;
488e5dd7070Spatrick 
489e5dd7070Spatrick   SymbolRef StreamSym = StreamVal->getAsSymbol();
490*ec727ea7Spatrick   // Do not care about concrete values for stream ("(FILE *)0x12345"?).
491*ec727ea7Spatrick   // FIXME: Can be stdin, stdout, stderr such values?
492e5dd7070Spatrick   if (!StreamSym)
493e5dd7070Spatrick     return;
494e5dd7070Spatrick 
495*ec727ea7Spatrick   // Do not handle untracked stream. It is probably escaped.
496*ec727ea7Spatrick   if (!State->get<StreamMap>(StreamSym))
497*ec727ea7Spatrick     return;
498*ec727ea7Spatrick 
499e5dd7070Spatrick   // Generate state for non-failed case.
500e5dd7070Spatrick   // Return value is the passed stream pointer.
501e5dd7070Spatrick   // According to the documentations, the stream is closed first
502e5dd7070Spatrick   // but any close error is ignored. The state changes to (or remains) opened.
503e5dd7070Spatrick   ProgramStateRef StateRetNotNull =
504e5dd7070Spatrick       State->BindExpr(CE, C.getLocationContext(), *StreamVal);
505e5dd7070Spatrick   // Generate state for NULL return value.
506e5dd7070Spatrick   // Stream switches to OpenFailed state.
507e5dd7070Spatrick   ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
508e5dd7070Spatrick                                                  C.getSValBuilder().makeNull());
509e5dd7070Spatrick 
510e5dd7070Spatrick   StateRetNotNull =
511*ec727ea7Spatrick       StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
512e5dd7070Spatrick   StateRetNull =
513*ec727ea7Spatrick       StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
514e5dd7070Spatrick 
515*ec727ea7Spatrick   C.addTransition(StateRetNotNull,
516*ec727ea7Spatrick                   constructNoteTag(C, StreamSym, "Stream reopened here"));
517e5dd7070Spatrick   C.addTransition(StateRetNull);
518e5dd7070Spatrick }
519e5dd7070Spatrick 
520*ec727ea7Spatrick void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
521*ec727ea7Spatrick                                CheckerContext &C) const {
522e5dd7070Spatrick   ProgramStateRef State = C.getState();
523*ec727ea7Spatrick   SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
524*ec727ea7Spatrick   if (!Sym)
525*ec727ea7Spatrick     return;
526*ec727ea7Spatrick 
527*ec727ea7Spatrick   const StreamState *SS = State->get<StreamMap>(Sym);
528*ec727ea7Spatrick   if (!SS)
529*ec727ea7Spatrick     return;
530*ec727ea7Spatrick 
531*ec727ea7Spatrick   assertStreamStateOpened(SS);
532*ec727ea7Spatrick 
533*ec727ea7Spatrick   // Close the File Descriptor.
534*ec727ea7Spatrick   // Regardless if the close fails or not, stream becomes "closed"
535*ec727ea7Spatrick   // and can not be used any more.
536*ec727ea7Spatrick   State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
537*ec727ea7Spatrick 
538e5dd7070Spatrick   C.addTransition(State);
539e5dd7070Spatrick }
540e5dd7070Spatrick 
541*ec727ea7Spatrick void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
542*ec727ea7Spatrick                              CheckerContext &C) const {
543e5dd7070Spatrick   ProgramStateRef State = C.getState();
544*ec727ea7Spatrick   SVal StreamVal = getStreamArg(Desc, Call);
545*ec727ea7Spatrick   State = ensureStreamNonNull(StreamVal, C, State);
546*ec727ea7Spatrick   if (!State)
547*ec727ea7Spatrick     return;
548*ec727ea7Spatrick   State = ensureStreamOpened(StreamVal, C, State);
549*ec727ea7Spatrick   if (!State)
550*ec727ea7Spatrick     return;
551*ec727ea7Spatrick   State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
552*ec727ea7Spatrick   if (!State)
553e5dd7070Spatrick     return;
554e5dd7070Spatrick 
555*ec727ea7Spatrick   SymbolRef Sym = StreamVal.getAsSymbol();
556*ec727ea7Spatrick   if (Sym && State->get<StreamMap>(Sym)) {
557*ec727ea7Spatrick     const StreamState *SS = State->get<StreamMap>(Sym);
558*ec727ea7Spatrick     if (SS->ErrorState & ErrorFEof)
559*ec727ea7Spatrick       reportFEofWarning(C, State);
560*ec727ea7Spatrick   } else {
561e5dd7070Spatrick     C.addTransition(State);
562*ec727ea7Spatrick   }
563e5dd7070Spatrick }
564e5dd7070Spatrick 
565*ec727ea7Spatrick void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call,
566*ec727ea7Spatrick                               CheckerContext &C) const {
567e5dd7070Spatrick   ProgramStateRef State = C.getState();
568*ec727ea7Spatrick   SVal StreamVal = getStreamArg(Desc, Call);
569*ec727ea7Spatrick   State = ensureStreamNonNull(StreamVal, C, State);
570*ec727ea7Spatrick   if (!State)
571*ec727ea7Spatrick     return;
572*ec727ea7Spatrick   State = ensureStreamOpened(StreamVal, C, State);
573*ec727ea7Spatrick   if (!State)
574*ec727ea7Spatrick     return;
575*ec727ea7Spatrick   State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
576*ec727ea7Spatrick   if (!State)
577*ec727ea7Spatrick     return;
578*ec727ea7Spatrick 
579e5dd7070Spatrick   C.addTransition(State);
580e5dd7070Spatrick }
581e5dd7070Spatrick 
582*ec727ea7Spatrick void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
583*ec727ea7Spatrick                                     const CallEvent &Call, CheckerContext &C,
584*ec727ea7Spatrick                                     bool IsFread) const {
585*ec727ea7Spatrick   ProgramStateRef State = C.getState();
586*ec727ea7Spatrick   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
587*ec727ea7Spatrick   if (!StreamSym)
588*ec727ea7Spatrick     return;
589*ec727ea7Spatrick 
590*ec727ea7Spatrick   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
591*ec727ea7Spatrick   if (!CE)
592*ec727ea7Spatrick     return;
593*ec727ea7Spatrick 
594*ec727ea7Spatrick   Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
595*ec727ea7Spatrick   if (!SizeVal)
596*ec727ea7Spatrick     return;
597*ec727ea7Spatrick   Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
598*ec727ea7Spatrick   if (!NMembVal)
599*ec727ea7Spatrick     return;
600*ec727ea7Spatrick 
601*ec727ea7Spatrick   const StreamState *SS = State->get<StreamMap>(StreamSym);
602*ec727ea7Spatrick   if (!SS)
603*ec727ea7Spatrick     return;
604*ec727ea7Spatrick 
605*ec727ea7Spatrick   assertStreamStateOpened(SS);
606*ec727ea7Spatrick 
607*ec727ea7Spatrick   // C'99 standard, §7.19.8.1.3, the return value of fread:
608*ec727ea7Spatrick   // The fread function returns the number of elements successfully read, which
609*ec727ea7Spatrick   // may be less than nmemb if a read error or end-of-file is encountered. If
610*ec727ea7Spatrick   // size or nmemb is zero, fread returns zero and the contents of the array and
611*ec727ea7Spatrick   // the state of the stream remain unchanged.
612*ec727ea7Spatrick 
613*ec727ea7Spatrick   if (State->isNull(*SizeVal).isConstrainedTrue() ||
614*ec727ea7Spatrick       State->isNull(*NMembVal).isConstrainedTrue()) {
615*ec727ea7Spatrick     // This is the "size or nmemb is zero" case.
616*ec727ea7Spatrick     // Just return 0, do nothing more (not clear the error flags).
617*ec727ea7Spatrick     State = bindInt(0, State, C, CE);
618*ec727ea7Spatrick     C.addTransition(State);
619*ec727ea7Spatrick     return;
620*ec727ea7Spatrick   }
621*ec727ea7Spatrick 
622*ec727ea7Spatrick   // Generate a transition for the success state.
623*ec727ea7Spatrick   // If we know the state to be FEOF at fread, do not add a success state.
624*ec727ea7Spatrick   if (!IsFread || (SS->ErrorState != ErrorFEof)) {
625*ec727ea7Spatrick     ProgramStateRef StateNotFailed =
626*ec727ea7Spatrick         State->BindExpr(CE, C.getLocationContext(), *NMembVal);
627*ec727ea7Spatrick     if (StateNotFailed) {
628*ec727ea7Spatrick       StateNotFailed = StateNotFailed->set<StreamMap>(
629*ec727ea7Spatrick           StreamSym, StreamState::getOpened(Desc));
630*ec727ea7Spatrick       C.addTransition(StateNotFailed);
631*ec727ea7Spatrick     }
632*ec727ea7Spatrick   }
633*ec727ea7Spatrick 
634*ec727ea7Spatrick   // Add transition for the failed state.
635*ec727ea7Spatrick   Optional<NonLoc> RetVal = makeRetVal(C, CE).castAs<NonLoc>();
636*ec727ea7Spatrick   assert(RetVal && "Value should be NonLoc.");
637*ec727ea7Spatrick   ProgramStateRef StateFailed =
638*ec727ea7Spatrick       State->BindExpr(CE, C.getLocationContext(), *RetVal);
639*ec727ea7Spatrick   if (!StateFailed)
640*ec727ea7Spatrick     return;
641*ec727ea7Spatrick   auto Cond = C.getSValBuilder()
642*ec727ea7Spatrick                   .evalBinOpNN(State, BO_LT, *RetVal, *NMembVal,
643*ec727ea7Spatrick                                C.getASTContext().IntTy)
644*ec727ea7Spatrick                   .getAs<DefinedOrUnknownSVal>();
645*ec727ea7Spatrick   if (!Cond)
646*ec727ea7Spatrick     return;
647*ec727ea7Spatrick   StateFailed = StateFailed->assume(*Cond, true);
648*ec727ea7Spatrick   if (!StateFailed)
649*ec727ea7Spatrick     return;
650*ec727ea7Spatrick 
651*ec727ea7Spatrick   StreamErrorState NewES;
652*ec727ea7Spatrick   if (IsFread)
653*ec727ea7Spatrick     NewES = (SS->ErrorState == ErrorFEof) ? ErrorFEof : ErrorFEof | ErrorFError;
654*ec727ea7Spatrick   else
655*ec727ea7Spatrick     NewES = ErrorFError;
656*ec727ea7Spatrick   // If a (non-EOF) error occurs, the resulting value of the file position
657*ec727ea7Spatrick   // indicator for the stream is indeterminate.
658*ec727ea7Spatrick   StreamState NewState = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
659*ec727ea7Spatrick   StateFailed = StateFailed->set<StreamMap>(StreamSym, NewState);
660*ec727ea7Spatrick   C.addTransition(StateFailed);
661*ec727ea7Spatrick }
662*ec727ea7Spatrick 
663*ec727ea7Spatrick void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
664*ec727ea7Spatrick                              CheckerContext &C) const {
665*ec727ea7Spatrick   ProgramStateRef State = C.getState();
666*ec727ea7Spatrick   SVal StreamVal = getStreamArg(Desc, Call);
667*ec727ea7Spatrick   State = ensureStreamNonNull(StreamVal, C, State);
668*ec727ea7Spatrick   if (!State)
669*ec727ea7Spatrick     return;
670*ec727ea7Spatrick   State = ensureStreamOpened(StreamVal, C, State);
671*ec727ea7Spatrick   if (!State)
672*ec727ea7Spatrick     return;
673*ec727ea7Spatrick   State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
674*ec727ea7Spatrick   if (!State)
675*ec727ea7Spatrick     return;
676*ec727ea7Spatrick 
677*ec727ea7Spatrick   C.addTransition(State);
678*ec727ea7Spatrick }
679*ec727ea7Spatrick 
680*ec727ea7Spatrick void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
681*ec727ea7Spatrick                               CheckerContext &C) const {
682*ec727ea7Spatrick   ProgramStateRef State = C.getState();
683*ec727ea7Spatrick   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
684*ec727ea7Spatrick   if (!StreamSym)
685*ec727ea7Spatrick     return;
686*ec727ea7Spatrick 
687*ec727ea7Spatrick   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
688*ec727ea7Spatrick   if (!CE)
689*ec727ea7Spatrick     return;
690*ec727ea7Spatrick 
691*ec727ea7Spatrick   // Ignore the call if the stream is not tracked.
692*ec727ea7Spatrick   if (!State->get<StreamMap>(StreamSym))
693*ec727ea7Spatrick     return;
694*ec727ea7Spatrick 
695*ec727ea7Spatrick   DefinedSVal RetVal = makeRetVal(C, CE);
696*ec727ea7Spatrick 
697*ec727ea7Spatrick   // Make expression result.
698*ec727ea7Spatrick   State = State->BindExpr(CE, C.getLocationContext(), RetVal);
699*ec727ea7Spatrick 
700*ec727ea7Spatrick   // Bifurcate the state into failed and non-failed.
701*ec727ea7Spatrick   // Return zero on success, nonzero on error.
702*ec727ea7Spatrick   ProgramStateRef StateNotFailed, StateFailed;
703*ec727ea7Spatrick   std::tie(StateFailed, StateNotFailed) =
704*ec727ea7Spatrick       C.getConstraintManager().assumeDual(State, RetVal);
705*ec727ea7Spatrick 
706*ec727ea7Spatrick   // Reset the state to opened with no error.
707*ec727ea7Spatrick   StateNotFailed =
708*ec727ea7Spatrick       StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
709*ec727ea7Spatrick   // We get error.
710*ec727ea7Spatrick   // It is possible that fseek fails but sets none of the error flags.
711*ec727ea7Spatrick   // If fseek failed, assume that the file position becomes indeterminate in any
712*ec727ea7Spatrick   // case.
713*ec727ea7Spatrick   StateFailed = StateFailed->set<StreamMap>(
714*ec727ea7Spatrick       StreamSym,
715*ec727ea7Spatrick       StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true));
716*ec727ea7Spatrick 
717*ec727ea7Spatrick   C.addTransition(StateNotFailed);
718*ec727ea7Spatrick   C.addTransition(StateFailed);
719*ec727ea7Spatrick }
720*ec727ea7Spatrick 
721*ec727ea7Spatrick void StreamChecker::evalClearerr(const FnDescription *Desc,
722*ec727ea7Spatrick                                  const CallEvent &Call,
723*ec727ea7Spatrick                                  CheckerContext &C) const {
724*ec727ea7Spatrick   ProgramStateRef State = C.getState();
725*ec727ea7Spatrick   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
726*ec727ea7Spatrick   if (!StreamSym)
727*ec727ea7Spatrick     return;
728*ec727ea7Spatrick 
729*ec727ea7Spatrick   const StreamState *SS = State->get<StreamMap>(StreamSym);
730*ec727ea7Spatrick   if (!SS)
731*ec727ea7Spatrick     return;
732*ec727ea7Spatrick 
733*ec727ea7Spatrick   assertStreamStateOpened(SS);
734*ec727ea7Spatrick 
735*ec727ea7Spatrick   // FilePositionIndeterminate is not cleared.
736*ec727ea7Spatrick   State = State->set<StreamMap>(
737*ec727ea7Spatrick       StreamSym,
738*ec727ea7Spatrick       StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
739*ec727ea7Spatrick   C.addTransition(State);
740*ec727ea7Spatrick }
741*ec727ea7Spatrick 
742*ec727ea7Spatrick void StreamChecker::evalFeofFerror(const FnDescription *Desc,
743*ec727ea7Spatrick                                    const CallEvent &Call, CheckerContext &C,
744*ec727ea7Spatrick                                    const StreamErrorState &ErrorKind) const {
745*ec727ea7Spatrick   ProgramStateRef State = C.getState();
746*ec727ea7Spatrick   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
747*ec727ea7Spatrick   if (!StreamSym)
748*ec727ea7Spatrick     return;
749*ec727ea7Spatrick 
750*ec727ea7Spatrick   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
751*ec727ea7Spatrick   if (!CE)
752*ec727ea7Spatrick     return;
753*ec727ea7Spatrick 
754*ec727ea7Spatrick   const StreamState *SS = State->get<StreamMap>(StreamSym);
755*ec727ea7Spatrick   if (!SS)
756*ec727ea7Spatrick     return;
757*ec727ea7Spatrick 
758*ec727ea7Spatrick   assertStreamStateOpened(SS);
759*ec727ea7Spatrick 
760*ec727ea7Spatrick   if (SS->ErrorState & ErrorKind) {
761*ec727ea7Spatrick     // Execution path with error of ErrorKind.
762*ec727ea7Spatrick     // Function returns true.
763*ec727ea7Spatrick     // From now on it is the only one error state.
764*ec727ea7Spatrick     ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
765*ec727ea7Spatrick     C.addTransition(TrueState->set<StreamMap>(
766*ec727ea7Spatrick         StreamSym, StreamState::getOpened(Desc, ErrorKind,
767*ec727ea7Spatrick                                           SS->FilePositionIndeterminate &&
768*ec727ea7Spatrick                                               !ErrorKind.isFEof())));
769*ec727ea7Spatrick   }
770*ec727ea7Spatrick   if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
771*ec727ea7Spatrick     // Execution path(s) with ErrorKind not set.
772*ec727ea7Spatrick     // Function returns false.
773*ec727ea7Spatrick     // New error state is everything before minus ErrorKind.
774*ec727ea7Spatrick     ProgramStateRef FalseState = bindInt(0, State, C, CE);
775*ec727ea7Spatrick     C.addTransition(FalseState->set<StreamMap>(
776*ec727ea7Spatrick         StreamSym,
777*ec727ea7Spatrick         StreamState::getOpened(
778*ec727ea7Spatrick             Desc, NewES, SS->FilePositionIndeterminate && !NewES.isFEof())));
779*ec727ea7Spatrick   }
780*ec727ea7Spatrick }
781*ec727ea7Spatrick 
782*ec727ea7Spatrick void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
783*ec727ea7Spatrick                                CheckerContext &C) const {
784*ec727ea7Spatrick   ProgramStateRef State = C.getState();
785*ec727ea7Spatrick   SVal StreamVal = getStreamArg(Desc, Call);
786*ec727ea7Spatrick   State = ensureStreamNonNull(StreamVal, C, State);
787*ec727ea7Spatrick   if (!State)
788*ec727ea7Spatrick     return;
789*ec727ea7Spatrick   State = ensureStreamOpened(StreamVal, C, State);
790*ec727ea7Spatrick   if (!State)
791*ec727ea7Spatrick     return;
792*ec727ea7Spatrick 
793*ec727ea7Spatrick   C.addTransition(State);
794*ec727ea7Spatrick }
795*ec727ea7Spatrick 
796*ec727ea7Spatrick void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
797*ec727ea7Spatrick                                       const CallEvent &Call, CheckerContext &C,
798*ec727ea7Spatrick                                       const StreamErrorState &ErrorKind) const {
799*ec727ea7Spatrick   ProgramStateRef State = C.getState();
800*ec727ea7Spatrick   SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
801*ec727ea7Spatrick   assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
802*ec727ea7Spatrick   const StreamState *SS = State->get<StreamMap>(StreamSym);
803*ec727ea7Spatrick   assert(SS && "Stream should be tracked by the checker.");
804*ec727ea7Spatrick   State = State->set<StreamMap>(
805*ec727ea7Spatrick       StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
806*ec727ea7Spatrick   C.addTransition(State);
807*ec727ea7Spatrick }
808*ec727ea7Spatrick 
809*ec727ea7Spatrick ProgramStateRef
810*ec727ea7Spatrick StreamChecker::ensureStreamNonNull(SVal StreamVal, CheckerContext &C,
811*ec727ea7Spatrick                                    ProgramStateRef State) const {
812*ec727ea7Spatrick   auto Stream = StreamVal.getAs<DefinedSVal>();
813*ec727ea7Spatrick   if (!Stream)
814*ec727ea7Spatrick     return State;
815e5dd7070Spatrick 
816e5dd7070Spatrick   ConstraintManager &CM = C.getConstraintManager();
817*ec727ea7Spatrick 
818e5dd7070Spatrick   ProgramStateRef StateNotNull, StateNull;
819*ec727ea7Spatrick   std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
820e5dd7070Spatrick 
821e5dd7070Spatrick   if (!StateNotNull && StateNull) {
822e5dd7070Spatrick     if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
823e5dd7070Spatrick       C.emitReport(std::make_unique<PathSensitiveBugReport>(
824*ec727ea7Spatrick           BT_FileNull, "Stream pointer might be NULL.", N));
825e5dd7070Spatrick     }
826*ec727ea7Spatrick     return nullptr;
827e5dd7070Spatrick   }
828e5dd7070Spatrick 
829*ec727ea7Spatrick   return StateNotNull;
830e5dd7070Spatrick }
831e5dd7070Spatrick 
832*ec727ea7Spatrick ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
833*ec727ea7Spatrick                                                   CheckerContext &C,
834*ec727ea7Spatrick                                                   ProgramStateRef State) const {
835*ec727ea7Spatrick   SymbolRef Sym = StreamVal.getAsSymbol();
836*ec727ea7Spatrick   if (!Sym)
837*ec727ea7Spatrick     return State;
838*ec727ea7Spatrick 
839*ec727ea7Spatrick   const StreamState *SS = State->get<StreamMap>(Sym);
840*ec727ea7Spatrick   if (!SS)
841*ec727ea7Spatrick     return State;
842*ec727ea7Spatrick 
843*ec727ea7Spatrick   if (SS->isClosed()) {
844*ec727ea7Spatrick     // Using a stream pointer after 'fclose' causes undefined behavior
845*ec727ea7Spatrick     // according to cppreference.com .
846*ec727ea7Spatrick     ExplodedNode *N = C.generateErrorNode();
847*ec727ea7Spatrick     if (N) {
848*ec727ea7Spatrick       C.emitReport(std::make_unique<PathSensitiveBugReport>(
849*ec727ea7Spatrick           BT_UseAfterClose,
850*ec727ea7Spatrick           "Stream might be already closed. Causes undefined behaviour.", N));
851*ec727ea7Spatrick       return nullptr;
852e5dd7070Spatrick     }
853e5dd7070Spatrick 
854*ec727ea7Spatrick     return State;
855*ec727ea7Spatrick   }
856*ec727ea7Spatrick 
857*ec727ea7Spatrick   if (SS->isOpenFailed()) {
858*ec727ea7Spatrick     // Using a stream that has failed to open is likely to cause problems.
859*ec727ea7Spatrick     // This should usually not occur because stream pointer is NULL.
860*ec727ea7Spatrick     // But freopen can cause a state when stream pointer remains non-null but
861*ec727ea7Spatrick     // failed to open.
862*ec727ea7Spatrick     ExplodedNode *N = C.generateErrorNode();
863*ec727ea7Spatrick     if (N) {
864*ec727ea7Spatrick       C.emitReport(std::make_unique<PathSensitiveBugReport>(
865*ec727ea7Spatrick           BT_UseAfterOpenFailed,
866*ec727ea7Spatrick           "Stream might be invalid after "
867*ec727ea7Spatrick           "(re-)opening it has failed. "
868*ec727ea7Spatrick           "Can cause undefined behaviour.",
869*ec727ea7Spatrick           N));
870*ec727ea7Spatrick       return nullptr;
871*ec727ea7Spatrick     }
872*ec727ea7Spatrick     return State;
873*ec727ea7Spatrick   }
874*ec727ea7Spatrick 
875*ec727ea7Spatrick   return State;
876*ec727ea7Spatrick }
877*ec727ea7Spatrick 
878*ec727ea7Spatrick ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
879*ec727ea7Spatrick     SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
880*ec727ea7Spatrick   static const char *BugMessage =
881*ec727ea7Spatrick       "File position of the stream might be 'indeterminate' "
882*ec727ea7Spatrick       "after a failed operation. "
883*ec727ea7Spatrick       "Can cause undefined behavior.";
884*ec727ea7Spatrick 
885*ec727ea7Spatrick   SymbolRef Sym = StreamVal.getAsSymbol();
886*ec727ea7Spatrick   if (!Sym)
887*ec727ea7Spatrick     return State;
888*ec727ea7Spatrick 
889*ec727ea7Spatrick   const StreamState *SS = State->get<StreamMap>(Sym);
890*ec727ea7Spatrick   if (!SS)
891*ec727ea7Spatrick     return State;
892*ec727ea7Spatrick 
893*ec727ea7Spatrick   assert(SS->isOpened() && "First ensure that stream is opened.");
894*ec727ea7Spatrick 
895*ec727ea7Spatrick   if (SS->FilePositionIndeterminate) {
896*ec727ea7Spatrick     if (SS->ErrorState & ErrorFEof) {
897*ec727ea7Spatrick       // The error is unknown but may be FEOF.
898*ec727ea7Spatrick       // Continue analysis with the FEOF error state.
899*ec727ea7Spatrick       // Report warning because the other possible error states.
900*ec727ea7Spatrick       ExplodedNode *N = C.generateNonFatalErrorNode(State);
901*ec727ea7Spatrick       if (!N)
902*ec727ea7Spatrick         return nullptr;
903*ec727ea7Spatrick 
904*ec727ea7Spatrick       C.emitReport(std::make_unique<PathSensitiveBugReport>(
905*ec727ea7Spatrick           BT_IndeterminatePosition, BugMessage, N));
906*ec727ea7Spatrick       return State->set<StreamMap>(
907*ec727ea7Spatrick           Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
908*ec727ea7Spatrick     }
909*ec727ea7Spatrick 
910*ec727ea7Spatrick     // Known or unknown error state without FEOF possible.
911*ec727ea7Spatrick     // Stop analysis, report error.
912*ec727ea7Spatrick     ExplodedNode *N = C.generateErrorNode(State);
913*ec727ea7Spatrick     if (N)
914*ec727ea7Spatrick       C.emitReport(std::make_unique<PathSensitiveBugReport>(
915*ec727ea7Spatrick           BT_IndeterminatePosition, BugMessage, N));
916*ec727ea7Spatrick 
917*ec727ea7Spatrick     return nullptr;
918*ec727ea7Spatrick   }
919*ec727ea7Spatrick 
920*ec727ea7Spatrick   return State;
921*ec727ea7Spatrick }
922*ec727ea7Spatrick 
923*ec727ea7Spatrick ProgramStateRef
924*ec727ea7Spatrick StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
925*ec727ea7Spatrick                                         ProgramStateRef State) const {
926*ec727ea7Spatrick   Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>();
927e5dd7070Spatrick   if (!CI)
928*ec727ea7Spatrick     return State;
929e5dd7070Spatrick 
930e5dd7070Spatrick   int64_t X = CI->getValue().getSExtValue();
931e5dd7070Spatrick   if (X >= 0 && X <= 2)
932*ec727ea7Spatrick     return State;
933e5dd7070Spatrick 
934e5dd7070Spatrick   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
935*ec727ea7Spatrick     C.emitReport(std::make_unique<PathSensitiveBugReport>(
936*ec727ea7Spatrick         BT_IllegalWhence,
937e5dd7070Spatrick         "The whence argument to fseek() should be "
938*ec727ea7Spatrick         "SEEK_SET, SEEK_END, or SEEK_CUR.",
939*ec727ea7Spatrick         N));
940*ec727ea7Spatrick     return nullptr;
941*ec727ea7Spatrick   }
942*ec727ea7Spatrick 
943*ec727ea7Spatrick   return State;
944*ec727ea7Spatrick }
945*ec727ea7Spatrick 
946*ec727ea7Spatrick void StreamChecker::reportFEofWarning(CheckerContext &C,
947*ec727ea7Spatrick                                       ProgramStateRef State) const {
948*ec727ea7Spatrick   if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
949e5dd7070Spatrick     C.emitReport(std::make_unique<PathSensitiveBugReport>(
950*ec727ea7Spatrick         BT_StreamEof,
951*ec727ea7Spatrick         "Read function called when stream is in EOF state. "
952*ec727ea7Spatrick         "Function has no effect.",
953*ec727ea7Spatrick         N));
954*ec727ea7Spatrick     return;
955e5dd7070Spatrick   }
956*ec727ea7Spatrick   C.addTransition(State);
957e5dd7070Spatrick }
958e5dd7070Spatrick 
959e5dd7070Spatrick void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
960e5dd7070Spatrick                                      CheckerContext &C) const {
961e5dd7070Spatrick   ProgramStateRef State = C.getState();
962e5dd7070Spatrick 
963e5dd7070Spatrick   // TODO: Clean up the state.
964e5dd7070Spatrick   const StreamMapTy &Map = State->get<StreamMap>();
965e5dd7070Spatrick   for (const auto &I : Map) {
966e5dd7070Spatrick     SymbolRef Sym = I.first;
967e5dd7070Spatrick     const StreamState &SS = I.second;
968e5dd7070Spatrick     if (!SymReaper.isDead(Sym) || !SS.isOpened())
969e5dd7070Spatrick       continue;
970e5dd7070Spatrick 
971e5dd7070Spatrick     ExplodedNode *N = C.generateErrorNode();
972e5dd7070Spatrick     if (!N)
973e5dd7070Spatrick       continue;
974e5dd7070Spatrick 
975*ec727ea7Spatrick     // Do not warn for non-closed stream at program exit.
976*ec727ea7Spatrick     ExplodedNode *Pred = C.getPredecessor();
977*ec727ea7Spatrick     if (Pred && Pred->getCFGBlock() &&
978*ec727ea7Spatrick         Pred->getCFGBlock()->hasNoReturnElement())
979*ec727ea7Spatrick       continue;
980*ec727ea7Spatrick 
981*ec727ea7Spatrick     // Resource leaks can result in multiple warning that describe the same kind
982*ec727ea7Spatrick     // of programming error:
983*ec727ea7Spatrick     //  void f() {
984*ec727ea7Spatrick     //    FILE *F = fopen("a.txt");
985*ec727ea7Spatrick     //    if (rand()) // state split
986*ec727ea7Spatrick     //      return; // warning
987*ec727ea7Spatrick     //  } // warning
988*ec727ea7Spatrick     // While this isn't necessarily true (leaking the same stream could result
989*ec727ea7Spatrick     // from a different kinds of errors), the reduction in redundant reports
990*ec727ea7Spatrick     // makes this a worthwhile heuristic.
991*ec727ea7Spatrick     // FIXME: Add a checker option to turn this uniqueing feature off.
992*ec727ea7Spatrick 
993*ec727ea7Spatrick     const ExplodedNode *StreamOpenNode = getAcquisitionSite(N, Sym, C);
994*ec727ea7Spatrick     assert(StreamOpenNode && "Could not find place of stream opening.");
995*ec727ea7Spatrick     PathDiagnosticLocation LocUsedForUniqueing =
996*ec727ea7Spatrick         PathDiagnosticLocation::createBegin(
997*ec727ea7Spatrick             StreamOpenNode->getStmtForDiagnostics(), C.getSourceManager(),
998*ec727ea7Spatrick             StreamOpenNode->getLocationContext());
999*ec727ea7Spatrick 
1000*ec727ea7Spatrick     std::unique_ptr<PathSensitiveBugReport> R =
1001*ec727ea7Spatrick         std::make_unique<PathSensitiveBugReport>(
1002*ec727ea7Spatrick             BT_ResourceLeak,
1003*ec727ea7Spatrick             "Opened stream never closed. Potential resource leak.", N,
1004*ec727ea7Spatrick             LocUsedForUniqueing,
1005*ec727ea7Spatrick             StreamOpenNode->getLocationContext()->getDecl());
1006*ec727ea7Spatrick     R->markInteresting(Sym);
1007*ec727ea7Spatrick     C.emitReport(std::move(R));
1008e5dd7070Spatrick   }
1009e5dd7070Spatrick }
1010e5dd7070Spatrick 
1011*ec727ea7Spatrick ProgramStateRef StreamChecker::checkPointerEscape(
1012*ec727ea7Spatrick     ProgramStateRef State, const InvalidatedSymbols &Escaped,
1013*ec727ea7Spatrick     const CallEvent *Call, PointerEscapeKind Kind) const {
1014*ec727ea7Spatrick   // Check for file-handling system call that is not handled by the checker.
1015*ec727ea7Spatrick   // FIXME: The checker should be updated to handle all system calls that take
1016*ec727ea7Spatrick   // 'FILE*' argument. These are now ignored.
1017*ec727ea7Spatrick   if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
1018*ec727ea7Spatrick     return State;
1019*ec727ea7Spatrick 
1020*ec727ea7Spatrick   for (SymbolRef Sym : Escaped) {
1021*ec727ea7Spatrick     // The symbol escaped.
1022*ec727ea7Spatrick     // From now the stream can be manipulated in unknown way to the checker,
1023*ec727ea7Spatrick     // it is not possible to handle it any more.
1024*ec727ea7Spatrick     // Optimistically, assume that the corresponding file handle will be closed
1025*ec727ea7Spatrick     // somewhere else.
1026*ec727ea7Spatrick     // Remove symbol from state so the following stream calls on this symbol are
1027*ec727ea7Spatrick     // not handled by the checker.
1028*ec727ea7Spatrick     State = State->remove<StreamMap>(Sym);
1029*ec727ea7Spatrick   }
1030*ec727ea7Spatrick   return State;
1031e5dd7070Spatrick }
1032e5dd7070Spatrick 
1033*ec727ea7Spatrick void ento::registerStreamChecker(CheckerManager &Mgr) {
1034*ec727ea7Spatrick   Mgr.registerChecker<StreamChecker>();
1035*ec727ea7Spatrick }
1036*ec727ea7Spatrick 
1037*ec727ea7Spatrick bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
1038*ec727ea7Spatrick   return true;
1039*ec727ea7Spatrick }
1040*ec727ea7Spatrick 
1041*ec727ea7Spatrick void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
1042*ec727ea7Spatrick   auto *Checker = Mgr.getChecker<StreamChecker>();
1043*ec727ea7Spatrick   Checker->TestMode = true;
1044*ec727ea7Spatrick }
1045*ec727ea7Spatrick 
1046*ec727ea7Spatrick bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
1047e5dd7070Spatrick   return true;
1048e5dd7070Spatrick }
1049