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 "NoOwnershipChangeVisitor.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/ASTMatchers/ASTMatchers.h"
16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
27 #include "llvm/ADT/Sequence.h"
28 #include <functional>
29 #include <optional>
30
31 using namespace clang;
32 using namespace ento;
33 using namespace std::placeholders;
34
35 //===----------------------------------------------------------------------===//
36 // Definition of state data structures.
37 //===----------------------------------------------------------------------===//
38
39 namespace {
40
41 struct FnDescription;
42
43 /// State of the stream error flags.
44 /// Sometimes it is not known to the checker what error flags are set.
45 /// This is indicated by setting more than one flag to true.
46 /// This is an optimization to avoid state splits.
47 /// A stream can either be in FEOF or FERROR but not both at the same time.
48 /// Multiple flags are set to handle the corresponding states together.
49 struct StreamErrorState {
50 /// The stream can be in state where none of the error flags set.
51 bool NoError = true;
52 /// The stream can be in state where the EOF indicator is set.
53 bool FEof = false;
54 /// The stream can be in state where the error indicator is set.
55 bool FError = false;
56
isNoError__anon299e83ef0111::StreamErrorState57 bool isNoError() const { return NoError && !FEof && !FError; }
isFEof__anon299e83ef0111::StreamErrorState58 bool isFEof() const { return !NoError && FEof && !FError; }
isFError__anon299e83ef0111::StreamErrorState59 bool isFError() const { return !NoError && !FEof && FError; }
60
operator ==__anon299e83ef0111::StreamErrorState61 bool operator==(const StreamErrorState &ES) const {
62 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
63 }
64
operator !=__anon299e83ef0111::StreamErrorState65 bool operator!=(const StreamErrorState &ES) const { return !(*this == ES); }
66
operator |__anon299e83ef0111::StreamErrorState67 StreamErrorState operator|(const StreamErrorState &E) const {
68 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
69 }
70
operator &__anon299e83ef0111::StreamErrorState71 StreamErrorState operator&(const StreamErrorState &E) const {
72 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
73 }
74
operator ~__anon299e83ef0111::StreamErrorState75 StreamErrorState operator~() const { return {!NoError, !FEof, !FError}; }
76
77 /// Returns if the StreamErrorState is a valid object.
operator bool__anon299e83ef0111::StreamErrorState78 operator bool() const { return NoError || FEof || FError; }
79
dump__anon299e83ef0111::StreamErrorState80 LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
dumpToStream__anon299e83ef0111::StreamErrorState81 LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &os) const {
82 os << "NoError: " << NoError << ", FEof: " << FEof
83 << ", FError: " << FError;
84 }
85
Profile__anon299e83ef0111::StreamErrorState86 void Profile(llvm::FoldingSetNodeID &ID) const {
87 ID.AddBoolean(NoError);
88 ID.AddBoolean(FEof);
89 ID.AddBoolean(FError);
90 }
91 };
92
93 const StreamErrorState ErrorNone{true, false, false};
94 const StreamErrorState ErrorFEof{false, true, false};
95 const StreamErrorState ErrorFError{false, false, true};
96
97 /// Full state information about a stream pointer.
98 struct StreamState {
99 /// The last file operation called in the stream.
100 /// Can be nullptr.
101 const FnDescription *LastOperation;
102
103 /// State of a stream symbol.
104 enum KindTy {
105 Opened, /// Stream is opened.
106 Closed, /// Closed stream (an invalid stream pointer after it was closed).
107 OpenFailed /// The last open operation has failed.
108 } State;
109
getKindStr__anon299e83ef0111::StreamState110 StringRef getKindStr() const {
111 switch (State) {
112 case Opened:
113 return "Opened";
114 case Closed:
115 return "Closed";
116 case OpenFailed:
117 return "OpenFailed";
118 }
119 llvm_unreachable("Unknown StreamState!");
120 }
121
122 /// State of the error flags.
123 /// Ignored in non-opened stream state but must be NoError.
124 StreamErrorState const ErrorState;
125
126 /// Indicate if the file has an "indeterminate file position indicator".
127 /// This can be set at a failing read or write or seek operation.
128 /// If it is set no more read or write is allowed.
129 /// This value is not dependent on the stream error flags:
130 /// The error flag may be cleared with `clearerr` but the file position
131 /// remains still indeterminate.
132 /// This value applies to all error states in ErrorState except FEOF.
133 /// An EOF+indeterminate state is the same as EOF state.
134 bool const FilePositionIndeterminate = false;
135
StreamState__anon299e83ef0111::StreamState136 StreamState(const FnDescription *L, KindTy S, const StreamErrorState &ES,
137 bool IsFilePositionIndeterminate)
138 : LastOperation(L), State(S), ErrorState(ES),
139 FilePositionIndeterminate(IsFilePositionIndeterminate) {
140 assert((!ES.isFEof() || !IsFilePositionIndeterminate) &&
141 "FilePositionIndeterminate should be false in FEof case.");
142 assert((State == Opened || ErrorState.isNoError()) &&
143 "ErrorState should be None in non-opened stream state.");
144 }
145
isOpened__anon299e83ef0111::StreamState146 bool isOpened() const { return State == Opened; }
isClosed__anon299e83ef0111::StreamState147 bool isClosed() const { return State == Closed; }
isOpenFailed__anon299e83ef0111::StreamState148 bool isOpenFailed() const { return State == OpenFailed; }
149
operator ==__anon299e83ef0111::StreamState150 bool operator==(const StreamState &X) const {
151 // In not opened state error state should always NoError, so comparison
152 // here is no problem.
153 return LastOperation == X.LastOperation && State == X.State &&
154 ErrorState == X.ErrorState &&
155 FilePositionIndeterminate == X.FilePositionIndeterminate;
156 }
157
getOpened__anon299e83ef0111::StreamState158 static StreamState getOpened(const FnDescription *L,
159 const StreamErrorState &ES = ErrorNone,
160 bool IsFilePositionIndeterminate = false) {
161 return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
162 }
getClosed__anon299e83ef0111::StreamState163 static StreamState getClosed(const FnDescription *L) {
164 return StreamState{L, Closed, {}, false};
165 }
getOpenFailed__anon299e83ef0111::StreamState166 static StreamState getOpenFailed(const FnDescription *L) {
167 return StreamState{L, OpenFailed, {}, false};
168 }
169
dump__anon299e83ef0111::StreamState170 LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
171 LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &os) const;
172
Profile__anon299e83ef0111::StreamState173 void Profile(llvm::FoldingSetNodeID &ID) const {
174 ID.AddPointer(LastOperation);
175 ID.AddInteger(State);
176 ErrorState.Profile(ID);
177 ID.AddBoolean(FilePositionIndeterminate);
178 }
179 };
180
181 } // namespace
182
183 // This map holds the state of a stream.
184 // The stream is identified with a SymbolRef that is created when a stream
185 // opening function is modeled by the checker.
186 REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
187
188 //===----------------------------------------------------------------------===//
189 // StreamChecker class and utility functions.
190 //===----------------------------------------------------------------------===//
191
192 namespace {
193
194 class StreamChecker;
195 using FnCheck = std::function<void(const StreamChecker *, const FnDescription *,
196 const CallEvent &, CheckerContext &)>;
197
198 using ArgNoTy = unsigned int;
199 static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
200
201 const char *FeofNote = "Assuming stream reaches end-of-file here";
202 const char *FerrorNote = "Assuming this stream operation fails";
203
204 struct FnDescription {
205 FnCheck PreFn;
206 FnCheck EvalFn;
207 ArgNoTy StreamArgNo;
208 };
209
dumpToStream(llvm::raw_ostream & os) const210 LLVM_DUMP_METHOD void StreamState::dumpToStream(llvm::raw_ostream &os) const {
211 os << "{Kind: " << getKindStr() << ", Last operation: " << LastOperation
212 << ", ErrorState: ";
213 ErrorState.dumpToStream(os);
214 os << ", FilePos: " << (FilePositionIndeterminate ? "Indeterminate" : "OK")
215 << '}';
216 }
217
218 /// Get the value of the stream argument out of the passed call event.
219 /// The call should contain a function that is described by Desc.
getStreamArg(const FnDescription * Desc,const CallEvent & Call)220 SVal getStreamArg(const FnDescription *Desc, const CallEvent &Call) {
221 assert(Desc && Desc->StreamArgNo != ArgNone &&
222 "Try to get a non-existing stream argument.");
223 return Call.getArgSVal(Desc->StreamArgNo);
224 }
225
226 /// Create a conjured symbol return value for a call expression.
makeRetVal(CheckerContext & C,const CallExpr * CE)227 DefinedSVal makeRetVal(CheckerContext &C, const CallExpr *CE) {
228 assert(CE && "Expecting a call expression.");
229
230 const LocationContext *LCtx = C.getLocationContext();
231 return C.getSValBuilder()
232 .conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
233 .castAs<DefinedSVal>();
234 }
235
bindAndAssumeTrue(ProgramStateRef State,CheckerContext & C,const CallExpr * CE)236 ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
237 const CallExpr *CE) {
238 DefinedSVal RetVal = makeRetVal(C, CE);
239 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
240 State = State->assume(RetVal, true);
241 assert(State && "Assumption on new value should not fail.");
242 return State;
243 }
244
bindInt(uint64_t Value,ProgramStateRef State,CheckerContext & C,const CallExpr * CE)245 ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
246 CheckerContext &C, const CallExpr *CE) {
247 State = State->BindExpr(CE, C.getLocationContext(),
248 C.getSValBuilder().makeIntVal(Value, CE->getType()));
249 return State;
250 }
251
assertStreamStateOpened(const StreamState * SS)252 inline void assertStreamStateOpened(const StreamState *SS) {
253 assert(SS->isOpened() && "Stream is expected to be opened");
254 }
255
256 class StreamChecker : public Checker<check::PreCall, eval::Call,
257 check::DeadSymbols, check::PointerEscape> {
258 BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
259 BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
260 BugType BT_UseAfterOpenFailed{this, "Invalid stream",
261 "Stream handling error"};
262 BugType BT_IndeterminatePosition{this, "Invalid stream state",
263 "Stream handling error"};
264 BugType BT_IllegalWhence{this, "Illegal whence argument",
265 "Stream handling error"};
266 BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
267 BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
268 /*SuppressOnSink =*/true};
269
270 public:
271 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
272 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
273 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
274 ProgramStateRef checkPointerEscape(ProgramStateRef State,
275 const InvalidatedSymbols &Escaped,
276 const CallEvent *Call,
277 PointerEscapeKind Kind) const;
278
getBT_StreamEof() const279 const BugType *getBT_StreamEof() const { return &BT_StreamEof; }
getBT_IndeterminatePosition() const280 const BugType *getBT_IndeterminatePosition() const {
281 return &BT_IndeterminatePosition;
282 }
283
constructSetEofNoteTag(CheckerContext & C,SymbolRef StreamSym) const284 const NoteTag *constructSetEofNoteTag(CheckerContext &C,
285 SymbolRef StreamSym) const {
286 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
287 if (!BR.isInteresting(StreamSym) ||
288 &BR.getBugType() != this->getBT_StreamEof())
289 return "";
290
291 BR.markNotInteresting(StreamSym);
292
293 return FeofNote;
294 });
295 }
296
constructSetErrorNoteTag(CheckerContext & C,SymbolRef StreamSym) const297 const NoteTag *constructSetErrorNoteTag(CheckerContext &C,
298 SymbolRef StreamSym) const {
299 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
300 if (!BR.isInteresting(StreamSym) ||
301 &BR.getBugType() != this->getBT_IndeterminatePosition())
302 return "";
303
304 BR.markNotInteresting(StreamSym);
305
306 return FerrorNote;
307 });
308 }
309
constructSetEofOrErrorNoteTag(CheckerContext & C,SymbolRef StreamSym) const310 const NoteTag *constructSetEofOrErrorNoteTag(CheckerContext &C,
311 SymbolRef StreamSym) const {
312 return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) {
313 if (!BR.isInteresting(StreamSym))
314 return "";
315
316 if (&BR.getBugType() == this->getBT_StreamEof()) {
317 BR.markNotInteresting(StreamSym);
318 return FeofNote;
319 }
320 if (&BR.getBugType() == this->getBT_IndeterminatePosition()) {
321 BR.markNotInteresting(StreamSym);
322 return FerrorNote;
323 }
324
325 return "";
326 });
327 }
328
329 /// If true, evaluate special testing stream functions.
330 bool TestMode = false;
331
332 /// If true, generate failure branches for cases that are often not checked.
333 bool PedanticMode = false;
334
335 const CallDescription FCloseDesc = {CDM::CLibrary, {"fclose"}, 1};
336
337 private:
338 CallDescriptionMap<FnDescription> FnDescriptions = {
339 {{CDM::CLibrary, {"fopen"}, 2},
340 {nullptr, &StreamChecker::evalFopen, ArgNone}},
341 {{CDM::CLibrary, {"fdopen"}, 2},
342 {nullptr, &StreamChecker::evalFopen, ArgNone}},
343 {{CDM::CLibrary, {"freopen"}, 3},
344 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
345 {{CDM::CLibrary, {"tmpfile"}, 0},
346 {nullptr, &StreamChecker::evalFopen, ArgNone}},
347 {FCloseDesc, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
348 {{CDM::CLibrary, {"fread"}, 4},
349 {&StreamChecker::preRead,
350 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
351 {{CDM::CLibrary, {"fwrite"}, 4},
352 {&StreamChecker::preWrite,
353 std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
354 {{CDM::CLibrary, {"fgetc"}, 1},
355 {&StreamChecker::preRead,
356 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
357 {{CDM::CLibrary, {"fgets"}, 3},
358 {&StreamChecker::preRead,
359 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, false), 2}},
360 {{CDM::CLibrary, {"getc"}, 1},
361 {&StreamChecker::preRead,
362 std::bind(&StreamChecker::evalFgetx, _1, _2, _3, _4, true), 0}},
363 {{CDM::CLibrary, {"fputc"}, 2},
364 {&StreamChecker::preWrite,
365 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
366 {{CDM::CLibrary, {"fputs"}, 2},
367 {&StreamChecker::preWrite,
368 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}},
369 {{CDM::CLibrary, {"putc"}, 2},
370 {&StreamChecker::preWrite,
371 std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, true), 1}},
372 {{CDM::CLibrary, {"fprintf"}},
373 {&StreamChecker::preWrite,
374 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
375 {{CDM::CLibrary, {"vfprintf"}, 3},
376 {&StreamChecker::preWrite,
377 std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}},
378 {{CDM::CLibrary, {"fscanf"}},
379 {&StreamChecker::preRead,
380 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
381 {{CDM::CLibrary, {"vfscanf"}, 3},
382 {&StreamChecker::preRead,
383 std::bind(&StreamChecker::evalFscanf, _1, _2, _3, _4), 0}},
384 {{CDM::CLibrary, {"ungetc"}, 2},
385 {&StreamChecker::preWrite,
386 std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
387 {{CDM::CLibrary, {"getdelim"}, 4},
388 {&StreamChecker::preRead,
389 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 3}},
390 {{CDM::CLibrary, {"getline"}, 3},
391 {&StreamChecker::preRead,
392 std::bind(&StreamChecker::evalGetdelim, _1, _2, _3, _4), 2}},
393 {{CDM::CLibrary, {"fseek"}, 3},
394 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
395 {{CDM::CLibrary, {"fseeko"}, 3},
396 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
397 {{CDM::CLibrary, {"ftell"}, 1},
398 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
399 {{CDM::CLibrary, {"ftello"}, 1},
400 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
401 {{CDM::CLibrary, {"fflush"}, 1},
402 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
403 {{CDM::CLibrary, {"rewind"}, 1},
404 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
405 {{CDM::CLibrary, {"fgetpos"}, 2},
406 {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},
407 {{CDM::CLibrary, {"fsetpos"}, 2},
408 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
409 {{CDM::CLibrary, {"clearerr"}, 1},
410 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
411 {{CDM::CLibrary, {"feof"}, 1},
412 {&StreamChecker::preDefault,
413 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
414 0}},
415 {{CDM::CLibrary, {"ferror"}, 1},
416 {&StreamChecker::preDefault,
417 std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
418 0}},
419 {{CDM::CLibrary, {"fileno"}, 1},
420 {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}},
421 };
422
423 CallDescriptionMap<FnDescription> FnTestDescriptions = {
424 {{CDM::SimpleFunc, {"StreamTesterChecker_make_feof_stream"}, 1},
425 {nullptr,
426 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof,
427 false),
428 0}},
429 {{CDM::SimpleFunc, {"StreamTesterChecker_make_ferror_stream"}, 1},
430 {nullptr,
431 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
432 ErrorFError, false),
433 0}},
434 {{CDM::SimpleFunc,
435 {"StreamTesterChecker_make_ferror_indeterminate_stream"},
436 1},
437 {nullptr,
438 std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
439 ErrorFError, true),
440 0}},
441 };
442
443 /// Expanded value of EOF, empty before initialization.
444 mutable std::optional<int> EofVal;
445 /// Expanded value of SEEK_SET, 0 if not found.
446 mutable int SeekSetVal = 0;
447 /// Expanded value of SEEK_CUR, 1 if not found.
448 mutable int SeekCurVal = 1;
449 /// Expanded value of SEEK_END, 2 if not found.
450 mutable int SeekEndVal = 2;
451 /// The built-in va_list type is platform-specific
452 mutable QualType VaListType;
453
454 void evalFopen(const FnDescription *Desc, const CallEvent &Call,
455 CheckerContext &C) const;
456
457 void preFreopen(const FnDescription *Desc, const CallEvent &Call,
458 CheckerContext &C) const;
459 void evalFreopen(const FnDescription *Desc, const CallEvent &Call,
460 CheckerContext &C) const;
461
462 void evalFclose(const FnDescription *Desc, const CallEvent &Call,
463 CheckerContext &C) const;
464
465 void preRead(const FnDescription *Desc, const CallEvent &Call,
466 CheckerContext &C) const;
467
468 void preWrite(const FnDescription *Desc, const CallEvent &Call,
469 CheckerContext &C) const;
470
471 void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call,
472 CheckerContext &C, bool IsFread) const;
473
474 void evalFgetx(const FnDescription *Desc, const CallEvent &Call,
475 CheckerContext &C, bool SingleChar) const;
476
477 void evalFputx(const FnDescription *Desc, const CallEvent &Call,
478 CheckerContext &C, bool IsSingleChar) const;
479
480 void evalFprintf(const FnDescription *Desc, const CallEvent &Call,
481 CheckerContext &C) const;
482
483 void evalFscanf(const FnDescription *Desc, const CallEvent &Call,
484 CheckerContext &C) const;
485
486 void evalUngetc(const FnDescription *Desc, const CallEvent &Call,
487 CheckerContext &C) const;
488
489 void evalGetdelim(const FnDescription *Desc, const CallEvent &Call,
490 CheckerContext &C) const;
491
492 void preFseek(const FnDescription *Desc, const CallEvent &Call,
493 CheckerContext &C) const;
494 void evalFseek(const FnDescription *Desc, const CallEvent &Call,
495 CheckerContext &C) const;
496
497 void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
498 CheckerContext &C) const;
499
500 void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
501 CheckerContext &C) const;
502
503 void evalFtell(const FnDescription *Desc, const CallEvent &Call,
504 CheckerContext &C) const;
505
506 void evalRewind(const FnDescription *Desc, const CallEvent &Call,
507 CheckerContext &C) const;
508
509 void preDefault(const FnDescription *Desc, const CallEvent &Call,
510 CheckerContext &C) const;
511
512 void evalClearerr(const FnDescription *Desc, const CallEvent &Call,
513 CheckerContext &C) const;
514
515 void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call,
516 CheckerContext &C,
517 const StreamErrorState &ErrorKind) const;
518
519 void evalSetFeofFerror(const FnDescription *Desc, const CallEvent &Call,
520 CheckerContext &C, const StreamErrorState &ErrorKind,
521 bool Indeterminate) const;
522
523 void preFflush(const FnDescription *Desc, const CallEvent &Call,
524 CheckerContext &C) const;
525
526 void evalFflush(const FnDescription *Desc, const CallEvent &Call,
527 CheckerContext &C) const;
528
529 void evalFileno(const FnDescription *Desc, const CallEvent &Call,
530 CheckerContext &C) const;
531
532 /// Check that the stream (in StreamVal) is not NULL.
533 /// If it can only be NULL a fatal error is emitted and nullptr returned.
534 /// Otherwise the return value is a new state where the stream is constrained
535 /// to be non-null.
536 ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
537 CheckerContext &C,
538 ProgramStateRef State) const;
539
540 /// Check that the stream is the opened state.
541 /// If the stream is known to be not opened an error is generated
542 /// and nullptr returned, otherwise the original state is returned.
543 ProgramStateRef ensureStreamOpened(SVal StreamVal, CheckerContext &C,
544 ProgramStateRef State) const;
545
546 /// Check that the stream has not an invalid ("indeterminate") file position,
547 /// generate warning for it.
548 /// (EOF is not an invalid position.)
549 /// The returned state can be nullptr if a fatal error was generated.
550 /// It can return non-null state if the stream has not an invalid position or
551 /// there is execution path with non-invalid position.
552 ProgramStateRef
553 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &C,
554 ProgramStateRef State) const;
555
556 /// Check the legality of the 'whence' argument of 'fseek'.
557 /// Generate error and return nullptr if it is found to be illegal.
558 /// Otherwise returns the state.
559 /// (State is not changed here because the "whence" value is already known.)
560 ProgramStateRef ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
561 ProgramStateRef State) const;
562
563 /// Generate warning about stream in EOF state.
564 /// There will be always a state transition into the passed State,
565 /// by the new non-fatal error node or (if failed) a normal transition,
566 /// to ensure uniform handling.
567 void reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
568 ProgramStateRef State) const;
569
570 /// Emit resource leak warnings for the given symbols.
571 /// Createn a non-fatal error node for these, and returns it (if any warnings
572 /// were generated). Return value is non-null.
573 ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
574 CheckerContext &C, ExplodedNode *Pred) const;
575
576 /// Find the description data of the function called by a call event.
577 /// Returns nullptr if no function is recognized.
lookupFn(const CallEvent & Call) const578 const FnDescription *lookupFn(const CallEvent &Call) const {
579 // Recognize "global C functions" with only integral or pointer arguments
580 // (and matching name) as stream functions.
581 for (auto *P : Call.parameters()) {
582 QualType T = P->getType();
583 if (!T->isIntegralOrEnumerationType() && !T->isPointerType() &&
584 T.getCanonicalType() != VaListType)
585 return nullptr;
586 }
587
588 return FnDescriptions.lookup(Call);
589 }
590
591 /// Generate a message for BugReporterVisitor if the stored symbol is
592 /// marked as interesting by the actual bug report.
constructLeakNoteTag(CheckerContext & C,SymbolRef StreamSym,const std::string & Message) const593 const NoteTag *constructLeakNoteTag(CheckerContext &C, SymbolRef StreamSym,
594 const std::string &Message) const {
595 return C.getNoteTag([this, StreamSym,
596 Message](PathSensitiveBugReport &BR) -> std::string {
597 if (BR.isInteresting(StreamSym) && &BR.getBugType() == &BT_ResourceLeak)
598 return Message;
599 return "";
600 });
601 }
602
initMacroValues(CheckerContext & C) const603 void initMacroValues(CheckerContext &C) const {
604 if (EofVal)
605 return;
606
607 if (const std::optional<int> OptInt =
608 tryExpandAsInteger("EOF", C.getPreprocessor()))
609 EofVal = *OptInt;
610 else
611 EofVal = -1;
612 if (const std::optional<int> OptInt =
613 tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
614 SeekSetVal = *OptInt;
615 if (const std::optional<int> OptInt =
616 tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
617 SeekEndVal = *OptInt;
618 if (const std::optional<int> OptInt =
619 tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
620 SeekCurVal = *OptInt;
621 }
622
initVaListType(CheckerContext & C) const623 void initVaListType(CheckerContext &C) const {
624 VaListType = C.getASTContext().getBuiltinVaListType().getCanonicalType();
625 }
626
627 /// Searches for the ExplodedNode where the file descriptor was acquired for
628 /// StreamSym.
629 static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
630 SymbolRef StreamSym,
631 CheckerContext &C);
632 };
633
634 struct StreamOperationEvaluator {
635 SValBuilder &SVB;
636 const ASTContext &ACtx;
637
638 SymbolRef StreamSym = nullptr;
639 const StreamState *SS = nullptr;
640 const CallExpr *CE = nullptr;
641 StreamErrorState NewES;
642
StreamOperationEvaluator__anon299e83ef0211::StreamOperationEvaluator643 StreamOperationEvaluator(CheckerContext &C)
644 : SVB(C.getSValBuilder()), ACtx(C.getASTContext()) {
645 ;
646 }
647
Init__anon299e83ef0211::StreamOperationEvaluator648 bool Init(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C,
649 ProgramStateRef State) {
650 StreamSym = getStreamArg(Desc, Call).getAsSymbol();
651 if (!StreamSym)
652 return false;
653 SS = State->get<StreamMap>(StreamSym);
654 if (!SS)
655 return false;
656 NewES = SS->ErrorState;
657 CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
658 if (!CE)
659 return false;
660
661 assertStreamStateOpened(SS);
662
663 return true;
664 }
665
isStreamEof__anon299e83ef0211::StreamOperationEvaluator666 bool isStreamEof() const { return SS->ErrorState == ErrorFEof; }
667
getZeroVal__anon299e83ef0211::StreamOperationEvaluator668 NonLoc getZeroVal(const CallEvent &Call) {
669 return *SVB.makeZeroVal(Call.getResultType()).getAs<NonLoc>();
670 }
671
setStreamState__anon299e83ef0211::StreamOperationEvaluator672 ProgramStateRef setStreamState(ProgramStateRef State,
673 const StreamState &NewSS) {
674 NewES = NewSS.ErrorState;
675 return State->set<StreamMap>(StreamSym, NewSS);
676 }
677
makeAndBindRetVal__anon299e83ef0211::StreamOperationEvaluator678 ProgramStateRef makeAndBindRetVal(ProgramStateRef State, CheckerContext &C) {
679 NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
680 return State->BindExpr(CE, C.getLocationContext(), RetVal);
681 }
682
bindReturnValue__anon299e83ef0211::StreamOperationEvaluator683 ProgramStateRef bindReturnValue(ProgramStateRef State, CheckerContext &C,
684 uint64_t Val) {
685 return State->BindExpr(CE, C.getLocationContext(),
686 SVB.makeIntVal(Val, CE->getCallReturnType(ACtx)));
687 }
688
bindReturnValue__anon299e83ef0211::StreamOperationEvaluator689 ProgramStateRef bindReturnValue(ProgramStateRef State, CheckerContext &C,
690 SVal Val) {
691 return State->BindExpr(CE, C.getLocationContext(), Val);
692 }
693
bindNullReturnValue__anon299e83ef0211::StreamOperationEvaluator694 ProgramStateRef bindNullReturnValue(ProgramStateRef State,
695 CheckerContext &C) {
696 return State->BindExpr(CE, C.getLocationContext(),
697 C.getSValBuilder().makeNullWithType(CE->getType()));
698 }
699
assumeBinOpNN__anon299e83ef0211::StreamOperationEvaluator700 ProgramStateRef assumeBinOpNN(ProgramStateRef State,
701 BinaryOperator::Opcode Op, NonLoc LHS,
702 NonLoc RHS) {
703 auto Cond = SVB.evalBinOpNN(State, Op, LHS, RHS, SVB.getConditionType())
704 .getAs<DefinedOrUnknownSVal>();
705 if (!Cond)
706 return nullptr;
707 return State->assume(*Cond, true);
708 }
709
710 ConstraintManager::ProgramStatePair
makeRetValAndAssumeDual__anon299e83ef0211::StreamOperationEvaluator711 makeRetValAndAssumeDual(ProgramStateRef State, CheckerContext &C) {
712 DefinedSVal RetVal = makeRetVal(C, CE);
713 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
714 return C.getConstraintManager().assumeDual(State, RetVal);
715 }
716
getFailureNoteTag__anon299e83ef0211::StreamOperationEvaluator717 const NoteTag *getFailureNoteTag(const StreamChecker *Ch, CheckerContext &C) {
718 bool SetFeof = NewES.FEof && !SS->ErrorState.FEof;
719 bool SetFerror = NewES.FError && !SS->ErrorState.FError;
720 if (SetFeof && !SetFerror)
721 return Ch->constructSetEofNoteTag(C, StreamSym);
722 if (!SetFeof && SetFerror)
723 return Ch->constructSetErrorNoteTag(C, StreamSym);
724 if (SetFeof && SetFerror)
725 return Ch->constructSetEofOrErrorNoteTag(C, StreamSym);
726 return nullptr;
727 }
728 };
729
730 } // end anonymous namespace
731
732 //===----------------------------------------------------------------------===//
733 // Definition of NoStreamStateChangeVisitor.
734 //===----------------------------------------------------------------------===//
735
736 namespace {
737 class NoStreamStateChangeVisitor final : public NoOwnershipChangeVisitor {
738 protected:
739 /// Syntactically checks whether the callee is a closing function. Since
740 /// we have no path-sensitive information on this call (we would need a
741 /// CallEvent instead of a CallExpr for that), its possible that a
742 /// closing function was called indirectly through a function pointer,
743 /// but we are not able to tell, so this is a best effort analysis.
isClosingCallAsWritten(const CallExpr & Call) const744 bool isClosingCallAsWritten(const CallExpr &Call) const {
745 const auto *StreamChk = static_cast<const StreamChecker *>(&Checker);
746 return StreamChk->FCloseDesc.matchesAsWritten(Call);
747 }
748
doesFnIntendToHandleOwnership(const Decl * Callee,ASTContext & ACtx)749 bool doesFnIntendToHandleOwnership(const Decl *Callee,
750 ASTContext &ACtx) final {
751 using namespace clang::ast_matchers;
752 const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);
753
754 auto Matches =
755 match(findAll(callExpr().bind("call")), *FD->getBody(), ACtx);
756 for (BoundNodes Match : Matches) {
757 if (const auto *Call = Match.getNodeAs<CallExpr>("call"))
758 if (isClosingCallAsWritten(*Call))
759 return true;
760 }
761 // TODO: Ownership might change with an attempt to store stream object, not
762 // only through closing it. Check for attempted stores as well.
763 return false;
764 }
765
hasResourceStateChanged(ProgramStateRef CallEnterState,ProgramStateRef CallExitEndState)766 bool hasResourceStateChanged(ProgramStateRef CallEnterState,
767 ProgramStateRef CallExitEndState) final {
768 return CallEnterState->get<StreamMap>(Sym) !=
769 CallExitEndState->get<StreamMap>(Sym);
770 }
771
emitNote(const ExplodedNode * N)772 PathDiagnosticPieceRef emitNote(const ExplodedNode *N) override {
773 PathDiagnosticLocation L = PathDiagnosticLocation::create(
774 N->getLocation(),
775 N->getState()->getStateManager().getContext().getSourceManager());
776 return std::make_shared<PathDiagnosticEventPiece>(
777 L, "Returning without closing stream object or storing it for later "
778 "release");
779 }
780
781 public:
NoStreamStateChangeVisitor(SymbolRef Sym,const StreamChecker * Checker)782 NoStreamStateChangeVisitor(SymbolRef Sym, const StreamChecker *Checker)
783 : NoOwnershipChangeVisitor(Sym, Checker) {}
784 };
785
786 } // end anonymous namespace
787
getAcquisitionSite(const ExplodedNode * N,SymbolRef StreamSym,CheckerContext & C)788 const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
789 SymbolRef StreamSym,
790 CheckerContext &C) {
791 ProgramStateRef State = N->getState();
792 // When bug type is resource leak, exploded node N may not have state info
793 // for leaked file descriptor, but predecessor should have it.
794 if (!State->get<StreamMap>(StreamSym))
795 N = N->getFirstPred();
796
797 const ExplodedNode *Pred = N;
798 while (N) {
799 State = N->getState();
800 if (!State->get<StreamMap>(StreamSym))
801 return Pred;
802 Pred = N;
803 N = N->getFirstPred();
804 }
805
806 return nullptr;
807 }
808
getKnownValue(ProgramStateRef State,SVal V)809 static std::optional<int64_t> getKnownValue(ProgramStateRef State, SVal V) {
810 SValBuilder &SVB = State->getStateManager().getSValBuilder();
811 if (const llvm::APSInt *Int = SVB.getKnownValue(State, V))
812 return Int->tryExtValue();
813 return std::nullopt;
814 }
815
816 /// Invalidate only the requested elements instead of the whole buffer.
817 /// This is basically a refinement of the more generic 'escapeArgs' or
818 /// the plain old 'invalidateRegions'.
819 static ProgramStateRef
escapeByStartIndexAndCount(ProgramStateRef State,const CallEvent & Call,unsigned BlockCount,const SubRegion * Buffer,QualType ElemType,int64_t StartIndex,int64_t ElementCount)820 escapeByStartIndexAndCount(ProgramStateRef State, const CallEvent &Call,
821 unsigned BlockCount, const SubRegion *Buffer,
822 QualType ElemType, int64_t StartIndex,
823 int64_t ElementCount) {
824 constexpr auto DoNotInvalidateSuperRegion =
825 RegionAndSymbolInvalidationTraits::InvalidationKinds::
826 TK_DoNotInvalidateSuperRegion;
827
828 const LocationContext *LCtx = Call.getLocationContext();
829 const ASTContext &Ctx = State->getStateManager().getContext();
830 SValBuilder &SVB = State->getStateManager().getSValBuilder();
831 auto &RegionManager = Buffer->getMemRegionManager();
832
833 SmallVector<SVal> EscapingVals;
834 EscapingVals.reserve(ElementCount);
835
836 RegionAndSymbolInvalidationTraits ITraits;
837 for (auto Idx : llvm::seq(StartIndex, StartIndex + ElementCount)) {
838 NonLoc Index = SVB.makeArrayIndex(Idx);
839 const auto *Element =
840 RegionManager.getElementRegion(ElemType, Index, Buffer, Ctx);
841 EscapingVals.push_back(loc::MemRegionVal(Element));
842 ITraits.setTrait(Element, DoNotInvalidateSuperRegion);
843 }
844 return State->invalidateRegions(
845 EscapingVals, Call.getOriginExpr(), BlockCount, LCtx,
846 /*CausesPointerEscape=*/false,
847 /*InvalidatedSymbols=*/nullptr, &Call, &ITraits);
848 }
849
escapeArgs(ProgramStateRef State,CheckerContext & C,const CallEvent & Call,ArrayRef<unsigned int> EscapingArgs)850 static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C,
851 const CallEvent &Call,
852 ArrayRef<unsigned int> EscapingArgs) {
853 auto GetArgSVal = [&Call](int Idx) { return Call.getArgSVal(Idx); };
854 auto EscapingVals = to_vector(map_range(EscapingArgs, GetArgSVal));
855 State = State->invalidateRegions(EscapingVals, Call.getOriginExpr(),
856 C.blockCount(), C.getLocationContext(),
857 /*CausesPointerEscape=*/false,
858 /*InvalidatedSymbols=*/nullptr);
859 return State;
860 }
861
862 //===----------------------------------------------------------------------===//
863 // Methods of StreamChecker.
864 //===----------------------------------------------------------------------===//
865
checkPreCall(const CallEvent & Call,CheckerContext & C) const866 void StreamChecker::checkPreCall(const CallEvent &Call,
867 CheckerContext &C) const {
868 initMacroValues(C);
869 initVaListType(C);
870
871 const FnDescription *Desc = lookupFn(Call);
872 if (!Desc || !Desc->PreFn)
873 return;
874
875 Desc->PreFn(this, Desc, Call, C);
876 }
877
evalCall(const CallEvent & Call,CheckerContext & C) const878 bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
879 const FnDescription *Desc = lookupFn(Call);
880 if (!Desc && TestMode)
881 Desc = FnTestDescriptions.lookup(Call);
882 if (!Desc || !Desc->EvalFn)
883 return false;
884
885 Desc->EvalFn(this, Desc, Call, C);
886
887 return C.isDifferent();
888 }
889
evalFopen(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const890 void StreamChecker::evalFopen(const FnDescription *Desc, const CallEvent &Call,
891 CheckerContext &C) const {
892 ProgramStateRef State = C.getState();
893 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
894 if (!CE)
895 return;
896
897 DefinedSVal RetVal = makeRetVal(C, CE);
898 SymbolRef RetSym = RetVal.getAsSymbol();
899 assert(RetSym && "RetVal must be a symbol here.");
900
901 State = State->BindExpr(CE, C.getLocationContext(), RetVal);
902
903 // Bifurcate the state into two: one with a valid FILE* pointer, the other
904 // with a NULL.
905 ProgramStateRef StateNotNull, StateNull;
906 std::tie(StateNotNull, StateNull) =
907 C.getConstraintManager().assumeDual(State, RetVal);
908
909 StateNotNull =
910 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
911 StateNull =
912 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
913
914 C.addTransition(StateNotNull,
915 constructLeakNoteTag(C, RetSym, "Stream opened here"));
916 C.addTransition(StateNull);
917 }
918
preFreopen(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const919 void StreamChecker::preFreopen(const FnDescription *Desc, const CallEvent &Call,
920 CheckerContext &C) const {
921 // Do not allow NULL as passed stream pointer but allow a closed stream.
922 ProgramStateRef State = C.getState();
923 State = ensureStreamNonNull(getStreamArg(Desc, Call),
924 Call.getArgExpr(Desc->StreamArgNo), C, State);
925 if (!State)
926 return;
927
928 C.addTransition(State);
929 }
930
evalFreopen(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const931 void StreamChecker::evalFreopen(const FnDescription *Desc,
932 const CallEvent &Call,
933 CheckerContext &C) const {
934 ProgramStateRef State = C.getState();
935
936 auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
937 if (!CE)
938 return;
939
940 std::optional<DefinedSVal> StreamVal =
941 getStreamArg(Desc, Call).getAs<DefinedSVal>();
942 if (!StreamVal)
943 return;
944
945 SymbolRef StreamSym = StreamVal->getAsSymbol();
946 // Do not care about concrete values for stream ("(FILE *)0x12345"?).
947 // FIXME: Can be stdin, stdout, stderr such values?
948 if (!StreamSym)
949 return;
950
951 // Do not handle untracked stream. It is probably escaped.
952 if (!State->get<StreamMap>(StreamSym))
953 return;
954
955 // Generate state for non-failed case.
956 // Return value is the passed stream pointer.
957 // According to the documentations, the stream is closed first
958 // but any close error is ignored. The state changes to (or remains) opened.
959 ProgramStateRef StateRetNotNull =
960 State->BindExpr(CE, C.getLocationContext(), *StreamVal);
961 // Generate state for NULL return value.
962 // Stream switches to OpenFailed state.
963 ProgramStateRef StateRetNull =
964 State->BindExpr(CE, C.getLocationContext(),
965 C.getSValBuilder().makeNullWithType(CE->getType()));
966
967 StateRetNotNull =
968 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
969 StateRetNull =
970 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
971
972 C.addTransition(StateRetNotNull,
973 constructLeakNoteTag(C, StreamSym, "Stream reopened here"));
974 C.addTransition(StateRetNull);
975 }
976
evalFclose(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const977 void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
978 CheckerContext &C) const {
979 ProgramStateRef State = C.getState();
980 StreamOperationEvaluator E(C);
981 if (!E.Init(Desc, Call, C, State))
982 return;
983
984 // Close the File Descriptor.
985 // Regardless if the close fails or not, stream becomes "closed"
986 // and can not be used any more.
987 State = E.setStreamState(State, StreamState::getClosed(Desc));
988
989 // Return 0 on success, EOF on failure.
990 C.addTransition(E.bindReturnValue(State, C, 0));
991 C.addTransition(E.bindReturnValue(State, C, *EofVal));
992 }
993
preRead(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const994 void StreamChecker::preRead(const FnDescription *Desc, const CallEvent &Call,
995 CheckerContext &C) const {
996 ProgramStateRef State = C.getState();
997 SVal StreamVal = getStreamArg(Desc, Call);
998 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
999 State);
1000 if (!State)
1001 return;
1002 State = ensureStreamOpened(StreamVal, C, State);
1003 if (!State)
1004 return;
1005 State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
1006 if (!State)
1007 return;
1008
1009 SymbolRef Sym = StreamVal.getAsSymbol();
1010 if (Sym && State->get<StreamMap>(Sym)) {
1011 const StreamState *SS = State->get<StreamMap>(Sym);
1012 if (SS->ErrorState & ErrorFEof)
1013 reportFEofWarning(Sym, C, State);
1014 } else {
1015 C.addTransition(State);
1016 }
1017 }
1018
preWrite(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1019 void StreamChecker::preWrite(const FnDescription *Desc, const CallEvent &Call,
1020 CheckerContext &C) const {
1021 ProgramStateRef State = C.getState();
1022 SVal StreamVal = getStreamArg(Desc, Call);
1023 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1024 State);
1025 if (!State)
1026 return;
1027 State = ensureStreamOpened(StreamVal, C, State);
1028 if (!State)
1029 return;
1030 State = ensureNoFilePositionIndeterminate(StreamVal, C, State);
1031 if (!State)
1032 return;
1033
1034 C.addTransition(State);
1035 }
1036
getPointeeType(const MemRegion * R)1037 static QualType getPointeeType(const MemRegion *R) {
1038 if (!R)
1039 return {};
1040 if (const auto *ER = dyn_cast<ElementRegion>(R))
1041 return ER->getElementType();
1042 if (const auto *TR = dyn_cast<TypedValueRegion>(R))
1043 return TR->getValueType();
1044 if (const auto *SR = dyn_cast<SymbolicRegion>(R))
1045 return SR->getPointeeStaticType();
1046 return {};
1047 }
1048
getStartIndex(SValBuilder & SVB,const MemRegion * R)1049 static std::optional<NonLoc> getStartIndex(SValBuilder &SVB,
1050 const MemRegion *R) {
1051 if (!R)
1052 return std::nullopt;
1053
1054 auto Zero = [&SVB] {
1055 BasicValueFactory &BVF = SVB.getBasicValueFactory();
1056 return nonloc::ConcreteInt(BVF.getIntValue(0, /*isUnsigned=*/false));
1057 };
1058
1059 if (const auto *ER = dyn_cast<ElementRegion>(R))
1060 return ER->getIndex();
1061 if (isa<TypedValueRegion>(R))
1062 return Zero();
1063 if (isa<SymbolicRegion>(R))
1064 return Zero();
1065 return std::nullopt;
1066 }
1067
1068 static ProgramStateRef
tryToInvalidateFReadBufferByElements(ProgramStateRef State,CheckerContext & C,const CallEvent & Call,NonLoc SizeVal,NonLoc NMembVal)1069 tryToInvalidateFReadBufferByElements(ProgramStateRef State, CheckerContext &C,
1070 const CallEvent &Call, NonLoc SizeVal,
1071 NonLoc NMembVal) {
1072 // Try to invalidate the individual elements.
1073 const auto *Buffer =
1074 dyn_cast_or_null<SubRegion>(Call.getArgSVal(0).getAsRegion());
1075
1076 const ASTContext &Ctx = C.getASTContext();
1077 QualType ElemTy = getPointeeType(Buffer);
1078 std::optional<SVal> StartElementIndex =
1079 getStartIndex(C.getSValBuilder(), Buffer);
1080
1081 // Drop the outermost ElementRegion to get the buffer.
1082 if (const auto *ER = dyn_cast_or_null<ElementRegion>(Buffer))
1083 Buffer = dyn_cast<SubRegion>(ER->getSuperRegion());
1084
1085 std::optional<int64_t> CountVal = getKnownValue(State, NMembVal);
1086 std::optional<int64_t> Size = getKnownValue(State, SizeVal);
1087 std::optional<int64_t> StartIndexVal =
1088 getKnownValue(State, StartElementIndex.value_or(UnknownVal()));
1089
1090 if (!ElemTy.isNull() && CountVal && Size && StartIndexVal) {
1091 int64_t NumBytesRead = Size.value() * CountVal.value();
1092 int64_t ElemSizeInChars = Ctx.getTypeSizeInChars(ElemTy).getQuantity();
1093 if (ElemSizeInChars == 0)
1094 return nullptr;
1095
1096 bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0;
1097 int64_t NumCompleteOrIncompleteElementsRead =
1098 NumBytesRead / ElemSizeInChars + IncompleteLastElement;
1099
1100 constexpr int MaxInvalidatedElementsLimit = 64;
1101 if (NumCompleteOrIncompleteElementsRead <= MaxInvalidatedElementsLimit) {
1102 return escapeByStartIndexAndCount(State, Call, C.blockCount(), Buffer,
1103 ElemTy, *StartIndexVal,
1104 NumCompleteOrIncompleteElementsRead);
1105 }
1106 }
1107 return nullptr;
1108 }
1109
evalFreadFwrite(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,bool IsFread) const1110 void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
1111 const CallEvent &Call, CheckerContext &C,
1112 bool IsFread) const {
1113 ProgramStateRef State = C.getState();
1114 StreamOperationEvaluator E(C);
1115 if (!E.Init(Desc, Call, C, State))
1116 return;
1117
1118 std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
1119 if (!SizeVal)
1120 return;
1121 std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
1122 if (!NMembVal)
1123 return;
1124
1125 // C'99 standard, §7.19.8.1.3, the return value of fread:
1126 // The fread function returns the number of elements successfully read, which
1127 // may be less than nmemb if a read error or end-of-file is encountered. If
1128 // size or nmemb is zero, fread returns zero and the contents of the array and
1129 // the state of the stream remain unchanged.
1130 if (State->isNull(*SizeVal).isConstrainedTrue() ||
1131 State->isNull(*NMembVal).isConstrainedTrue()) {
1132 // This is the "size or nmemb is zero" case.
1133 // Just return 0, do nothing more (not clear the error flags).
1134 C.addTransition(E.bindReturnValue(State, C, 0));
1135 return;
1136 }
1137
1138 // At read, invalidate the buffer in any case of error or success,
1139 // except if EOF was already present.
1140 if (IsFread && !E.isStreamEof()) {
1141 // Try to invalidate the individual elements.
1142 // Otherwise just fall back to invalidating the whole buffer.
1143 ProgramStateRef InvalidatedState = tryToInvalidateFReadBufferByElements(
1144 State, C, Call, *SizeVal, *NMembVal);
1145 State =
1146 InvalidatedState ? InvalidatedState : escapeArgs(State, C, Call, {0});
1147 }
1148
1149 // Generate a transition for the success state.
1150 // If we know the state to be FEOF at fread, do not add a success state.
1151 if (!IsFread || !E.isStreamEof()) {
1152 ProgramStateRef StateNotFailed =
1153 State->BindExpr(E.CE, C.getLocationContext(), *NMembVal);
1154 StateNotFailed =
1155 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1156 C.addTransition(StateNotFailed);
1157 }
1158
1159 // Add transition for the failed state.
1160 // At write, add failure case only if "pedantic mode" is on.
1161 if (!IsFread && !PedanticMode)
1162 return;
1163
1164 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1165 ProgramStateRef StateFailed =
1166 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1167 StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal);
1168 if (!StateFailed)
1169 return;
1170
1171 StreamErrorState NewES;
1172 if (IsFread)
1173 NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1174 else
1175 NewES = ErrorFError;
1176 // If a (non-EOF) error occurs, the resulting value of the file position
1177 // indicator for the stream is indeterminate.
1178 StateFailed = E.setStreamState(
1179 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1180 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1181 }
1182
evalFgetx(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,bool SingleChar) const1183 void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
1184 CheckerContext &C, bool SingleChar) const {
1185 // `fgetc` returns the read character on success, otherwise returns EOF.
1186 // `fgets` returns the read buffer address on success, otherwise returns NULL.
1187
1188 ProgramStateRef State = C.getState();
1189 StreamOperationEvaluator E(C);
1190 if (!E.Init(Desc, Call, C, State))
1191 return;
1192
1193 if (!E.isStreamEof()) {
1194 // If there was already EOF, assume that read buffer is not changed.
1195 // Otherwise it may change at success or failure.
1196 State = escapeArgs(State, C, Call, {0});
1197 if (SingleChar) {
1198 // Generate a transition for the success state of `fgetc`.
1199 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1200 ProgramStateRef StateNotFailed =
1201 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1202 // The returned 'unsigned char' of `fgetc` is converted to 'int',
1203 // so we need to check if it is in range [0, 255].
1204 StateNotFailed = StateNotFailed->assumeInclusiveRange(
1205 RetVal,
1206 E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy),
1207 E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy),
1208 true);
1209 if (!StateNotFailed)
1210 return;
1211 C.addTransition(StateNotFailed);
1212 } else {
1213 // Generate a transition for the success state of `fgets`.
1214 std::optional<DefinedSVal> GetBuf =
1215 Call.getArgSVal(0).getAs<DefinedSVal>();
1216 if (!GetBuf)
1217 return;
1218 ProgramStateRef StateNotFailed =
1219 State->BindExpr(E.CE, C.getLocationContext(), *GetBuf);
1220 StateNotFailed =
1221 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1222 C.addTransition(StateNotFailed);
1223 }
1224 }
1225
1226 // Add transition for the failed state.
1227 ProgramStateRef StateFailed;
1228 if (SingleChar)
1229 StateFailed = E.bindReturnValue(State, C, *EofVal);
1230 else
1231 StateFailed = E.bindNullReturnValue(State, C);
1232
1233 // If a (non-EOF) error occurs, the resulting value of the file position
1234 // indicator for the stream is indeterminate.
1235 StreamErrorState NewES =
1236 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1237 StateFailed = E.setStreamState(
1238 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1239 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1240 }
1241
evalFputx(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,bool IsSingleChar) const1242 void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call,
1243 CheckerContext &C, bool IsSingleChar) const {
1244 // `fputc` returns the written character on success, otherwise returns EOF.
1245 // `fputs` returns a nonnegative value on success, otherwise returns EOF.
1246
1247 ProgramStateRef State = C.getState();
1248 StreamOperationEvaluator E(C);
1249 if (!E.Init(Desc, Call, C, State))
1250 return;
1251
1252 if (IsSingleChar) {
1253 // Generate a transition for the success state of `fputc`.
1254 std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
1255 if (!PutVal)
1256 return;
1257 ProgramStateRef StateNotFailed =
1258 State->BindExpr(E.CE, C.getLocationContext(), *PutVal);
1259 StateNotFailed =
1260 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1261 C.addTransition(StateNotFailed);
1262 } else {
1263 // Generate a transition for the success state of `fputs`.
1264 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1265 ProgramStateRef StateNotFailed =
1266 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1267 StateNotFailed =
1268 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1269 if (!StateNotFailed)
1270 return;
1271 StateNotFailed =
1272 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1273 C.addTransition(StateNotFailed);
1274 }
1275
1276 if (!PedanticMode)
1277 return;
1278
1279 // Add transition for the failed state. The resulting value of the file
1280 // position indicator for the stream is indeterminate.
1281 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1282 StateFailed = E.setStreamState(
1283 StateFailed, StreamState::getOpened(Desc, ErrorFError, true));
1284 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1285 }
1286
evalFprintf(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1287 void StreamChecker::evalFprintf(const FnDescription *Desc,
1288 const CallEvent &Call,
1289 CheckerContext &C) const {
1290 if (Call.getNumArgs() < 2)
1291 return;
1292
1293 ProgramStateRef State = C.getState();
1294 StreamOperationEvaluator E(C);
1295 if (!E.Init(Desc, Call, C, State))
1296 return;
1297
1298 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1299 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1300 auto Cond =
1301 E.SVB
1302 .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy),
1303 E.SVB.getConditionType())
1304 .getAs<DefinedOrUnknownSVal>();
1305 if (!Cond)
1306 return;
1307 ProgramStateRef StateNotFailed, StateFailed;
1308 std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
1309
1310 StateNotFailed =
1311 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1312 C.addTransition(StateNotFailed);
1313
1314 if (!PedanticMode)
1315 return;
1316
1317 // Add transition for the failed state. The resulting value of the file
1318 // position indicator for the stream is indeterminate.
1319 StateFailed = E.setStreamState(
1320 StateFailed, StreamState::getOpened(Desc, ErrorFError, true));
1321 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1322 }
1323
evalFscanf(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1324 void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
1325 CheckerContext &C) const {
1326 if (Call.getNumArgs() < 2)
1327 return;
1328
1329 ProgramStateRef State = C.getState();
1330 StreamOperationEvaluator E(C);
1331 if (!E.Init(Desc, Call, C, State))
1332 return;
1333
1334 // Add the success state.
1335 // In this context "success" means there is not an EOF or other read error
1336 // before any item is matched in 'fscanf'. But there may be match failure,
1337 // therefore return value can be 0 or greater.
1338 // It is not specified what happens if some items (not all) are matched and
1339 // then EOF or read error happens. Now this case is handled like a "success"
1340 // case, and no error flags are set on the stream. This is probably not
1341 // accurate, and the POSIX documentation does not tell more.
1342 if (!E.isStreamEof()) {
1343 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1344 ProgramStateRef StateNotFailed =
1345 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1346 StateNotFailed =
1347 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1348 if (!StateNotFailed)
1349 return;
1350
1351 if (auto const *Callee = Call.getCalleeIdentifier();
1352 !Callee || Callee->getName() != "vfscanf") {
1353 SmallVector<unsigned int> EscArgs;
1354 for (auto EscArg : llvm::seq(2u, Call.getNumArgs()))
1355 EscArgs.push_back(EscArg);
1356 StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs);
1357 }
1358
1359 if (StateNotFailed)
1360 C.addTransition(StateNotFailed);
1361 }
1362
1363 // Add transition for the failed state.
1364 // Error occurs if nothing is matched yet and reading the input fails.
1365 // Error can be EOF, or other error. At "other error" FERROR or 'errno' can
1366 // be set but it is not further specified if all are required to be set.
1367 // Documentation does not mention, but file position will be set to
1368 // indeterminate similarly as at 'fread'.
1369 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1370 StreamErrorState NewES =
1371 E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError;
1372 StateFailed = E.setStreamState(
1373 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1374 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1375 }
1376
evalUngetc(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1377 void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call,
1378 CheckerContext &C) const {
1379 ProgramStateRef State = C.getState();
1380 StreamOperationEvaluator E(C);
1381 if (!E.Init(Desc, Call, C, State))
1382 return;
1383
1384 // Generate a transition for the success state.
1385 std::optional<NonLoc> PutVal = Call.getArgSVal(0).getAs<NonLoc>();
1386 if (!PutVal)
1387 return;
1388 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, *PutVal);
1389 StateNotFailed =
1390 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1391 C.addTransition(StateNotFailed);
1392
1393 // Add transition for the failed state.
1394 // Failure of 'ungetc' does not result in feof or ferror state.
1395 // If the PutVal has value of EofVal the function should "fail", but this is
1396 // the same transition as the success state.
1397 // In this case only one state transition is added by the analyzer (the two
1398 // new states may be similar).
1399 ProgramStateRef StateFailed = E.bindReturnValue(State, C, *EofVal);
1400 StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc));
1401 C.addTransition(StateFailed);
1402 }
1403
evalGetdelim(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1404 void StreamChecker::evalGetdelim(const FnDescription *Desc,
1405 const CallEvent &Call,
1406 CheckerContext &C) const {
1407 ProgramStateRef State = C.getState();
1408 StreamOperationEvaluator E(C);
1409 if (!E.Init(Desc, Call, C, State))
1410 return;
1411
1412 // Upon successful completion, the getline() and getdelim() functions shall
1413 // return the number of bytes written into the buffer.
1414 // If the end-of-file indicator for the stream is set, the function shall
1415 // return -1.
1416 // If an error occurs, the function shall return -1 and set 'errno'.
1417
1418 if (!E.isStreamEof()) {
1419 // Escape buffer and size (may change by the call).
1420 // May happen even at error (partial read?).
1421 State = escapeArgs(State, C, Call, {0, 1});
1422
1423 // Add transition for the successful state.
1424 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1425 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, RetVal);
1426 StateNotFailed =
1427 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1428
1429 // On success, a buffer is allocated.
1430 auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State);
1431 if (NewLinePtr && isa<DefinedOrUnknownSVal>(*NewLinePtr))
1432 StateNotFailed = StateNotFailed->assume(
1433 NewLinePtr->castAs<DefinedOrUnknownSVal>(), true);
1434
1435 // The buffer size `*n` must be enough to hold the whole line, and
1436 // greater than the return value, since it has to account for '\0'.
1437 SVal SizePtrSval = Call.getArgSVal(1);
1438 auto NVal = getPointeeVal(SizePtrSval, State);
1439 if (NVal && isa<NonLoc>(*NVal)) {
1440 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,
1441 NVal->castAs<NonLoc>(), RetVal);
1442 StateNotFailed = E.bindReturnValue(StateNotFailed, C, RetVal);
1443 }
1444 if (!StateNotFailed)
1445 return;
1446 C.addTransition(StateNotFailed);
1447 }
1448
1449 // Add transition for the failed state.
1450 // If a (non-EOF) error occurs, the resulting value of the file position
1451 // indicator for the stream is indeterminate.
1452 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
1453 StreamErrorState NewES =
1454 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1455 StateFailed = E.setStreamState(
1456 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1457 // On failure, the content of the buffer is undefined.
1458 if (auto NewLinePtr = getPointeeVal(Call.getArgSVal(0), State))
1459 StateFailed = StateFailed->bindLoc(*NewLinePtr, UndefinedVal(),
1460 C.getLocationContext());
1461 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1462 }
1463
preFseek(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1464 void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call,
1465 CheckerContext &C) const {
1466 ProgramStateRef State = C.getState();
1467 SVal StreamVal = getStreamArg(Desc, Call);
1468 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1469 State);
1470 if (!State)
1471 return;
1472 State = ensureStreamOpened(StreamVal, C, State);
1473 if (!State)
1474 return;
1475 State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State);
1476 if (!State)
1477 return;
1478
1479 C.addTransition(State);
1480 }
1481
evalFseek(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1482 void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
1483 CheckerContext &C) const {
1484 ProgramStateRef State = C.getState();
1485 StreamOperationEvaluator E(C);
1486 if (!E.Init(Desc, Call, C, State))
1487 return;
1488
1489 // Add success state.
1490 ProgramStateRef StateNotFailed = E.bindReturnValue(State, C, 0);
1491 // No failure: Reset the state to opened with no error.
1492 StateNotFailed =
1493 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1494 C.addTransition(StateNotFailed);
1495
1496 if (!PedanticMode)
1497 return;
1498
1499 // Add failure state.
1500 // At error it is possible that fseek fails but sets none of the error flags.
1501 // If fseek failed, assume that the file position becomes indeterminate in any
1502 // case.
1503 // It is allowed to set the position beyond the end of the file. EOF error
1504 // should not occur.
1505 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
1506 StateFailed = E.setStreamState(
1507 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1508 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1509 }
1510
evalFgetpos(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1511 void StreamChecker::evalFgetpos(const FnDescription *Desc,
1512 const CallEvent &Call,
1513 CheckerContext &C) const {
1514 ProgramStateRef State = C.getState();
1515 StreamOperationEvaluator E(C);
1516 if (!E.Init(Desc, Call, C, State))
1517 return;
1518
1519 ProgramStateRef StateNotFailed, StateFailed;
1520 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
1521 StateNotFailed = escapeArgs(StateNotFailed, C, Call, {1});
1522
1523 // This function does not affect the stream state.
1524 // Still we add success and failure state with the appropriate return value.
1525 // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1526 C.addTransition(StateNotFailed);
1527 C.addTransition(StateFailed);
1528 }
1529
evalFsetpos(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1530 void StreamChecker::evalFsetpos(const FnDescription *Desc,
1531 const CallEvent &Call,
1532 CheckerContext &C) const {
1533 ProgramStateRef State = C.getState();
1534 StreamOperationEvaluator E(C);
1535 if (!E.Init(Desc, Call, C, State))
1536 return;
1537
1538 ProgramStateRef StateNotFailed, StateFailed;
1539 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
1540
1541 StateNotFailed = E.setStreamState(
1542 StateNotFailed, StreamState::getOpened(Desc, ErrorNone, false));
1543 C.addTransition(StateNotFailed);
1544
1545 if (!PedanticMode)
1546 return;
1547
1548 // At failure ferror could be set.
1549 // The standards do not tell what happens with the file position at failure.
1550 // But we can assume that it is dangerous to make a next I/O operation after
1551 // the position was not set correctly (similar to 'fseek').
1552 StateFailed = E.setStreamState(
1553 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
1554
1555 C.addTransition(StateFailed, E.getFailureNoteTag(this, C));
1556 }
1557
evalFtell(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1558 void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
1559 CheckerContext &C) const {
1560 ProgramStateRef State = C.getState();
1561 StreamOperationEvaluator E(C);
1562 if (!E.Init(Desc, Call, C, State))
1563 return;
1564
1565 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1566 ProgramStateRef StateNotFailed =
1567 State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1568 StateNotFailed =
1569 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
1570 if (!StateNotFailed)
1571 return;
1572
1573 ProgramStateRef StateFailed = E.bindReturnValue(State, C, -1);
1574
1575 // This function does not affect the stream state.
1576 // Still we add success and failure state with the appropriate return value.
1577 // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
1578 C.addTransition(StateNotFailed);
1579 C.addTransition(StateFailed);
1580 }
1581
evalRewind(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1582 void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
1583 CheckerContext &C) const {
1584 ProgramStateRef State = C.getState();
1585 StreamOperationEvaluator E(C);
1586 if (!E.Init(Desc, Call, C, State))
1587 return;
1588
1589 State =
1590 E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone, false));
1591 C.addTransition(State);
1592 }
1593
preFflush(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1594 void StreamChecker::preFflush(const FnDescription *Desc, const CallEvent &Call,
1595 CheckerContext &C) const {
1596 ProgramStateRef State = C.getState();
1597 SVal StreamVal = getStreamArg(Desc, Call);
1598 std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
1599 if (!Stream)
1600 return;
1601
1602 ProgramStateRef StateNotNull, StateNull;
1603 std::tie(StateNotNull, StateNull) =
1604 C.getConstraintManager().assumeDual(State, *Stream);
1605 if (StateNotNull && !StateNull)
1606 ensureStreamOpened(StreamVal, C, StateNotNull);
1607 }
1608
evalFflush(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1609 void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call,
1610 CheckerContext &C) const {
1611 ProgramStateRef State = C.getState();
1612 SVal StreamVal = getStreamArg(Desc, Call);
1613 std::optional<DefinedSVal> Stream = StreamVal.getAs<DefinedSVal>();
1614 if (!Stream)
1615 return;
1616
1617 // Skip if the stream can be both NULL and non-NULL.
1618 ProgramStateRef StateNotNull, StateNull;
1619 std::tie(StateNotNull, StateNull) =
1620 C.getConstraintManager().assumeDual(State, *Stream);
1621 if (StateNotNull && StateNull)
1622 return;
1623 if (StateNotNull && !StateNull)
1624 State = StateNotNull;
1625 else
1626 State = StateNull;
1627
1628 const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
1629 if (!CE)
1630 return;
1631
1632 // `fflush` returns EOF on failure, otherwise returns 0.
1633 ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE);
1634 ProgramStateRef StateNotFailed = bindInt(0, State, C, CE);
1635
1636 // Clear error states if `fflush` returns 0, but retain their EOF flags.
1637 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](SymbolRef Sym,
1638 const StreamState *SS) {
1639 if (SS->ErrorState & ErrorFError) {
1640 StreamErrorState NewES =
1641 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
1642 StreamState NewSS = StreamState::getOpened(Desc, NewES, false);
1643 StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
1644 }
1645 };
1646
1647 if (StateNotNull && !StateNull) {
1648 // Skip if the input stream's state is unknown, open-failed or closed.
1649 if (SymbolRef StreamSym = StreamVal.getAsSymbol()) {
1650 const StreamState *SS = State->get<StreamMap>(StreamSym);
1651 if (SS) {
1652 assert(SS->isOpened() && "Stream is expected to be opened");
1653 ClearErrorInNotFailed(StreamSym, SS);
1654 } else
1655 return;
1656 }
1657 } else {
1658 // Clear error states for all streams.
1659 const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
1660 for (const auto &I : Map) {
1661 SymbolRef Sym = I.first;
1662 const StreamState &SS = I.second;
1663 if (SS.isOpened())
1664 ClearErrorInNotFailed(Sym, &SS);
1665 }
1666 }
1667
1668 C.addTransition(StateNotFailed);
1669 C.addTransition(StateFailed);
1670 }
1671
evalClearerr(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1672 void StreamChecker::evalClearerr(const FnDescription *Desc,
1673 const CallEvent &Call,
1674 CheckerContext &C) const {
1675 ProgramStateRef State = C.getState();
1676 StreamOperationEvaluator E(C);
1677 if (!E.Init(Desc, Call, C, State))
1678 return;
1679
1680 // FilePositionIndeterminate is not cleared.
1681 State = E.setStreamState(
1682 State,
1683 StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate));
1684 C.addTransition(State);
1685 }
1686
evalFeofFerror(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,const StreamErrorState & ErrorKind) const1687 void StreamChecker::evalFeofFerror(const FnDescription *Desc,
1688 const CallEvent &Call, CheckerContext &C,
1689 const StreamErrorState &ErrorKind) const {
1690 ProgramStateRef State = C.getState();
1691 StreamOperationEvaluator E(C);
1692 if (!E.Init(Desc, Call, C, State))
1693 return;
1694
1695 if (E.SS->ErrorState & ErrorKind) {
1696 // Execution path with error of ErrorKind.
1697 // Function returns true.
1698 // From now on it is the only one error state.
1699 ProgramStateRef TrueState = bindAndAssumeTrue(State, C, E.CE);
1700 C.addTransition(E.setStreamState(
1701 TrueState, StreamState::getOpened(Desc, ErrorKind,
1702 E.SS->FilePositionIndeterminate &&
1703 !ErrorKind.isFEof())));
1704 }
1705 if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) {
1706 // Execution path(s) with ErrorKind not set.
1707 // Function returns false.
1708 // New error state is everything before minus ErrorKind.
1709 ProgramStateRef FalseState = E.bindReturnValue(State, C, 0);
1710 C.addTransition(E.setStreamState(
1711 FalseState,
1712 StreamState::getOpened(
1713 Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof())));
1714 }
1715 }
1716
evalFileno(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1717 void StreamChecker::evalFileno(const FnDescription *Desc, const CallEvent &Call,
1718 CheckerContext &C) const {
1719 // Fileno should fail only if the passed pointer is invalid.
1720 // Some of the preconditions are checked already in preDefault.
1721 // Here we can assume that the operation does not fail, because if we
1722 // introduced a separate branch where fileno() returns -1, then it would cause
1723 // many unexpected and unwanted warnings in situations where fileno() is
1724 // called on valid streams.
1725 // The stream error states are not modified by 'fileno', and 'errno' is also
1726 // left unchanged (so this evalCall does not invalidate it, but we have a
1727 // custom evalCall instead of the default that would invalidate it).
1728 ProgramStateRef State = C.getState();
1729 StreamOperationEvaluator E(C);
1730 if (!E.Init(Desc, Call, C, State))
1731 return;
1732
1733 NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
1734 State = State->BindExpr(E.CE, C.getLocationContext(), RetVal);
1735 State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(Call));
1736 if (!State)
1737 return;
1738
1739 C.addTransition(State);
1740 }
1741
preDefault(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C) const1742 void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call,
1743 CheckerContext &C) const {
1744 ProgramStateRef State = C.getState();
1745 SVal StreamVal = getStreamArg(Desc, Call);
1746 State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C,
1747 State);
1748 if (!State)
1749 return;
1750 State = ensureStreamOpened(StreamVal, C, State);
1751 if (!State)
1752 return;
1753
1754 C.addTransition(State);
1755 }
1756
evalSetFeofFerror(const FnDescription * Desc,const CallEvent & Call,CheckerContext & C,const StreamErrorState & ErrorKind,bool Indeterminate) const1757 void StreamChecker::evalSetFeofFerror(const FnDescription *Desc,
1758 const CallEvent &Call, CheckerContext &C,
1759 const StreamErrorState &ErrorKind,
1760 bool Indeterminate) const {
1761 ProgramStateRef State = C.getState();
1762 SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
1763 assert(StreamSym && "Operation not permitted on non-symbolic stream value.");
1764 const StreamState *SS = State->get<StreamMap>(StreamSym);
1765 assert(SS && "Stream should be tracked by the checker.");
1766 State = State->set<StreamMap>(
1767 StreamSym,
1768 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
1769 C.addTransition(State);
1770 }
1771
1772 ProgramStateRef
ensureStreamNonNull(SVal StreamVal,const Expr * StreamE,CheckerContext & C,ProgramStateRef State) const1773 StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
1774 CheckerContext &C,
1775 ProgramStateRef State) const {
1776 auto Stream = StreamVal.getAs<DefinedSVal>();
1777 if (!Stream)
1778 return State;
1779
1780 ConstraintManager &CM = C.getConstraintManager();
1781
1782 ProgramStateRef StateNotNull, StateNull;
1783 std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream);
1784
1785 if (!StateNotNull && StateNull) {
1786 if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
1787 auto R = std::make_unique<PathSensitiveBugReport>(
1788 BT_FileNull, "Stream pointer might be NULL.", N);
1789 if (StreamE)
1790 bugreporter::trackExpressionValue(N, StreamE, *R);
1791 C.emitReport(std::move(R));
1792 }
1793 return nullptr;
1794 }
1795
1796 return StateNotNull;
1797 }
1798
ensureStreamOpened(SVal StreamVal,CheckerContext & C,ProgramStateRef State) const1799 ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal,
1800 CheckerContext &C,
1801 ProgramStateRef State) const {
1802 SymbolRef Sym = StreamVal.getAsSymbol();
1803 if (!Sym)
1804 return State;
1805
1806 const StreamState *SS = State->get<StreamMap>(Sym);
1807 if (!SS)
1808 return State;
1809
1810 if (SS->isClosed()) {
1811 // Using a stream pointer after 'fclose' causes undefined behavior
1812 // according to cppreference.com .
1813 ExplodedNode *N = C.generateErrorNode();
1814 if (N) {
1815 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1816 BT_UseAfterClose,
1817 "Stream might be already closed. Causes undefined behaviour.", N));
1818 return nullptr;
1819 }
1820
1821 return State;
1822 }
1823
1824 if (SS->isOpenFailed()) {
1825 // Using a stream that has failed to open is likely to cause problems.
1826 // This should usually not occur because stream pointer is NULL.
1827 // But freopen can cause a state when stream pointer remains non-null but
1828 // failed to open.
1829 ExplodedNode *N = C.generateErrorNode();
1830 if (N) {
1831 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1832 BT_UseAfterOpenFailed,
1833 "Stream might be invalid after "
1834 "(re-)opening it has failed. "
1835 "Can cause undefined behaviour.",
1836 N));
1837 return nullptr;
1838 }
1839 }
1840
1841 return State;
1842 }
1843
ensureNoFilePositionIndeterminate(SVal StreamVal,CheckerContext & C,ProgramStateRef State) const1844 ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
1845 SVal StreamVal, CheckerContext &C, ProgramStateRef State) const {
1846 static const char *BugMessage =
1847 "File position of the stream might be 'indeterminate' "
1848 "after a failed operation. "
1849 "Can cause undefined behavior.";
1850
1851 SymbolRef Sym = StreamVal.getAsSymbol();
1852 if (!Sym)
1853 return State;
1854
1855 const StreamState *SS = State->get<StreamMap>(Sym);
1856 if (!SS)
1857 return State;
1858
1859 assert(SS->isOpened() && "First ensure that stream is opened.");
1860
1861 if (SS->FilePositionIndeterminate) {
1862 if (SS->ErrorState & ErrorFEof) {
1863 // The error is unknown but may be FEOF.
1864 // Continue analysis with the FEOF error state.
1865 // Report warning because the other possible error states.
1866 ExplodedNode *N = C.generateNonFatalErrorNode(State);
1867 if (!N)
1868 return nullptr;
1869
1870 auto R = std::make_unique<PathSensitiveBugReport>(
1871 BT_IndeterminatePosition, BugMessage, N);
1872 R->markInteresting(Sym);
1873 C.emitReport(std::move(R));
1874 return State->set<StreamMap>(
1875 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof, false));
1876 }
1877
1878 // Known or unknown error state without FEOF possible.
1879 // Stop analysis, report error.
1880 if (ExplodedNode *N = C.generateErrorNode(State)) {
1881 auto R = std::make_unique<PathSensitiveBugReport>(
1882 BT_IndeterminatePosition, BugMessage, N);
1883 R->markInteresting(Sym);
1884 C.emitReport(std::move(R));
1885 }
1886
1887 return nullptr;
1888 }
1889
1890 return State;
1891 }
1892
1893 ProgramStateRef
ensureFseekWhenceCorrect(SVal WhenceVal,CheckerContext & C,ProgramStateRef State) const1894 StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
1895 ProgramStateRef State) const {
1896 std::optional<nonloc::ConcreteInt> CI =
1897 WhenceVal.getAs<nonloc::ConcreteInt>();
1898 if (!CI)
1899 return State;
1900
1901 int64_t X = CI->getValue().getSExtValue();
1902 if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
1903 return State;
1904
1905 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1906 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1907 BT_IllegalWhence,
1908 "The whence argument to fseek() should be "
1909 "SEEK_SET, SEEK_END, or SEEK_CUR.",
1910 N));
1911 return nullptr;
1912 }
1913
1914 return State;
1915 }
1916
reportFEofWarning(SymbolRef StreamSym,CheckerContext & C,ProgramStateRef State) const1917 void StreamChecker::reportFEofWarning(SymbolRef StreamSym, CheckerContext &C,
1918 ProgramStateRef State) const {
1919 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
1920 auto R = std::make_unique<PathSensitiveBugReport>(
1921 BT_StreamEof,
1922 "Read function called when stream is in EOF state. "
1923 "Function has no effect.",
1924 N);
1925 R->markInteresting(StreamSym);
1926 C.emitReport(std::move(R));
1927 return;
1928 }
1929 C.addTransition(State);
1930 }
1931
1932 ExplodedNode *
reportLeaks(const SmallVector<SymbolRef,2> & LeakedSyms,CheckerContext & C,ExplodedNode * Pred) const1933 StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
1934 CheckerContext &C, ExplodedNode *Pred) const {
1935 ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
1936 if (!Err)
1937 return Pred;
1938
1939 for (SymbolRef LeakSym : LeakedSyms) {
1940 // Resource leaks can result in multiple warning that describe the same kind
1941 // of programming error:
1942 // void f() {
1943 // FILE *F = fopen("a.txt");
1944 // if (rand()) // state split
1945 // return; // warning
1946 // } // warning
1947 // While this isn't necessarily true (leaking the same stream could result
1948 // from a different kinds of errors), the reduction in redundant reports
1949 // makes this a worthwhile heuristic.
1950 // FIXME: Add a checker option to turn this uniqueing feature off.
1951 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
1952 assert(StreamOpenNode && "Could not find place of stream opening.");
1953
1954 PathDiagnosticLocation LocUsedForUniqueing;
1955 if (const Stmt *StreamStmt = StreamOpenNode->getStmtForDiagnostics())
1956 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
1957 StreamStmt, C.getSourceManager(),
1958 StreamOpenNode->getLocationContext());
1959
1960 std::unique_ptr<PathSensitiveBugReport> R =
1961 std::make_unique<PathSensitiveBugReport>(
1962 BT_ResourceLeak,
1963 "Opened stream never closed. Potential resource leak.", Err,
1964 LocUsedForUniqueing,
1965 StreamOpenNode->getLocationContext()->getDecl());
1966 R->markInteresting(LeakSym);
1967 R->addVisitor<NoStreamStateChangeVisitor>(LeakSym, this);
1968 C.emitReport(std::move(R));
1969 }
1970
1971 return Err;
1972 }
1973
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const1974 void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1975 CheckerContext &C) const {
1976 ProgramStateRef State = C.getState();
1977
1978 llvm::SmallVector<SymbolRef, 2> LeakedSyms;
1979
1980 const StreamMapTy &Map = State->get<StreamMap>();
1981 for (const auto &I : Map) {
1982 SymbolRef Sym = I.first;
1983 const StreamState &SS = I.second;
1984 if (!SymReaper.isDead(Sym))
1985 continue;
1986 if (SS.isOpened())
1987 LeakedSyms.push_back(Sym);
1988 State = State->remove<StreamMap>(Sym);
1989 }
1990
1991 ExplodedNode *N = C.getPredecessor();
1992 if (!LeakedSyms.empty())
1993 N = reportLeaks(LeakedSyms, C, N);
1994
1995 C.addTransition(State, N);
1996 }
1997
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const1998 ProgramStateRef StreamChecker::checkPointerEscape(
1999 ProgramStateRef State, const InvalidatedSymbols &Escaped,
2000 const CallEvent *Call, PointerEscapeKind Kind) const {
2001 // Check for file-handling system call that is not handled by the checker.
2002 // FIXME: The checker should be updated to handle all system calls that take
2003 // 'FILE*' argument. These are now ignored.
2004 if (Kind == PSK_DirectEscapeOnCall && Call->isInSystemHeader())
2005 return State;
2006
2007 for (SymbolRef Sym : Escaped) {
2008 // The symbol escaped.
2009 // From now the stream can be manipulated in unknown way to the checker,
2010 // it is not possible to handle it any more.
2011 // Optimistically, assume that the corresponding file handle will be closed
2012 // somewhere else.
2013 // Remove symbol from state so the following stream calls on this symbol are
2014 // not handled by the checker.
2015 State = State->remove<StreamMap>(Sym);
2016 }
2017 return State;
2018 }
2019
2020 //===----------------------------------------------------------------------===//
2021 // Checker registration.
2022 //===----------------------------------------------------------------------===//
2023
registerStreamChecker(CheckerManager & Mgr)2024 void ento::registerStreamChecker(CheckerManager &Mgr) {
2025 auto *Checker = Mgr.registerChecker<StreamChecker>();
2026 Checker->PedanticMode =
2027 Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "Pedantic");
2028 }
2029
shouldRegisterStreamChecker(const CheckerManager & Mgr)2030 bool ento::shouldRegisterStreamChecker(const CheckerManager &Mgr) {
2031 return true;
2032 }
2033
registerStreamTesterChecker(CheckerManager & Mgr)2034 void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
2035 auto *Checker = Mgr.getChecker<StreamChecker>();
2036 Checker->TestMode = true;
2037 }
2038
shouldRegisterStreamTesterChecker(const CheckerManager & Mgr)2039 bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
2040 return true;
2041 }
2042