Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
// This file defines checkers that model and check stream handling functions.
10
//
11
//===----------------------------------------------------------------------===//
12
13
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
15
#include "clang/StaticAnalyzer/Core/Checker.h"
16
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
17
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
18
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
21
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
22
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
23
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
24
#include <functional>
25
#include <optional>
26
27
using namespace clang;
28
using namespace ento;
29
using namespace std::placeholders;
30
31
//===----------------------------------------------------------------------===//
32
// Definition of state data structures.
33
//===----------------------------------------------------------------------===//
34
35
namespace {
36
37
struct FnDescription;
38
39
/// State of the stream error flags.
40
/// Sometimes it is not known to the checker what error flags are set.
41
/// This is indicated by setting more than one flag to true.
42
/// This is an optimization to avoid state splits.
43
/// A stream can either be in FEOF or FERROR but not both at the same time.
44
/// Multiple flags are set to handle the corresponding states together.
45
struct StreamErrorState {
46
  /// The stream can be in state where none of the error flags set.
47
  bool NoError = true;
48
  /// The stream can be in state where the EOF indicator is set.
49
  bool FEof = false;
50
  /// The stream can be in state where the error indicator is set.
51
  bool FError = false;
52
53
289
  bool isNoError() const { return NoError && !FEof && !FError; }
54
758
  bool isFEof() const { return !NoError && 
FEof183
&&
!FError95
; }
55
0
  bool isFError() const { return !NoError && !FEof && FError; }
56
57
193
  bool operator==(const StreamErrorState &ES) const {
58
193
    return NoError == ES.NoError && 
FEof == ES.FEof133
&&
FError == ES.FError133
;
59
193
  }
60
61
54
  bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
62
63
42
  StreamErrorState operator|(const StreamErrorState &E) const {
64
42
    return {NoError || 
E.NoError20
, FEof ||
E.FEof22
, FError ||
E.FError36
};
65
42
  }
66
67
231
  StreamErrorState operator&(const StreamErrorState &E) const {
68
231
    return {NoError && 
E.NoError138
, FEof &&
E.FEof59
, FError &&
E.FError78
};
69
231
  }
70
71
93
  StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
72
73
  /// Returns if the StreamErrorState is a valid object.
74
231
  operator bool() const { return NoError || 
FEof177
||
FError143
; }
75
76
688
  void Profile(llvm::FoldingSetNodeID &ID) const {
77
688
    ID.AddBoolean(NoError);
78
688
    ID.AddBoolean(FEof);
79
688
    ID.AddBoolean(FError);
80
688
  }
81
};
82
83
const StreamErrorState ErrorNone{true, false, false};
84
const StreamErrorState ErrorFEof{false, true, false};
85
const StreamErrorState ErrorFError{false, false, true};
86
87
/// Full state information about a stream pointer.
88
struct StreamState {
89
  /// The last file operation called in the stream.
90
  /// Can be nullptr.
91
  const FnDescription *LastOperation;
92
93
  /// State of a stream symbol.
94
  enum KindTy {
95
    Opened, /// Stream is opened.
96
    Closed, /// Closed stream (an invalid stream pointer after it was closed).
97
    OpenFailed /// The last open operation has failed.
98
  } State;
99
100
  /// State of the error flags.
101
  /// Ignored in non-opened stream state but must be NoError.
102
  StreamErrorState const ErrorState;
103
104
  /// Indicate if the file has an "indeterminate file position indicator".
105
  /// This can be set at a failing read or write or seek operation.
106
  /// If it is set no more read or write is allowed.
107
  /// This value is not dependent on the stream error flags:
108
  /// The error flag may be cleared with `clearerr` but the file position
109
  /// remains still indeterminate.
110
  /// This value applies to all error states in ErrorState except FEOF.
111
  /// An EOF+indeterminate state is the same as EOF state.
112
  bool const FilePositionIndeterminate = false;
113
114
  StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
115
              bool IsFilePositionIndeterminate)
116
664
      : LastOperation(L), State(S), ErrorState(ES),
117
664
        FilePositionIndeterminate(IsFilePositionIndeterminate) {
118
664
    assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
119
664
           "FilePositionIndeterminate should be false in FEof case.");
120
664
    assert((State == Opened || ErrorState.isNoError()) &&
121
664
           "ErrorState should be None in non-opened stream state.");
122
664
  }
123
124
750
  bool isOpened() const { return State == Opened; }
125
376
  bool isClosed() const { return State == Closed; }
126
369
  bool isOpenFailed() const { return State == OpenFailed; }
127
128
112
  bool operator==(const StreamState &X) const {
129
    // In not opened state error state should always NoError, so comparison
130
    // here is no problem.
131
112
    return LastOperation == X.LastOperation && State == X.State &&
132
112
           ErrorState == X.ErrorState &&
133
112
           FilePositionIndeterminate == X.FilePositionIndeterminate;
134
112
  }
135
136
  static StreamState getOpened(const FnDescription *L,
137
                               const StreamErrorState &ES = ErrorNone,
138
375
                               bool IsFilePositionIndeterminate = false) {
139
375
    return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
140
375
  }
141
156
  static StreamState getClosed(const FnDescription *L) {
142
156
    return StreamState{L, Closed, {}, false};
143
156
  }
144
133
  static StreamState getOpenFailed(const FnDescription *L) {
145
133
    return StreamState{L, OpenFailed, {}, false};
146
133
  }
147
148
688
  void Profile(llvm::FoldingSetNodeID &ID) const {
149
688
    ID.AddPointer(LastOperation);
150
688
    ID.AddInteger(State);
151
688
    ErrorState.Profile(ID);
152
688
    ID.AddBoolean(FilePositionIndeterminate);
153
688
  }
154
};
155
156
} // namespace
157
158
//===----------------------------------------------------------------------===//
159
// StreamChecker class and utility functions.
160
//===----------------------------------------------------------------------===//
161
162
namespace {
163
164
class StreamChecker;
165
using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
166
                                   const CallEvent &, CheckerContext &)>;
167
168
using ArgNoTy = unsigned int;
169
static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
170
171
struct FnDescription {
172
  FnCheck PreFn;
173
  FnCheck EvalFn;
174
  ArgNoTy StreamArgNo;
175
};
176
177
/// Get the value of the stream argument out of the passed call event.
178
/// The call should contain a function that is described by Desc.
179
917
SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
180
917
  assert(Desc && Desc->StreamArgNo != ArgNone &&
181
917
         "Try to get a non-existing stream argument.");
182
917
  return Call.getArgSVal(Desc->StreamArgNo);
183
917
}
184
185
/// Create a conjured symbol return value for a call expression.
186
219
DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
187
219
  assert(CE && "Expecting a call expression.");
188
189
219
  const LocationContext *LCtx = C.getLocationContext();
190
219
  return C.getSValBuilder()
191
219
      .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
192
219
      .castAs<DefinedSVal>();
193
219
}
194
195
ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
196
32
                                  const CallExpr *CE) {
197
32
  DefinedSVal RetVal = makeRetVal(C, CE);
198
32
  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
199
32
  State = State->assume(RetVal, true);
200
32
  assert(State && "Assumption on new value should not fail.");
201
32
  return State;
202
32
}
203
204
ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
205
99
                        CheckerContext &C, const CallExpr *CE) {
206
99
  State = State->BindExpr(CE, C.getLocationContext(),
207
99
                          C.getSValBuilder().makeIntVal(Value, CE->getType()));
208
99
  return State;
209
99
}
210
211
class StreamChecker : public Checker<check::PreCall, eval::Call,
212
                                     check::DeadSymbols, check::PointerEscape> {
213
  BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
214
  BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
215
  BugType BT_UseAfterOpenFailed{this, "Invalid stream",
216
                                "Stream handling error"};
217
  BugType BT_IndeterminatePosition{this, "Invalid stream state",
218
                                   "Stream handling error"};
219
  BugType BT_IllegalWhence{this, "Illegal whence argument",
220
                           "Stream handling error"};
221
  BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
222
  BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
223
                          /*SuppressOnSink =*/true};
224
225
public:
226
  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
227
  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
228
  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
229
  ProgramStateRef checkPointerEscape(ProgramStateRef State,
230
                                     const InvalidatedSymbols &Escaped,
231
                                     const CallEvent *Call,
232
                                     PointerEscapeKind Kind) const;
233
234
  /// If true, evaluate special testing stream functions.
235
  bool TestMode = false;
236
237
7
  const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
238
239
private:
240
  CallDescriptionMap<FnDescription> FnDescriptions = {
241
      {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
242
      {{{"freopen"}, 3},
243
       {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
244
      {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
245
      {{{"fclose"}, 1},
246
       {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
247
      {{{"fread"}, 4},
248
       {&StreamChecker::preFread,
249
        std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
250
      {{{"fwrite"}, 4},
251
       {&StreamChecker::preFwrite,
252
        std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
253
      {{{"fseek"}, 3},
254
       {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
255
      {{{"ftell"}, 1},
256
       {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
257
      {{{"rewind"}, 1},
258
       {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
259
      {{{"fgetpos"}, 2},
260
       {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
261
      {{{"fsetpos"}, 2},
262
       {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
263
      {{{"clearerr"}, 1},
264
       {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
265
      {{{"feof"}, 1},
266
       {&StreamChecker::preDefault,
267
        std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
268
        0}},
269
      {{{"ferror"}, 1},
270
       {&StreamChecker::preDefault,
271
        std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
272
        0}},
273
      {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
274
  };
275
276
  CallDescriptionMap<FnDescription> FnTestDescriptions = {
277
      {{{"StreamTesterChecker_make_feof_stream"}, 1},
278
       {nullptr,
279
        std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
280
        0}},
281
      {{{"StreamTesterChecker_make_ferror_stream"}, 1},
282
       {nullptr,
283
        std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
284
                  ErrorFError),
285
        0}},
286
  };
287
288
  /// Expanded value of EOF, empty before initialization.
289
  mutable std::optional<int> EofVal;
290
  /// Expanded value of SEEK_SET, 0 if not found.
291
  mutable int SeekSetVal = 0;
292
  /// Expanded value of SEEK_CUR, 1 if not found.
293
  mutable int SeekCurVal = 1;
294
  /// Expanded value of SEEK_END, 2 if not found.
295
  mutable int SeekEndVal = 2;
296
297
  void evalFopen(const FnDescription *Desc, const CallEvent &Call,
298
                 CheckerContext &C) const;
299
300
  void preFreopen(const FnDescription *Desc, const CallEvent &Call,
301
                  CheckerContext &C) const;
302
  void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
303
                   CheckerContext &C) const;
304
305
  void evalFclose(const FnDescription *Desc, const CallEvent &Call,
306
                  CheckerContext &C) const;
307
308
  void preFread(const FnDescription *Desc, const CallEvent &Call,
309
                CheckerContext &C) const;
310
311
  void preFwrite(const FnDescription *Desc, const CallEvent &Call,
312
                 CheckerContext &C) const;
313
314
  void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
315
                       CheckerContext &C, bool IsFread) const;
316
317
  void preFseek(const FnDescription *Desc, const CallEvent &Call,
318
                CheckerContext &C) const;
319
  void evalFseek(const FnDescription *Desc, const CallEvent &Call,
320
                 CheckerContext &C) const;
321
322
  void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
323
                   CheckerContext &C) const;
324
325
  void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
326
                   CheckerContext &C) const;
327
328
  void evalFtell(const FnDescription *Desc, const CallEvent &Call,
329
                 CheckerContext &C) const;
330
331
  void evalRewind(const FnDescription *Desc, const CallEvent &Call,
332
                  CheckerContext &C) const;
333
334
  void preDefault(const FnDescription *Desc, const CallEvent &Call,
335
                  CheckerContext &C) const;
336
337
  void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
338
                    CheckerContext &C) const;
339
340
  void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
341
                      CheckerContext &C,
342
                      const StreamErrorState &ErrorKind) const;
343
344
  void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
345
                         CheckerContext &C,
346
                         const StreamErrorState &ErrorKind) const;
347
348
  /// Check that the stream (in StreamVal) is not NULL.
349
  /// If it can only be NULL a fatal error is emitted and nullptr returned.
350
  /// Otherwise the return value is a new state where the stream is constrained
351
  /// to be non-null.
352
  ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
353
                                      CheckerContext &C,
354
                                      ProgramStateRef State) const;
355
356
  /// Check that the stream is the opened state.
357
  /// If the stream is known to be not opened an error is generated
358
  /// and nullptr returned, otherwise the original state is returned.
359
  ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
360
                                     ProgramStateRef State) const;
361
362
  /// Check that the stream has not an invalid ("indeterminate") file position,
363
  /// generate warning for it.
364
  /// (EOF is not an invalid position.)
365
  /// The returned state can be nullptr if a fatal error was generated.
366
  /// It can return non-null state if the stream has not an invalid position or
367
  /// there is execution path with non-invalid position.
368
  ProgramStateRef
369
  ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
370
                                    ProgramStateRef State) const;
371
372
  /// Check the legality of the 'whence' argument of 'fseek'.
373
  /// Generate error and return nullptr if it is found to be illegal.
374
  /// Otherwise returns the state.
375
  /// (State is not changed here because the "whence" value is already known.)
376
  ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
377
                                           ProgramStateRef State) const;
378
379
  /// Generate warning about stream in EOF state.
380
  /// There will be always a state transition into the passed State,
381
  /// by the new non-fatal error node or (if failed) a normal transition,
382
  /// to ensure uniform handling.
383
  void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
384
                         ProgramStateRef State) const;
385
386
  /// Emit resource leak warnings for the given symbols.
387
  /// Createn a non-fatal error node for these, and returns it (if any warnings
388
  /// were generated). Return value is non-null.
389
  ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
390
                            CheckerContext &C, ExplodedNode *Pred) const;
391
392
  /// Find the description data of the function called by a call event.
393
  /// Returns nullptr if no function is recognized.
394
1.83k
  const FnDescription *lookupFn(const CallEvent &Call) const {
395
    // Recognize "global C functions" with only integral or pointer arguments
396
    // (and matching name) as stream functions.
397
1.83k
    if (!Call.isGlobalCFunction())
398
16
      return nullptr;
399
2.29k
    
for (auto *P : Call.parameters())1.82k
{
400
2.29k
      QualType T = P->getType();
401
2.29k
      if (!T->isIntegralOrEnumerationType() && 
!T->isPointerType()1.40k
)
402
2
        return nullptr;
403
2.29k
    }
404
405
1.81k
    return FnDescriptions.lookup(Call);
406
1.82k
  }
407
408
  /// Generate a message for BugReporterVisitor if the stored symbol is
409
  /// marked as interesting by the actual bug report.
410
  // FIXME: Use lambda instead.
411
  struct NoteFn {
412
    const BugType *BT_ResourceLeak;
413
    SymbolRef StreamSym;
414
    std::string Message;
415
416
195
    std::string operator()(PathSensitiveBugReport &BR) const {
417
195
      if (BR.isInteresting(StreamSym) && 
&BR.getBugType() == BT_ResourceLeak18
)
418
16
        return Message;
419
420
179
      return "";
421
195
    }
422
  };
423
424
  const NoteTag *constructNoteTag(CheckerContext &C, SymbolRef StreamSym,
425
133
                                  const std::string &Message) const {
426
133
    return C.getNoteTag(NoteFn{&BT_ResourceLeak, StreamSym, Message});
427
133
  }
428
429
  const NoteTag *constructSetEofNoteTag(CheckerContext &C,
430
32
                                        SymbolRef StreamSym) const {
431
42
    return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
432
42
      if (!BR.isInteresting(StreamSym) ||
433
42
          
&BR.getBugType() != this->getBT_StreamEof()7
)
434
35
        return "";
435
436
7
      BR.markNotInteresting(StreamSym);
437
438
7
      return "Assuming stream reaches end-of-file here";
439
42
    });
440
32
  }
441
442
951
  void initMacroValues(CheckerContext &C) const {
443
951
    if (EofVal)
444
935
      return;
445
446
16
    if (const std::optional<int> OptInt =
447
16
            tryExpandAsInteger("EOF", C.getPreprocessor()))
448
12
      EofVal = *OptInt;
449
4
    else
450
4
      EofVal = -1;
451
16
    if (const std::optional<int> OptInt =
452
16
            tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
453
12
      SeekSetVal = *OptInt;
454
16
    if (const std::optional<int> OptInt =
455
16
            tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
456
12
      SeekEndVal = *OptInt;
457
16
    if (const std::optional<int> OptInt =
458
16
            tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
459
12
      SeekCurVal = *OptInt;
460
16
  }
461
462
  /// Searches for the ExplodedNode where the file descriptor was acquired for
463
  /// StreamSym.
464
  static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
465
                                                SymbolRef StreamSym,
466
                                                CheckerContext &C);
467
};
468
469
} // end anonymous namespace
470
471
// This map holds the state of a stream.
472
// The stream is identified with a SymbolRef that is created when a stream
473
// opening function is modeled by the checker.
474
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
475
476
335
inline void assertStreamStateOpened(const StreamState *SS) {
477
335
  assert(SS->isOpened() && "Stream is expected to be opened");
478
335
}
479
480
const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
481
                                                      SymbolRef StreamSym,
482
29
                                                      CheckerContext &C) {
483
29
  ProgramStateRef State = N->getState();
484
  // When bug type is resource leak, exploded node N may not have state info
485
  // for leaked file descriptor, but predecessor should have it.
486
29
  if (!State->get<StreamMap>(StreamSym))
487
0
    N = N->getFirstPred();
488
489
29
  const ExplodedNode *Pred = N;
490
1.02k
  while (N) {
491
1.02k
    State = N->getState();
492
1.02k
    if (!State->get<StreamMap>(StreamSym))
493
29
      return Pred;
494
991
    Pred = N;
495
991
    N = N->getFirstPred();
496
991
  }
497
498
0
  return nullptr;
499
29
}
500
501
//===----------------------------------------------------------------------===//
502
// Methods of StreamChecker.
503
//===----------------------------------------------------------------------===//
504
505
void StreamChecker::checkPreCall(const CallEvent &Call,
506
951
                                 CheckerContext &C) const {
507
951
  initMacroValues(C);
508
509
951
  const FnDescription *Desc = lookupFn(Call);
510
951
  if (!Desc || 
!Desc->PreFn614
)
511
460
    return;
512
513
491
  Desc->PreFn(this, Desc, Call, C);
514
491
}
515
516
885
bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
517
885
  const FnDescription *Desc = lookupFn(Call);
518
885
  if (!Desc && 
TestMode335
)
519
71
    Desc = FnTestDescriptions.lookup(Call);
520
885
  if (!Desc || 
!Desc->EvalFn554
)
521
336
    return false;
522
523
549
  Desc->EvalFn(this, Desc, Call, C);
524
525
549
  return C.isDifferent();
526
885
}
527
528
void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
529
123
                              CheckerContext &C) const {
530
123
  ProgramStateRef State = C.getState();
531
123
  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
532
123
  if (!CE)
533
0
    return;
534
535
123
  DefinedSVal RetVal = makeRetVal(C, CE);
536
123
  SymbolRef RetSym = RetVal.getAsSymbol();
537
123
  assert(RetSym && "RetVal must be a symbol here.");
538
539
123
  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
540
541
  // Bifurcate the state into two: one with a valid FILE* pointer, the other
542
  // with a NULL.
543
123
  ProgramStateRef StateNotNull, StateNull;
544
123
  std::tie(StateNotNull, StateNull) =
545
123
      C.getConstraintManager().assumeDual(State, RetVal);
546
547
123
  StateNotNull =
548
123
      StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
549
123
  StateNull =
550
123
      StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
551
552
123
  C.addTransition(StateNotNull,
553
123
                  constructNoteTag(C, RetSym, "Stream opened here"));
554
123
  C.addTransition(StateNull);
555
123
}
556
557
void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
558
15
                               CheckerContext &C) const {
559
  // Do not allow NULL as passed stream pointer but allow a closed stream.
560
15
  ProgramStateRef State = C.getState();
561
15
  State = ensureStreamNonNull(getStreamArg(Desc, Call),
562
15
                              Call.getArgExpr(Desc->StreamArgNo), C, State);
563
15
  if (!State)
564
3
    return;
565
566
12
  C.addTransition(State);
567
12
}
568
569
void StreamChecker::evalFreopen(const FnDescription *Desc,
570
                                const CallEvent &Call,
571
12
                                CheckerContext &C) const {
572
12
  ProgramStateRef State = C.getState();
573
574
12
  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
575
12
  if (!CE)
576
0
    return;
577
578
12
  std::optional<DefinedSVal> StreamVal =
579
12
      getStreamArg(Desc, Call).getAs<DefinedSVal>();
580
12
  if (!StreamVal)
581
0
    return;
582
583
12
  SymbolRef StreamSym = StreamVal->getAsSymbol();
584
  // Do not care about concrete values for stream ("(FILE *)0x12345"?).
585
  // FIXME: Can be stdin, stdout, stderr such values?
586
12
  if (!StreamSym)
587
0
    return;
588
589
  // Do not handle untracked stream. It is probably escaped.
590
12
  if (!State->get<StreamMap>(StreamSym))
591
2
    return;
592
593
  // Generate state for non-failed case.
594
  // Return value is the passed stream pointer.
595
  // According to the documentations, the stream is closed first
596
  // but any close error is ignored. The state changes to (or remains) opened.
597
10
  ProgramStateRef StateRetNotNull =
598
10
      State->BindExpr(CE, C.getLocationContext(), *StreamVal);
599
  // Generate state for NULL return value.
600
  // Stream switches to OpenFailed state.
601
10
  ProgramStateRef StateRetNull =
602
10
      State->BindExpr(CE, C.getLocationContext(),
603
10
                      C.getSValBuilder().makeNullWithType(CE->getType()));
604
605
10
  StateRetNotNull =
606
10
      StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
607
10
  StateRetNull =
608
10
      StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
609
610
10
  C.addTransition(StateRetNotNull,
611
10
                  constructNoteTag(C, StreamSym, "Stream reopened here"));
612
10
  C.addTransition(StateRetNull);
613
10
}
614
615
void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
616
158
                               CheckerContext &C) const {
617
158
  ProgramStateRef State = C.getState();
618
158
  SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
619
158
  if (!Sym)
620
0
    return;
621
622
158
  const StreamState *SS = State->get<StreamMap>(Sym);
623
158
  if (!SS)
624
2
    return;
625
626
156
  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
627
156
  if (!CE)
628
0
    return;
629
630
156
  assertStreamStateOpened(SS);
631
632
  // Close the File Descriptor.
633
  // Regardless if the close fails or not, stream becomes "closed"
634
  // and can not be used any more.
635
156
  State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
636
637
  // Return 0 on success, EOF on failure.
638
156
  SValBuilder &SVB = C.getSValBuilder();
639
156
  ProgramStateRef StateSuccess = State->BindExpr(
640
156
      CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy));
641
156
  ProgramStateRef StateFailure =
642
156
      State->BindExpr(CE, C.getLocationContext(),
643
156
                      SVB.makeIntVal(*EofVal, C.getASTContext().IntTy));
644
645
156
  C.addTransition(StateSuccess);
646
156
  C.addTransition(StateFailure);
647
156
}
648
649
void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
650
50
                             CheckerContext &C) const {
651
50
  ProgramStateRef State = C.getState();
652
50
  SVal StreamVal = getStreamArg(Desc, Call);
653
50
  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
654
50
                              State);
655
50
  if (!State)
656
4
    return;
657
46
  State = ensureStreamOpened(StreamVal, C, State);
658
46
  if (!State)
659
2
    return;
660
44
  State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
661
44
  if (!State)
662
1
    return;
663
664
43
  SymbolRef Sym = StreamVal.getAsSymbol();
665
43
  if (Sym && State->get<StreamMap>(Sym)) {
666
38
    const StreamState *SS = State->get<StreamMap>(Sym);
667
38
    if (SS->ErrorState & ErrorFEof)
668
9
      reportFEofWarning(Sym, C, State);
669
38
  } else {
670
5
    C.addTransition(State);
671
5
  }
672
43
}
673
674
void StreamChecker::preFwrite(const FnDescription *Desc, const CallEvent &Call,
675
41
                              CheckerContext &C) const {
676
41
  ProgramStateRef State = C.getState();
677
41
  SVal StreamVal = getStreamArg(Desc, Call);
678
41
  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
679
41
                              State);
680
41
  if (!State)
681
3
    return;
682
38
  State = ensureStreamOpened(StreamVal, C, State);
683
38
  if (!State)
684
1
    return;
685
37
  State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
686
37
  if (!State)
687
6
    return;
688
689
31
  C.addTransition(State);
690
31
}
691
692
void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
693
                                    const CallEvent &Call, CheckerContext &C,
694
74
                                    bool IsFread) const {
695
74
  ProgramStateRef State = C.getState();
696
74
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
697
74
  if (!StreamSym)
698
0
    return;
699
700
74
  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
701
74
  if (!CE)
702
0
    return;
703
704
74
  std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
705
74
  if (!SizeVal)
706
0
    return;
707
74
  std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
708
74
  if (!NMembVal)
709
0
    return;
710
711
74
  const StreamState *OldSS = State->get<StreamMap>(StreamSym);
712
74
  if (!OldSS)
713
14
    return;
714
715
60
  assertStreamStateOpened(OldSS);
716
717
  // C'99 standard, ยง7.19.8.1.3, the return value of fread:
718
  // The fread function returns the number of elements successfully read, which
719
  // may be less than nmemb if a read error or end-of-file is encountered. If
720
  // size or nmemb is zero, fread returns zero and the contents of the array and
721
  // the state of the stream remain unchanged.
722
723
60
  if (State->isNull(*SizeVal).isConstrainedTrue() ||
724
60
      
State->isNull(*NMembVal).isConstrainedTrue()49
) {
725
    // This is the "size or nmemb is zero" case.
726
    // Just return 0, do nothing more (not clear the error flags).
727
20
    State = bindInt(0, State, C, CE);
728
20
    C.addTransition(State);
729
20
    return;
730
20
  }
731
732
  // Generate a transition for the success state.
733
  // If we know the state to be FEOF at fread, do not add a success state.
734
40
  if (!IsFread || 
(OldSS->ErrorState != ErrorFEof)27
) {
735
33
    ProgramStateRef StateNotFailed =
736
33
        State->BindExpr(CE, C.getLocationContext(), *NMembVal);
737
33
    StateNotFailed =
738
33
        StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
739
33
    C.addTransition(StateNotFailed);
740
33
  }
741
742
  // Add transition for the failed state.
743
40
  NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
744
40
  ProgramStateRef StateFailed =
745
40
      State->BindExpr(CE, C.getLocationContext(), RetVal);
746
40
  auto Cond =
747
40
      C.getSValBuilder()
748
40
          .evalBinOpNN(State, BO_LT, RetVal, *NMembVal, C.getASTContext().IntTy)
749
40
          .getAs<DefinedOrUnknownSVal>();
750
40
  if (!Cond)
751
0
    return;
752
40
  StateFailed = StateFailed->assume(*Cond, true);
753
40
  if (!StateFailed)
754
0
    return;
755
756
40
  StreamErrorState NewES;
757
40
  if (IsFread)
758
27
    NewES =
759
27
        (OldSS->ErrorState == ErrorFEof) ? 
ErrorFEof7
:
ErrorFEof | ErrorFError20
;
760
13
  else
761
13
    NewES = ErrorFError;
762
  // If a (non-EOF) error occurs, the resulting value of the file position
763
  // indicator for the stream is indeterminate.
764
40
  StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof());
765
40
  StateFailed = StateFailed->set<StreamMap>(StreamSym, NewSS);
766
40
  if (IsFread && 
OldSS->ErrorState != ErrorFEof27
)
767
20
    C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
768
20
  else
769
20
    C.addTransition(StateFailed);
770
40
}
771
772
void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
773
17
                             CheckerContext &C) const {
774
17
  ProgramStateRef State = C.getState();
775
17
  SVal StreamVal = getStreamArg(Desc, Call);
776
17
  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
777
17
                              State);
778
17
  if (!State)
779
3
    return;
780
14
  State = ensureStreamOpened(StreamVal, C, State);
781
14
  if (!State)
782
0
    return;
783
14
  State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
784
14
  if (!State)
785
2
    return;
786
787
12
  C.addTransition(State);
788
12
}
789
790
void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
791
14
                              CheckerContext &C) const {
792
14
  ProgramStateRef State = C.getState();
793
14
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
794
14
  if (!StreamSym)
795
0
    return;
796
797
14
  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
798
14
  if (!CE)
799
0
    return;
800
801
  // Ignore the call if the stream is not tracked.
802
14
  if (!State->get<StreamMap>(StreamSym))
803
2
    return;
804
805
12
  const llvm::APSInt *PosV =
806
12
      C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1));
807
12
  const llvm::APSInt *WhenceV =
808
12
      C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2));
809
810
12
  DefinedSVal RetVal = makeRetVal(C, CE);
811
812
  // Make expression result.
813
12
  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
814
815
  // Bifurcate the state into failed and non-failed.
816
  // Return zero on success, nonzero on error.
817
12
  ProgramStateRef StateNotFailed, StateFailed;
818
12
  std::tie(StateFailed, StateNotFailed) =
819
12
      C.getConstraintManager().assumeDual(State, RetVal);
820
821
  // Reset the state to opened with no error.
822
12
  StateNotFailed =
823
12
      StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
824
  // We get error.
825
  // It is possible that fseek fails but sets none of the error flags.
826
  // If fseek failed, assume that the file position becomes indeterminate in any
827
  // case.
828
12
  StreamErrorState NewErrS = ErrorNone | ErrorFError;
829
  // Setting the position to start of file never produces EOF error.
830
12
  if (!(PosV && *PosV == 0 && 
WhenceV6
&&
*WhenceV == SeekSetVal6
))
831
6
    NewErrS = NewErrS | ErrorFEof;
832
12
  StateFailed = StateFailed->set<StreamMap>(
833
12
      StreamSym, StreamState::getOpened(Desc, NewErrS, true));
834
835
12
  C.addTransition(StateNotFailed);
836
12
  C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
837
12
}
838
839
void StreamChecker::evalFgetpos(const FnDescription *Desc,
840
                                const CallEvent &Call,
841
5
                                CheckerContext &C) const {
842
5
  ProgramStateRef State = C.getState();
843
5
  SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
844
5
  if (!Sym)
845
0
    return;
846
847
  // Do not evaluate if stream is not found.
848
5
  if (!State->get<StreamMap>(Sym))
849
1
    return;
850
851
4
  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
852
4
  if (!CE)
853
0
    return;
854
855
4
  DefinedSVal RetVal = makeRetVal(C, CE);
856
4
  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
857
4
  ProgramStateRef StateNotFailed, StateFailed;
858
4
  std::tie(StateFailed, StateNotFailed) =
859
4
      C.getConstraintManager().assumeDual(State, RetVal);
860
861
  // This function does not affect the stream state.
862
  // Still we add success and failure state with the appropriate return value.
863
  // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
864
4
  C.addTransition(StateNotFailed);
865
4
  C.addTransition(StateFailed);
866
4
}
867
868
void StreamChecker::evalFsetpos(const FnDescription *Desc,
869
                                const CallEvent &Call,
870
5
                                CheckerContext &C) const {
871
5
  ProgramStateRef State = C.getState();
872
5
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
873
5
  if (!StreamSym)
874
0
    return;
875
876
5
  const StreamState *SS = State->get<StreamMap>(StreamSym);
877
5
  if (!SS)
878
1
    return;
879
880
4
  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
881
4
  if (!CE)
882
0
    return;
883
884
4
  assertStreamStateOpened(SS);
885
886
4
  DefinedSVal RetVal = makeRetVal(C, CE);
887
4
  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
888
4
  ProgramStateRef StateNotFailed, StateFailed;
889
4
  std::tie(StateFailed, StateNotFailed) =
890
4
      C.getConstraintManager().assumeDual(State, RetVal);
891
892
4
  StateNotFailed = StateNotFailed->set<StreamMap>(
893
4
      StreamSym, StreamState::getOpened(Desc, ErrorNone, false));
894
895
  // At failure ferror could be set.
896
  // The standards do not tell what happens with the file position at failure.
897
  // But we can assume that it is dangerous to make a next I/O operation after
898
  // the position was not set correctly (similar to 'fseek').
899
4
  StateFailed = StateFailed->set<StreamMap>(
900
4
      StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
901
902
4
  C.addTransition(StateNotFailed);
903
4
  C.addTransition(StateFailed);
904
4
}
905
906
void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
907
5
                              CheckerContext &C) const {
908
5
  ProgramStateRef State = C.getState();
909
5
  SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
910
5
  if (!Sym)
911
0
    return;
912
913
5
  if (!State->get<StreamMap>(Sym))
914
1
    return;
915
916
4
  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
917
4
  if (!CE)
918
0
    return;
919
920
4
  SValBuilder &SVB = C.getSValBuilder();
921
4
  NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
922
4
  ProgramStateRef StateNotFailed =
923
4
      State->BindExpr(CE, C.getLocationContext(), RetVal);
924
4
  auto Cond = SVB.evalBinOp(State, BO_GE, RetVal,
925
4
                            SVB.makeZeroVal(C.getASTContext().LongTy),
926
4
                            SVB.getConditionType())
927
4
                  .getAs<DefinedOrUnknownSVal>();
928
4
  if (!Cond)
929
0
    return;
930
4
  StateNotFailed = StateNotFailed->assume(*Cond, true);
931
4
  if (!StateNotFailed)
932
0
    return;
933
934
4
  ProgramStateRef StateFailed = State->BindExpr(
935
4
      CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
936
937
4
  C.addTransition(StateNotFailed);
938
4
  C.addTransition(StateFailed);
939
4
}
940
941
void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
942
15
                               CheckerContext &C) const {
943
15
  ProgramStateRef State = C.getState();
944
15
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
945
15
  if (!StreamSym)
946
0
    return;
947
948
15
  const StreamState *SS = State->get<StreamMap>(StreamSym);
949
15
  if (!SS)
950
3
    return;
951
952
12
  auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
953
12
  if (!CE)
954
0
    return;
955
956
12
  assertStreamStateOpened(SS);
957
958
12
  State = State->set<StreamMap>(StreamSym,
959
12
                                StreamState::getOpened(Desc, ErrorNone, false));
960
961
12
  C.addTransition(State);
962
12
}
963
964
void StreamChecker::evalClearerr(const FnDescription *Desc,
965
                                 const CallEvent &Call,
966
11
                                 CheckerContext &C) const {
967
11
  ProgramStateRef State = C.getState();
968
11
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
969
11
  if (!StreamSym)
970
0
    return;
971
972
11
  const StreamState *SS = State->get<StreamMap>(StreamSym);
973
11
  if (!SS)
974
1
    return;
975
976
10
  assertStreamStateOpened(SS);
977
978
  // FilePositionIndeterminate is not cleared.
979
10
  State = State->set<StreamMap>(
980
10
      StreamSym,
981
10
      StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate));
982
10
  C.addTransition(State);
983
10
}
984
985
void StreamChecker::evalFeofFerror(const FnDescription *Desc,
986
                                   const CallEvent &Call, CheckerContext &C,
987
123
                                   const StreamErrorState &ErrorKind) const {
988
123
  ProgramStateRef State = C.getState();
989
123
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
990
123
  if (!StreamSym)
991
0
    return;
992
993
123
  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
994
123
  if (!CE)
995
0
    return;
996
997
123
  const StreamState *SS = State->get<StreamMap>(StreamSym);
998
123
  if (!SS)
999
30
    return;
1000
1001
93
  assertStreamStateOpened(SS);
1002
1003
93
  if (SS->ErrorState & ErrorKind) {
1004
    // Execution path with error of ErrorKind.
1005
    // Function returns true.
1006
    // From now on it is the only one error state.
1007
32
    ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE);
1008
32
    C.addTransition(TrueState->set<StreamMap>(
1009
32
        StreamSym, StreamState::getOpened(Desc, ErrorKind,
1010
32
                                          SS->FilePositionIndeterminate &&
1011
32
                                              
!ErrorKind.isFEof()25
)));
1012
32
  }
1013
93
  if (StreamErrorState NewES = SS->ErrorState & (~ErrorKind)) {
1014
    // Execution path(s) with ErrorKind not set.
1015
    // Function returns false.
1016
    // New error state is everything before minus ErrorKind.
1017
79
    ProgramStateRef FalseState = bindInt(0, State, C, CE);
1018
79
    C.addTransition(FalseState->set<StreamMap>(
1019
79
        StreamSym,
1020
79
        StreamState::getOpened(
1021
79
            Desc, NewES, SS->FilePositionIndeterminate && 
!NewES.isFEof()29
)));
1022
79
  }
1023
93
}
1024
1025
void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
1026
368
                               CheckerContext &C) const {
1027
368
  ProgramStateRef State = C.getState();
1028
368
  SVal StreamVal = getStreamArg(Desc, Call);
1029
368
  State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1030
368
                              State);
1031
368
  if (!State)
1032
35
    return;
1033
333
  State = ensureStreamOpened(StreamVal, C, State);
1034
333
  if (!State)
1035
6
    return;
1036
1037
327
  C.addTransition(State);
1038
327
}
1039
1040
void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
1041
                                      const CallEvent &Call, CheckerContext &C,
1042
4
                                      const StreamErrorState &ErrorKind) const {
1043
4
  ProgramStateRef State = C.getState();
1044
4
  SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1045
4
  assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
1046
4
  const StreamState *SS = State->get<StreamMap>(StreamSym);
1047
4
  assert(SS && "Stream should be tracked by the checker.");
1048
4
  State = State->set<StreamMap>(
1049
4
      StreamSym, StreamState::getOpened(SS->LastOperation, ErrorKind));
1050
4
  C.addTransition(State);
1051
4
}
1052
1053
ProgramStateRef
1054
StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1055
                                   CheckerContext &C,
1056
491
                                   ProgramStateRef State) const {
1057
491
  auto Stream = StreamVal.getAs<DefinedSVal>();
1058
491
  if (!Stream)
1059
0
    return State;
1060
1061
491
  ConstraintManager &CM = C.getConstraintManager();
1062
1063
491
  ProgramStateRef StateNotNull, StateNull;
1064
491
  std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
1065
1066
491
  if (!StateNotNull && 
StateNull48
) {
1067
48
    if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
1068
48
      auto R = std::make_unique<PathSensitiveBugReport>(
1069
48
          BT_FileNull, "Stream pointer might be NULL.", N);
1070
48
      if (StreamE)
1071
48
        bugreporter::trackExpressionValue(N, StreamE, *R);
1072
48
      C.emitReport(std::move(R));
1073
48
    }
1074
48
    return nullptr;
1075
48
  }
1076
1077
443
  return StateNotNull;
1078
491
}
1079
1080
ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
1081
                                                  CheckerContext &C,
1082
431
                                                  ProgramStateRef State) const {
1083
431
  SymbolRef Sym = StreamVal.getAsSymbol();
1084
431
  if (!Sym)
1085
0
    return State;
1086
1087
431
  const StreamState *SS = State->get<StreamMap>(Sym);
1088
431
  if (!SS)
1089
55
    return State;
1090
1091
376
  if (SS->isClosed()) {
1092
    // Using a stream pointer after 'fclose' causes undefined behavior
1093
    // according to cppreference.com .
1094
7
    ExplodedNode *N = C.generateErrorNode();
1095
7
    if (N) {
1096
7
      C.emitReport(std::make_unique<PathSensitiveBugReport>(
1097
7
          BT_UseAfterClose,
1098
7
          "Stream might be already closed. Causes undefined behaviour.", N));
1099
7
      return nullptr;
1100
7
    }
1101
1102
0
    return State;
1103
7
  }
1104
1105
369
  if (SS->isOpenFailed()) {
1106
    // Using a stream that has failed to open is likely to cause problems.
1107
    // This should usually not occur because stream pointer is NULL.
1108
    // But freopen can cause a state when stream pointer remains non-null but
1109
    // failed to open.
1110
2
    ExplodedNode *N = C.generateErrorNode();
1111
2
    if (N) {
1112
2
      C.emitReport(std::make_unique<PathSensitiveBugReport>(
1113
2
          BT_UseAfterOpenFailed,
1114
2
          "Stream might be invalid after "
1115
2
          "(re-)opening it has failed. "
1116
2
          "Can cause undefined behaviour.",
1117
2
          N));
1118
2
      return nullptr;
1119
2
    }
1120
0
    return State;
1121
2
  }
1122
1123
367
  return State;
1124
369
}
1125
1126
ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
1127
81
    SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
1128
81
  static const char *BugMessage =
1129
81
      "File position of the stream might be 'indeterminate' "
1130
81
      "after a failed operation. "
1131
81
      "Can cause undefined behavior.";
1132
1133
81
  SymbolRef Sym = StreamVal.getAsSymbol();
1134
81
  if (!Sym)
1135
0
    return State;
1136
1137
81
  const StreamState *SS = State->get<StreamMap>(Sym);
1138
81
  if (!SS)
1139
14
    return State;
1140
1141
67
  assert(SS->isOpened() && "First ensure that stream is opened.");
1142
1143
67
  if (SS->FilePositionIndeterminate) {
1144
7
    if (SS->ErrorState & ErrorFEof) {
1145
      // The error is unknown but may be FEOF.
1146
      // Continue analysis with the FEOF error state.
1147
      // Report warning because the other possible error states.
1148
0
      ExplodedNode *N = C.generateNonFatalErrorNode(State);
1149
0
      if (!N)
1150
0
        return nullptr;
1151
1152
0
      C.emitReport(std::make_unique<PathSensitiveBugReport>(
1153
0
          BT_IndeterminatePosition, BugMessage, N));
1154
0
      return State->set<StreamMap>(
1155
0
          Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
1156
0
    }
1157
1158
    // Known or unknown error state without FEOF possible.
1159
    // Stop analysis, report error.
1160
7
    ExplodedNode *N = C.generateErrorNode(State);
1161
7
    if (N)
1162
7
      C.emitReport(std::make_unique<PathSensitiveBugReport>(
1163
7
          BT_IndeterminatePosition, BugMessage, N));
1164
1165
7
    return nullptr;
1166
7
  }
1167
1168
60
  return State;
1169
67
}
1170
1171
ProgramStateRef
1172
StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
1173
14
                                        ProgramStateRef State) const {
1174
14
  std::optional<nonloc::ConcreteInt> CI =
1175
14
      WhenceVal.getAs<nonloc::ConcreteInt>();
1176
14
  if (!CI)
1177
1
    return State;
1178
1179
13
  int64_t X = CI->getValue().getSExtValue();
1180
13
  if (X == SeekSetVal || 
X == SeekCurVal3
||
X == SeekEndVal2
)
1181
11
    return State;
1182
1183
2
  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1184
2
    C.emitReport(std::make_unique<PathSensitiveBugReport>(
1185
2
        BT_IllegalWhence,
1186
2
        "The whence argument to fseek() should be "
1187
2
        "SEEK_SET, SEEK_END, or SEEK_CUR.",
1188
2
        N));
1189
2
    return nullptr;
1190
2
  }
1191
1192
0
  return State;
1193
2
}
1194
1195
void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
1196
9
                                      ProgramStateRef State) const {
1197
9
  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1198
9
    auto R = std::make_unique<PathSensitiveBugReport>(
1199
9
        BT_StreamEof,
1200
9
        "Read function called when stream is in EOF state. "
1201
9
        "Function has no effect.",
1202
9
        N);
1203
9
    R->markInteresting(StreamSym);
1204
9
    C.emitReport(std::move(R));
1205
9
    return;
1206
9
  }
1207
0
  C.addTransition(State);
1208
0
}
1209
1210
ExplodedNode *
1211
StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1212
27
                           CheckerContext &C, ExplodedNode *Pred) const {
1213
27
  ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1214
27
  if (!Err)
1215
0
    return Pred;
1216
1217
29
  
for (SymbolRef LeakSym : LeakedSyms)27
{
1218
    // Resource leaks can result in multiple warning that describe the same kind
1219
    // of programming error:
1220
    //  void f() {
1221
    //    FILE *F = fopen("a.txt");
1222
    //    if (rand()) // state split
1223
    //      return; // warning
1224
    //  } // warning
1225
    // While this isn't necessarily true (leaking the same stream could result
1226
    // from a different kinds of errors), the reduction in redundant reports
1227
    // makes this a worthwhile heuristic.
1228
    // FIXME: Add a checker option to turn this uniqueing feature off.
1229
29
    const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
1230
29
    assert(StreamOpenNode && "Could not find place of stream opening.");
1231
1232
29
    PathDiagnosticLocation LocUsedForUniqueing;
1233
29
    if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
1234
29
       LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
1235
29
          StreamStmt, C.getSourceManager(),
1236
29
          StreamOpenNode->getLocationContext());
1237
1238
29
    std::unique_ptr<PathSensitiveBugReport> R =
1239
29
        std::make_unique<PathSensitiveBugReport>(
1240
29
            BT_ResourceLeak,
1241
29
            "Opened stream never closed. Potential resource leak.", Err,
1242
29
            LocUsedForUniqueing,
1243
29
            StreamOpenNode->getLocationContext()->getDecl());
1244
29
    R->markInteresting(LeakSym);
1245
29
    C.emitReport(std::move(R));
1246
29
  }
1247
1248
27
  return Err;
1249
27
}
1250
1251
void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1252
2.32k
                                     CheckerContext &C) const {
1253
2.32k
  ProgramStateRef State = C.getState();
1254
1255
2.32k
  llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1256
1257
2.32k
  const StreamMapTy &Map = State->get<StreamMap>();
1258
2.32k
  for (const auto &I : Map) {
1259
1.63k
    SymbolRef Sym = I.first;
1260
1.63k
    const StreamState &SS = I.second;
1261
1.63k
    if (!SymReaper.isDead(Sym))
1262
1.28k
      continue;
1263
348
    if (SS.isOpened())
1264
29
      LeakedSyms.push_back(Sym);
1265
348
    State = State->remove<StreamMap>(Sym);
1266
348
  }
1267
1268
2.32k
  ExplodedNode *N = C.getPredecessor();
1269
2.32k
  if (!LeakedSyms.empty())
1270
27
    N = reportLeaks(LeakedSyms, C, N);
1271
1272
2.32k
  C.addTransition(State, N);
1273
2.32k
}
1274
1275
ProgramStateRef StreamChecker::checkPointerEscape(
1276
    ProgramStateRef State, const InvalidatedSymbols &Escaped,
1277
771
    const CallEvent *Call, PointerEscapeKind Kind) const {
1278
  // Check for file-handling system call that is not handled by the checker.
1279
  // FIXME: The checker should be updated to handle all system calls that take
1280
  // 'FILE*' argument. These are now ignored.
1281
771
  if (Kind == PSK_DirectEscapeOnCall && 
Call->isInSystemHeader()77
)
1282
64
    return State;
1283
1284
707
  for (SymbolRef Sym : Escaped) {
1285
    // The symbol escaped.
1286
    // From now the stream can be manipulated in unknown way to the checker,
1287
    // it is not possible to handle it any more.
1288
    // Optimistically, assume that the corresponding file handle will be closed
1289
    // somewhere else.
1290
    // Remove symbol from state so the following stream calls on this symbol are
1291
    // not handled by the checker.
1292
127
    State = State->remove<StreamMap>(Sym);
1293
127
  }
1294
707
  return State;
1295
771
}
1296
1297
//===----------------------------------------------------------------------===//
1298
// Checker registration.
1299
//===----------------------------------------------------------------------===//
1300
1301
16
void ento::registerStreamChecker(CheckerManager &Mgr) {
1302
16
  Mgr.registerChecker<StreamChecker>();
1303
16
}
1304
1305
34
bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
1306
34
  return true;
1307
34
}
1308
1309
1
void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
1310
1
  auto *Checker = Mgr.getChecker<StreamChecker>();
1311
1
  Checker->TestMode = true;
1312
1
}
1313
1314
2
bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
1315
2
  return true;
1316
2
}