27#include "llvm/ADT/Sequence.h"
33using namespace std::placeholders;
49struct StreamErrorState {
57 bool isNoError()
const {
return NoError && !FEof && !FError; }
58 bool isFEof()
const {
return !NoError && FEof && !FError; }
59 bool isFError()
const {
return !NoError && !FEof && FError; }
61 bool operator==(
const StreamErrorState &ES)
const {
62 return NoError == ES.NoError && FEof == ES.FEof && FError == ES.FError;
65 bool operator!=(
const StreamErrorState &ES)
const {
return !(*
this == ES); }
67 StreamErrorState
operator|(
const StreamErrorState &E)
const {
68 return {NoError || E.NoError, FEof || E.FEof, FError || E.FError};
71 StreamErrorState
operator&(
const StreamErrorState &E)
const {
72 return {NoError && E.NoError, FEof && E.FEof, FError && E.FError};
75 StreamErrorState
operator~()
const {
return {!NoError, !FEof, !FError}; }
78 operator bool()
const {
return NoError || FEof || FError; }
80 LLVM_DUMP_METHOD
void dump()
const { dumpToStream(llvm::errs()); }
81 LLVM_DUMP_METHOD
void dumpToStream(llvm::raw_ostream &os)
const {
82 os <<
"NoError: " << NoError <<
", FEof: " << FEof
83 <<
", FError: " << FError;
86 void Profile(llvm::FoldingSetNodeID &ID)
const {
87 ID.AddBoolean(NoError);
89 ID.AddBoolean(FError);
93const StreamErrorState ErrorNone{
true,
false,
false};
94const StreamErrorState ErrorFEof{
false,
true,
false};
95const StreamErrorState ErrorFError{
false,
false,
true};
101 const FnDescription *LastOperation;
110 StringRef getKindStr()
const {
119 llvm_unreachable(
"Unknown StreamState!");
124 StreamErrorState
const ErrorState;
134 bool const FilePositionIndeterminate =
false;
136 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.");
146 bool isOpened()
const {
return State == Opened; }
147 bool isClosed()
const {
return State == Closed; }
148 bool isOpenFailed()
const {
return State == OpenFailed; }
153 return LastOperation ==
X.LastOperation && State ==
X.State &&
154 ErrorState ==
X.ErrorState &&
155 FilePositionIndeterminate ==
X.FilePositionIndeterminate;
158 static StreamState getOpened(
const FnDescription *L,
159 const StreamErrorState &ES = ErrorNone,
160 bool IsFilePositionIndeterminate =
false) {
161 return StreamState{L, Opened, ES, IsFilePositionIndeterminate};
163 static StreamState getClosed(
const FnDescription *L) {
164 return StreamState{L, Closed, {},
false};
166 static StreamState getOpenFailed(
const FnDescription *L) {
167 return StreamState{L, OpenFailed, {},
false};
170 LLVM_DUMP_METHOD
void dump()
const { dumpToStream(llvm::errs()); }
171 LLVM_DUMP_METHOD
void dumpToStream(llvm::raw_ostream &os)
const;
173 void Profile(llvm::FoldingSetNodeID &ID)
const {
174 ID.AddPointer(LastOperation);
175 ID.AddInteger(State);
176 ErrorState.Profile(ID);
177 ID.AddBoolean(FilePositionIndeterminate);
195using FnCheck =
std::function<void(
const StreamChecker *,
const FnDescription *,
198using ArgNoTy =
unsigned int;
199static const ArgNoTy ArgNone = std::numeric_limits<ArgNoTy>::max();
201const char *FeofNote =
"Assuming stream reaches end-of-file here";
202const char *FerrorNote =
"Assuming this stream operation fails";
204struct FnDescription {
210LLVM_DUMP_METHOD
void StreamState::dumpToStream(llvm::raw_ostream &os)
const {
211 os <<
"{Kind: " << getKindStr() <<
", Last operation: " << LastOperation
213 ErrorState.dumpToStream(os);
214 os <<
", FilePos: " << (FilePositionIndeterminate ?
"Indeterminate" :
"OK")
220SVal 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);
228 return C.getSValBuilder()
229 .conjureSymbolVal(
nullptr, Elem,
C.getStackFrame(),
231 .castAs<DefinedSVal>();
236 DefinedSVal RetVal = makeRetVal(
C, Elem);
237 State = State->BindExpr(CE,
C.getStackFrame(), RetVal);
238 State = State->assume(RetVal,
true);
239 assert(State &&
"Assumption on new value should not fail.");
244 CheckerContext &
C,
const CallExpr *CE) {
245 State = State->BindExpr(CE,
C.getStackFrame(),
250inline void assertStreamStateOpened(
const StreamState *SS) {
251 assert(SS->isOpened() &&
"Stream is expected to be opened");
254class StreamChecker :
public Checker<check::PreCall, eval::Call,
255 check::DeadSymbols, check::PointerEscape,
256 check::ASTDecl<TranslationUnitDecl>> {
257 BugType BT_FileNull{
this,
"NULL stream pointer",
"Stream handling error"};
258 BugType BT_UseAfterClose{
this,
"Closed stream",
"Stream handling error"};
259 BugType BT_UseAfterOpenFailed{
this,
"Invalid stream",
260 "Stream handling error"};
261 BugType BT_IndeterminatePosition{
this,
"Invalid stream state",
262 "Stream handling error"};
263 BugType BT_IllegalWhence{
this,
"Illegal whence argument",
264 "Stream handling error"};
265 BugType BT_StreamEof{
this,
"Stream already in EOF",
"Stream handling error"};
266 BugType BT_ResourceLeak{
this,
"Resource leak",
"Stream handling error",
270 void checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
271 bool evalCall(
const CallEvent &
Call, CheckerContext &
C)
const;
272 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &
C)
const;
275 const CallEvent *
Call,
279 void checkASTDecl(
const TranslationUnitDecl *TU, AnalysisManager &,
280 BugReporter &)
const;
282 const BugType *getBT_StreamEof()
const {
return &BT_StreamEof; }
283 const BugType *getBT_IndeterminatePosition()
const {
284 return &BT_IndeterminatePosition;
291 CheckerContext &
C)
const;
293 const NoteTag *constructSetEofNoteTag(CheckerContext &
C,
295 return C.getNoteTag([
this, StreamSym](PathSensitiveBugReport &BR) {
306 const NoteTag *constructSetErrorNoteTag(CheckerContext &
C,
308 return C.getNoteTag([
this, StreamSym](PathSensitiveBugReport &BR) {
310 &BR.
getBugType() != this->getBT_IndeterminatePosition())
319 const NoteTag *constructSetEofOrErrorNoteTag(CheckerContext &
C,
321 return C.getNoteTag([
this, StreamSym](PathSensitiveBugReport &BR) {
325 if (&BR.
getBugType() == this->getBT_StreamEof()) {
329 if (&BR.
getBugType() == this->getBT_IndeterminatePosition()) {
339 bool TestMode =
false;
342 bool PedanticMode =
false;
344 const CallDescription FCloseDesc = {CDM::CLibrary, {
"fclose"}, 1};
347 CallDescriptionMap<FnDescription> FnDescriptions = {
348 {{CDM::CLibrary, {
"fopen"}, 2},
349 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
350 {{CDM::CLibrary, {
"fdopen"}, 2},
351 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
352 {{CDM::CLibrary, {
"freopen"}, 3},
353 {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
354 {{CDM::CLibrary, {
"tmpfile"}, 0},
355 {
nullptr, &StreamChecker::evalFopen, ArgNone}},
356 {FCloseDesc, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
357 {{CDM::CLibrary, {
"fread"}, 4},
358 {&StreamChecker::preRead,
359 std::bind(&StreamChecker::evalFreadFwrite, _1,
_2, _3, _4,
true), 3}},
360 {{CDM::CLibrary, {
"fwrite"}, 4},
361 {&StreamChecker::preWrite,
362 std::bind(&StreamChecker::evalFreadFwrite, _1,
_2, _3, _4,
false), 3}},
363 {{CDM::CLibrary, {
"fgetc"}, 1},
364 {&StreamChecker::preRead,
365 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
true), 0}},
366 {{CDM::CLibrary, {
"fgets"}, 3},
367 {&StreamChecker::preRead,
368 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
false), 2}},
369 {{CDM::CLibrary, {
"getc"}, 1},
370 {&StreamChecker::preRead,
371 std::bind(&StreamChecker::evalFgetx, _1,
_2, _3, _4,
true), 0}},
372 {{CDM::CLibrary, {
"fputc"}, 2},
373 {&StreamChecker::preWrite,
374 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
true), 1}},
375 {{CDM::CLibrary, {
"fputs"}, 2},
376 {&StreamChecker::preWrite,
377 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
false), 1}},
378 {{CDM::CLibrary, {
"putc"}, 2},
379 {&StreamChecker::preWrite,
380 std::bind(&StreamChecker::evalFputx, _1,
_2, _3, _4,
true), 1}},
381 {{CDM::CLibrary, {
"fprintf"}},
382 {&StreamChecker::preWrite,
383 std::bind(&StreamChecker::evalFprintf, _1,
_2, _3, _4), 0}},
384 {{CDM::CLibrary, {
"vfprintf"}, 3},
385 {&StreamChecker::preWrite,
386 std::bind(&StreamChecker::evalFprintf, _1,
_2, _3, _4), 0}},
387 {{CDM::CLibrary, {
"fscanf"}},
388 {&StreamChecker::preRead,
389 std::bind(&StreamChecker::evalFscanf, _1,
_2, _3, _4), 0}},
390 {{CDM::CLibrary, {
"vfscanf"}, 3},
391 {&StreamChecker::preRead,
392 std::bind(&StreamChecker::evalFscanf, _1,
_2, _3, _4), 0}},
393 {{CDM::CLibrary, {
"ungetc"}, 2},
394 {&StreamChecker::preWrite,
395 std::bind(&StreamChecker::evalUngetc, _1,
_2, _3, _4), 1}},
396 {{CDM::CLibrary, {
"getdelim"}, 4},
397 {&StreamChecker::preRead,
398 std::bind(&StreamChecker::evalGetdelim, _1,
_2, _3, _4), 3}},
399 {{CDM::CLibrary, {
"getline"}, 3},
400 {&StreamChecker::preRead,
401 std::bind(&StreamChecker::evalGetdelim, _1,
_2, _3, _4), 2}},
402 {{CDM::CLibrary, {
"fseek"}, 3},
403 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
404 {{CDM::CLibrary, {
"fseeko"}, 3},
405 {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
406 {{CDM::CLibrary, {
"ftell"}, 1},
407 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
408 {{CDM::CLibrary, {
"ftello"}, 1},
409 {&StreamChecker::preWrite, &StreamChecker::evalFtell, 0}},
410 {{CDM::CLibrary, {
"fflush"}, 1},
411 {&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
412 {{CDM::CLibrary, {
"rewind"}, 1},
413 {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
414 {{CDM::CLibrary, {
"fgetpos"}, 2},
415 {&StreamChecker::preWrite, &StreamChecker::evalFgetpos, 0}},
416 {{CDM::CLibrary, {
"fsetpos"}, 2},
417 {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
418 {{CDM::CLibrary, {
"clearerr"}, 1},
419 {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
420 {{CDM::CLibrary, {
"feof"}, 1},
421 {&StreamChecker::preDefault,
422 std::bind(&StreamChecker::evalFeofFerror, _1,
_2, _3, _4, ErrorFEof),
424 {{CDM::CLibrary, {
"ferror"}, 1},
425 {&StreamChecker::preDefault,
426 std::bind(&StreamChecker::evalFeofFerror, _1,
_2, _3, _4, ErrorFError),
428 {{CDM::CLibrary, {
"fileno"}, 1},
429 {&StreamChecker::preDefault, &StreamChecker::evalFileno, 0}},
432 CallDescriptionMap<FnDescription> FnTestDescriptions = {
433 {{CDM::SimpleFunc, {
"StreamTesterChecker_make_feof_stream"}, 1},
435 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4, ErrorFEof,
438 {{CDM::SimpleFunc, {
"StreamTesterChecker_make_ferror_stream"}, 1},
440 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4,
444 {
"StreamTesterChecker_make_ferror_indeterminate_stream"},
447 std::bind(&StreamChecker::evalSetFeofFerror, _1,
_2, _3, _4,
453 mutable std::optional<int> EofVal;
455 mutable int SeekSetVal = 0;
457 mutable int SeekCurVal = 1;
459 mutable int SeekEndVal = 2;
461 mutable QualType VaListType;
463 mutable const VarDecl *StdinDecl =
nullptr;
464 mutable const VarDecl *StdoutDecl =
nullptr;
465 mutable const VarDecl *StderrDecl =
nullptr;
467 void evalFopen(
const FnDescription *Desc,
const CallEvent &
Call,
468 CheckerContext &
C)
const;
470 void preFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
471 CheckerContext &
C)
const;
472 void evalFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
473 CheckerContext &
C)
const;
475 void evalFclose(
const FnDescription *Desc,
const CallEvent &
Call,
476 CheckerContext &
C)
const;
478 void preRead(
const FnDescription *Desc,
const CallEvent &
Call,
479 CheckerContext &
C)
const;
481 void preWrite(
const FnDescription *Desc,
const CallEvent &
Call,
482 CheckerContext &
C)
const;
484 void evalFreadFwrite(
const FnDescription *Desc,
const CallEvent &
Call,
485 CheckerContext &
C,
bool IsFread)
const;
487 void evalFgetx(
const FnDescription *Desc,
const CallEvent &
Call,
488 CheckerContext &
C,
bool SingleChar)
const;
490 void evalFputx(
const FnDescription *Desc,
const CallEvent &
Call,
491 CheckerContext &
C,
bool IsSingleChar)
const;
493 void evalFprintf(
const FnDescription *Desc,
const CallEvent &
Call,
494 CheckerContext &
C)
const;
496 void evalFscanf(
const FnDescription *Desc,
const CallEvent &
Call,
497 CheckerContext &
C)
const;
499 void evalUngetc(
const FnDescription *Desc,
const CallEvent &
Call,
500 CheckerContext &
C)
const;
502 void evalGetdelim(
const FnDescription *Desc,
const CallEvent &
Call,
503 CheckerContext &
C)
const;
505 void preFseek(
const FnDescription *Desc,
const CallEvent &
Call,
506 CheckerContext &
C)
const;
507 void evalFseek(
const FnDescription *Desc,
const CallEvent &
Call,
508 CheckerContext &
C)
const;
510 void evalFgetpos(
const FnDescription *Desc,
const CallEvent &
Call,
511 CheckerContext &
C)
const;
513 void evalFsetpos(
const FnDescription *Desc,
const CallEvent &
Call,
514 CheckerContext &
C)
const;
516 void evalFtell(
const FnDescription *Desc,
const CallEvent &
Call,
517 CheckerContext &
C)
const;
519 void evalRewind(
const FnDescription *Desc,
const CallEvent &
Call,
520 CheckerContext &
C)
const;
522 void preDefault(
const FnDescription *Desc,
const CallEvent &
Call,
523 CheckerContext &
C)
const;
525 void evalClearerr(
const FnDescription *Desc,
const CallEvent &
Call,
526 CheckerContext &
C)
const;
528 void evalFeofFerror(
const FnDescription *Desc,
const CallEvent &
Call,
530 const StreamErrorState &ErrorKind)
const;
532 void evalSetFeofFerror(
const FnDescription *Desc,
const CallEvent &
Call,
533 CheckerContext &
C,
const StreamErrorState &ErrorKind,
534 bool Indeterminate)
const;
536 void preFflush(
const FnDescription *Desc,
const CallEvent &
Call,
537 CheckerContext &
C)
const;
539 void evalFflush(
const FnDescription *Desc,
const CallEvent &
Call,
540 CheckerContext &
C)
const;
542 void evalFileno(
const FnDescription *Desc,
const CallEvent &
Call,
543 CheckerContext &
C)
const;
549 ProgramStateRef ensureStreamNonNull(SVal StreamVal,
const Expr *StreamE,
566 ensureNoFilePositionIndeterminate(SVal StreamVal, CheckerContext &
C,
580 void reportFEofWarning(
SymbolRef StreamSym, CheckerContext &
C,
586 ExplodedNode *reportLeaks(
const SmallVector<SymbolRef, 2> &LeakedSyms,
587 CheckerContext &
C, ExplodedNode *Pred)
const;
591 const FnDescription *lookupFn(
const CallEvent &
Call)
const {
594 for (
auto *P :
Call.parameters()) {
595 QualType
T = P->getType();
597 T.getCanonicalType() != VaListType)
606 const NoteTag *constructLeakNoteTag(CheckerContext &
C,
SymbolRef StreamSym,
607 const std::string &Message)
const {
608 return C.getNoteTag([
this, StreamSym,
609 Message](PathSensitiveBugReport &BR) -> std::string {
616 void initMacroValues(
const Preprocessor &PP)
const {
625 SeekSetVal = *OptInt;
627 SeekEndVal = *OptInt;
629 SeekCurVal = *OptInt;
634 static const ExplodedNode *getAcquisitionSite(
const ExplodedNode *N,
639struct StreamOperationEvaluator {
641 const ASTContext &ACtx;
644 const StreamState *SS =
nullptr;
645 const CallExpr *CE =
nullptr;
646 std::optional<ConstCFGElementRef> Elem;
647 StreamErrorState NewES;
649 StreamOperationEvaluator(CheckerContext &
C)
650 : SVB(
C.getSValBuilder()), ACtx(
C.getASTContext()) {
654 bool Init(
const FnDescription *Desc,
const CallEvent &
Call, CheckerContext &
C,
656 StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
659 SS = State->get<StreamMap>(StreamSym);
662 NewES = SS->ErrorState;
663 CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
666 Elem =
Call.getCFGElementRef();
668 assertStreamStateOpened(SS);
673 bool isStreamEof()
const {
return SS->ErrorState == ErrorFEof; }
675 NonLoc getZeroVal(
const CallEvent &
Call) {
680 const StreamState &NewSS) {
681 NewES = NewSS.ErrorState;
682 return State->set<StreamMap>(StreamSym, NewSS);
686 NonLoc RetVal = makeRetVal(
C, Elem.value()).
castAs<NonLoc>();
687 return State->BindExpr(CE,
C.getStackFrame(), RetVal);
692 return State->BindExpr(CE,
C.getStackFrame(),
698 return State->BindExpr(CE,
C.getStackFrame(), Val);
703 return State->BindExpr(CE,
C.getStackFrame(),
704 C.getSValBuilder().makeNullWithType(CE->
getType()));
711 .
getAs<DefinedOrUnknownSVal>();
714 return State->assume(*Cond,
true);
719 DefinedSVal RetVal = makeRetVal(
C, Elem.value());
720 State = State->BindExpr(CE,
C.getStackFrame(), RetVal);
721 return C.getConstraintManager().assumeDual(State, RetVal);
724 const NoteTag *getFailureNoteTag(
const StreamChecker *Ch, CheckerContext &
C) {
725 bool SetFeof = NewES.FEof && !SS->ErrorState.FEof;
726 bool SetFerror = NewES.FError && !SS->ErrorState.FError;
727 if (SetFeof && !SetFerror)
728 return Ch->constructSetEofNoteTag(
C, StreamSym);
729 if (!SetFeof && SetFerror)
730 return Ch->constructSetErrorNoteTag(
C, StreamSym);
731 if (SetFeof && SetFerror)
732 return Ch->constructSetEofOrErrorNoteTag(
C, StreamSym);
744class NoStreamStateChangeVisitor final :
public NoOwnershipChangeVisitor {
751 bool isClosingCallAsWritten(
const CallExpr &
Call)
const {
752 const auto *StreamChk =
static_cast<const StreamChecker *
>(&Checker);
756 bool doesFnIntendToHandleOwnership(
const Decl *Callee,
757 ASTContext &ACtx)
final {
758 const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);
773 using namespace clang::ast_matchers;
776 for (BoundNodes
Match : Matches) {
777 if (
const auto *
Call =
Match.getNodeAs<CallExpr>(
"call"))
778 if (isClosingCallAsWritten(*
Call))
788 return CallEnterState->get<StreamMap>(Sym) !=
789 CallExitEndState->get<StreamMap>(Sym);
795 N->
getState()->getStateManager().getContext().getSourceManager());
796 return std::make_shared<PathDiagnosticEventPiece>(
797 L,
"Returning without closing stream object or storing it for later "
802 NoStreamStateChangeVisitor(
SymbolRef Sym,
const StreamChecker *Checker)
803 : NoOwnershipChangeVisitor(Sym, Checker) {}
808const ExplodedNode *StreamChecker::getAcquisitionSite(
const ExplodedNode *N,
814 if (!State->get<StreamMap>(StreamSym))
817 const ExplodedNode *Pred = N;
820 if (!State->get<StreamMap>(StreamSym))
832 return Int->tryExtValue();
841 unsigned BlockCount,
const SubRegion *Buffer,
842 QualType ElemType, int64_t StartIndex,
843 int64_t ElementCount) {
844 constexpr auto DoNotInvalidateSuperRegion =
845 RegionAndSymbolInvalidationTraits::InvalidationKinds::
846 TK_DoNotInvalidateSuperRegion;
849 const ASTContext &Ctx = State->getStateManager().getContext();
854 EscapingVals.reserve(ElementCount);
857 for (
auto Idx : llvm::seq(StartIndex, StartIndex + ElementCount)) {
859 const auto *Element =
860 RegionManager.getElementRegion(ElemType, Index, Buffer, Ctx);
862 ITraits.
setTrait(Element, DoNotInvalidateSuperRegion);
864 return State->invalidateRegions(
865 EscapingVals,
Call.getCFGElementRef(), BlockCount, SF,
867 nullptr, &
Call, &ITraits);
873 auto GetArgSVal = [&
Call](
int Idx) {
return Call.getArgSVal(Idx); };
874 auto EscapingVals = to_vector(map_range(EscapingArgs, GetArgSVal));
875 State = State->invalidateRegions(EscapingVals,
Call.getCFGElementRef(),
876 C.blockCount(),
C.getStackFrame(),
886void StreamChecker::checkPreCall(
const CallEvent &
Call,
887 CheckerContext &
C)
const {
888 const FnDescription *Desc = lookupFn(
Call);
889 if (!Desc || !Desc->PreFn)
892 Desc->PreFn(
this, Desc,
Call,
C);
895bool StreamChecker::evalCall(
const CallEvent &
Call, CheckerContext &
C)
const {
896 const FnDescription *Desc = lookupFn(
Call);
897 if (!Desc && TestMode)
899 if (!Desc || !Desc->EvalFn)
902 Desc->EvalFn(
this, Desc,
Call,
C);
904 return C.isDifferent();
913 const auto *SF =
C.getStackFrame();
914 auto &StoreMgr =
C.getStoreManager();
915 auto &SVB =
C.getSValBuilder();
916 SVal VarValue = State->getSVal(StoreMgr.getLValueVar(Var, SF));
919 .
castAs<DefinedOrUnknownSVal>();
920 return State->assume(NoAliasState,
true);
924 State = assumeRetNE(State, StdinDecl);
925 State = assumeRetNE(State, StdoutDecl);
926 State = assumeRetNE(State, StderrDecl);
931void StreamChecker::evalFopen(
const FnDescription *Desc,
const CallEvent &
Call,
932 CheckerContext &
C)
const {
934 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
938 DefinedSVal RetVal = makeRetVal(
C,
Call.getCFGElementRef());
940 assert(RetSym &&
"RetVal must be a symbol here.");
942 State = State->BindExpr(CE,
C.getStackFrame(), RetVal);
947 std::tie(StateNotNull, StateNull) =
948 C.getConstraintManager().assumeDual(State, RetVal);
951 StateNotNull->set<StreamMap>(RetSym, StreamState::getOpened(Desc));
953 StateNull->set<StreamMap>(RetSym, StreamState::getOpenFailed(Desc));
955 StateNotNull = assumeNoAliasingWithStdStreams(StateNotNull, RetVal,
C);
957 C.addTransition(StateNotNull,
958 constructLeakNoteTag(
C, RetSym,
"Stream opened here"));
959 C.addTransition(StateNull);
962void StreamChecker::preFreopen(
const FnDescription *Desc,
const CallEvent &
Call,
963 CheckerContext &
C)
const {
966 State = ensureStreamNonNull(getStreamArg(Desc,
Call),
967 Call.getArgExpr(Desc->StreamArgNo),
C, State);
971 C.addTransition(State);
974void StreamChecker::evalFreopen(
const FnDescription *Desc,
975 const CallEvent &
Call,
976 CheckerContext &
C)
const {
979 auto *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
983 std::optional<DefinedSVal> StreamVal =
984 getStreamArg(Desc,
Call).getAs<DefinedSVal>();
988 SymbolRef StreamSym = StreamVal->getAsSymbol();
995 if (!State->get<StreamMap>(StreamSym))
1003 State->BindExpr(CE,
C.getStackFrame(), *StreamVal);
1007 State->BindExpr(CE,
C.getStackFrame(),
1008 C.getSValBuilder().makeNullWithType(CE->
getType()));
1011 StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened(Desc));
1013 StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed(Desc));
1015 C.addTransition(StateRetNotNull,
1016 constructLeakNoteTag(
C, StreamSym,
"Stream reopened here"));
1017 C.addTransition(StateRetNull);
1020void StreamChecker::evalFclose(
const FnDescription *Desc,
const CallEvent &
Call,
1021 CheckerContext &
C)
const {
1023 StreamOperationEvaluator E(
C);
1024 if (!E.Init(Desc,
Call,
C, State))
1030 State = E.setStreamState(State, StreamState::getClosed(Desc));
1033 C.addTransition(E.bindReturnValue(State,
C, 0));
1034 C.addTransition(E.bindReturnValue(State,
C, *EofVal));
1037void StreamChecker::preRead(
const FnDescription *Desc,
const CallEvent &
Call,
1038 CheckerContext &
C)
const {
1040 SVal StreamVal = getStreamArg(Desc,
Call);
1041 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1045 State = ensureStreamOpened(StreamVal,
C, State);
1048 State = ensureNoFilePositionIndeterminate(StreamVal,
C, State);
1053 if (Sym && State->get<StreamMap>(Sym)) {
1054 const StreamState *SS = State->get<StreamMap>(Sym);
1055 if (SS->ErrorState & ErrorFEof)
1056 reportFEofWarning(Sym,
C, State);
1058 C.addTransition(State);
1062void StreamChecker::preWrite(
const FnDescription *Desc,
const CallEvent &
Call,
1063 CheckerContext &
C)
const {
1065 SVal StreamVal = getStreamArg(Desc,
Call);
1066 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1070 State = ensureStreamOpened(StreamVal,
C, State);
1073 State = ensureNoFilePositionIndeterminate(StreamVal,
C, State);
1077 C.addTransition(State);
1083 if (
const auto *ER = dyn_cast<ElementRegion>(R))
1084 return ER->getElementType();
1085 if (
const auto *TR = dyn_cast<TypedValueRegion>(R))
1086 return TR->getValueType();
1087 if (
const auto *SR = dyn_cast<SymbolicRegion>(R))
1088 return SR->getPointeeStaticType();
1095 return std::nullopt;
1097 auto Zero = [&SVB] {
1102 if (
const auto *ER = dyn_cast<ElementRegion>(R))
1103 return ER->getIndex();
1108 return std::nullopt;
1116 const auto *Buffer =
1117 dyn_cast_or_null<SubRegion>(
Call.getArgSVal(0).getAsRegion());
1121 std::optional<SVal> StartElementIndex =
1125 if (
const auto *ER = dyn_cast_or_null<ElementRegion>(Buffer))
1126 Buffer = dyn_cast<SubRegion>(ER->getSuperRegion());
1128 std::optional<int64_t> CountVal =
getKnownValue(State, NMembVal);
1129 std::optional<int64_t> Size =
getKnownValue(State, SizeVal);
1130 std::optional<int64_t> StartIndexVal =
1133 if (!ElemTy.
isNull() && CountVal && Size && StartIndexVal) {
1134 int64_t NumBytesRead = Size.value() * CountVal.value();
1136 if (ElemSizeInChars == 0 || NumBytesRead < 0)
1139 bool IncompleteLastElement = (NumBytesRead % ElemSizeInChars) != 0;
1140 int64_t NumCompleteOrIncompleteElementsRead =
1141 NumBytesRead / ElemSizeInChars + IncompleteLastElement;
1143 constexpr int MaxInvalidatedElementsLimit = 64;
1144 if (NumCompleteOrIncompleteElementsRead <= MaxInvalidatedElementsLimit) {
1146 ElemTy, *StartIndexVal,
1147 NumCompleteOrIncompleteElementsRead);
1153void StreamChecker::evalFreadFwrite(
const FnDescription *Desc,
1154 const CallEvent &
Call, CheckerContext &
C,
1155 bool IsFread)
const {
1157 StreamOperationEvaluator E(
C);
1158 if (!E.Init(Desc,
Call,
C, State))
1161 std::optional<NonLoc> SizeVal =
Call.getArgSVal(1).getAs<NonLoc>();
1164 std::optional<NonLoc> NMembVal =
Call.getArgSVal(2).getAs<NonLoc>();
1173 if (State->isNull(*SizeVal).isConstrainedTrue() ||
1174 State->isNull(*NMembVal).isConstrainedTrue()) {
1177 C.addTransition(E.bindReturnValue(State,
C, 0));
1183 if (IsFread && !E.isStreamEof()) {
1187 State,
C,
Call, *SizeVal, *NMembVal);
1189 InvalidatedState ? InvalidatedState :
escapeArgs(State,
C,
Call, {0});
1194 if (!IsFread || !E.isStreamEof()) {
1196 State->BindExpr(E.CE,
C.getStackFrame(), *NMembVal);
1198 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1199 C.addTransition(StateNotFailed);
1204 if (!IsFread && !PedanticMode)
1207 NonLoc RetVal = makeRetVal(
C, E.Elem.value()).
castAs<NonLoc>();
1209 State->BindExpr(E.CE,
C.getStackFrame(), RetVal);
1210 StateFailed = E.assumeBinOpNN(StateFailed, BO_LT, RetVal, *NMembVal);
1214 StreamErrorState NewES;
1216 NewES = E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1218 NewES = ErrorFError;
1221 StateFailed = E.setStreamState(
1222 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1223 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1226void StreamChecker::evalFgetx(
const FnDescription *Desc,
const CallEvent &
Call,
1227 CheckerContext &
C,
bool SingleChar)
const {
1232 StreamOperationEvaluator E(
C);
1233 if (!E.Init(Desc,
Call,
C, State))
1236 if (!E.isStreamEof()) {
1242 NonLoc RetVal = makeRetVal(
C, E.Elem.value()).
castAs<NonLoc>();
1244 State->BindExpr(E.CE,
C.getStackFrame(), RetVal);
1247 StateNotFailed = StateNotFailed->assumeInclusiveRange(
1249 E.SVB.getBasicValueFactory().getValue(0, E.ACtx.UnsignedCharTy),
1250 E.SVB.getBasicValueFactory().getMaxValue(E.ACtx.UnsignedCharTy),
1252 if (!StateNotFailed)
1254 C.addTransition(StateNotFailed);
1257 std::optional<DefinedSVal> GetBuf =
1258 Call.getArgSVal(0).getAs<DefinedSVal>();
1262 State->BindExpr(E.CE,
C.getStackFrame(), *GetBuf);
1264 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1265 C.addTransition(StateNotFailed);
1272 StateFailed = E.bindReturnValue(State,
C, *EofVal);
1274 StateFailed = E.bindNullReturnValue(State,
C);
1278 StreamErrorState NewES =
1279 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1280 StateFailed = E.setStreamState(
1281 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1282 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1285void StreamChecker::evalFputx(
const FnDescription *Desc,
const CallEvent &
Call,
1286 CheckerContext &
C,
bool IsSingleChar)
const {
1291 StreamOperationEvaluator E(
C);
1292 if (!E.Init(Desc,
Call,
C, State))
1297 std::optional<NonLoc> PutVal =
Call.getArgSVal(0).getAs<NonLoc>();
1301 State->BindExpr(E.CE,
C.getStackFrame(), *PutVal);
1303 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1304 C.addTransition(StateNotFailed);
1307 NonLoc RetVal = makeRetVal(
C, E.Elem.value()).
castAs<NonLoc>();
1309 State->BindExpr(E.CE,
C.getStackFrame(), RetVal);
1311 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1312 if (!StateNotFailed)
1315 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1316 C.addTransition(StateNotFailed);
1325 StateFailed = E.setStreamState(
1326 StateFailed, StreamState::getOpened(Desc, ErrorFError,
true));
1327 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1330void StreamChecker::evalFprintf(
const FnDescription *Desc,
1331 const CallEvent &
Call,
1332 CheckerContext &
C)
const {
1333 if (
Call.getNumArgs() < 2)
1337 StreamOperationEvaluator E(
C);
1338 if (!E.Init(Desc,
Call,
C, State))
1341 NonLoc RetVal = makeRetVal(
C, E.Elem.value()).
castAs<NonLoc>();
1342 State = State->BindExpr(E.CE,
C.getStackFrame(), RetVal);
1345 .evalBinOp(State, BO_GE, RetVal, E.SVB.makeZeroVal(E.ACtx.IntTy),
1346 E.SVB.getConditionType())
1347 .
getAs<DefinedOrUnknownSVal>();
1351 std::tie(StateNotFailed, StateFailed) = State->assume(*Cond);
1354 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1355 C.addTransition(StateNotFailed);
1362 StateFailed = E.setStreamState(
1363 StateFailed, StreamState::getOpened(Desc, ErrorFError,
true));
1364 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1367void StreamChecker::evalFscanf(
const FnDescription *Desc,
const CallEvent &
Call,
1368 CheckerContext &
C)
const {
1369 if (
Call.getNumArgs() < 2)
1373 StreamOperationEvaluator E(
C);
1374 if (!E.Init(Desc,
Call,
C, State))
1385 if (!E.isStreamEof()) {
1386 NonLoc RetVal = makeRetVal(
C, E.Elem.value()).
castAs<NonLoc>();
1388 State->BindExpr(E.CE,
C.getStackFrame(), RetVal);
1390 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1391 if (!StateNotFailed)
1394 if (
auto const *Callee =
Call.getCalleeIdentifier();
1395 !Callee ||
Callee->getName() !=
"vfscanf") {
1396 SmallVector<unsigned int> EscArgs;
1397 for (
auto EscArg : llvm::seq(2u,
Call.getNumArgs()))
1398 EscArgs.push_back(EscArg);
1403 C.addTransition(StateNotFailed);
1413 StreamErrorState NewES =
1414 E.isStreamEof() ? ErrorFEof : ErrorNone | ErrorFEof | ErrorFError;
1415 StateFailed = E.setStreamState(
1416 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1417 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1420void StreamChecker::evalUngetc(
const FnDescription *Desc,
const CallEvent &
Call,
1421 CheckerContext &
C)
const {
1423 StreamOperationEvaluator E(
C);
1424 if (!E.Init(Desc,
Call,
C, State))
1428 std::optional<NonLoc> PutVal =
Call.getArgSVal(0).getAs<NonLoc>();
1433 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1434 C.addTransition(StateNotFailed);
1443 StateFailed = E.setStreamState(StateFailed, StreamState::getOpened(Desc));
1444 C.addTransition(StateFailed);
1447void StreamChecker::evalGetdelim(
const FnDescription *Desc,
1448 const CallEvent &
Call,
1449 CheckerContext &
C)
const {
1451 StreamOperationEvaluator E(
C);
1452 if (!E.Init(Desc,
Call,
C, State))
1461 if (!E.isStreamEof()) {
1467 NonLoc RetVal = makeRetVal(
C, E.Elem.value()).
castAs<NonLoc>();
1470 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1475 StateNotFailed = StateNotFailed->assume(
1476 NewLinePtr->castAs<DefinedOrUnknownSVal>(),
true);
1480 SVal SizePtrSval =
Call.getArgSVal(1);
1483 StateNotFailed = E.assumeBinOpNN(StateNotFailed, BO_GT,
1484 NVal->castAs<NonLoc>(), RetVal);
1485 StateNotFailed = E.bindReturnValue(StateNotFailed,
C, RetVal);
1487 if (!StateNotFailed)
1489 C.addTransition(StateNotFailed);
1496 StreamErrorState NewES =
1497 E.isStreamEof() ? ErrorFEof : ErrorFEof | ErrorFError;
1498 StateFailed = E.setStreamState(
1499 StateFailed, StreamState::getOpened(Desc, NewES, !NewES.isFEof()));
1503 StateFailed->bindLoc(*NewLinePtr, UndefinedVal(),
C.getStackFrame());
1504 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1507void StreamChecker::preFseek(
const FnDescription *Desc,
const CallEvent &
Call,
1508 CheckerContext &
C)
const {
1510 SVal StreamVal = getStreamArg(Desc,
Call);
1511 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1515 State = ensureStreamOpened(StreamVal,
C, State);
1518 State = ensureFseekWhenceCorrect(
Call.getArgSVal(2),
C, State);
1522 C.addTransition(State);
1525void StreamChecker::evalFseek(
const FnDescription *Desc,
const CallEvent &
Call,
1526 CheckerContext &
C)
const {
1528 StreamOperationEvaluator E(
C);
1529 if (!E.Init(Desc,
Call,
C, State))
1536 E.setStreamState(StateNotFailed, StreamState::getOpened(Desc));
1537 C.addTransition(StateNotFailed);
1549 StateFailed = E.setStreamState(
1550 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError,
true));
1551 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1554void StreamChecker::evalFgetpos(
const FnDescription *Desc,
1555 const CallEvent &
Call,
1556 CheckerContext &
C)
const {
1558 StreamOperationEvaluator E(
C);
1559 if (!E.Init(Desc,
Call,
C, State))
1563 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State,
C);
1569 C.addTransition(StateNotFailed);
1570 C.addTransition(StateFailed);
1573void StreamChecker::evalFsetpos(
const FnDescription *Desc,
1574 const CallEvent &
Call,
1575 CheckerContext &
C)
const {
1577 StreamOperationEvaluator E(
C);
1578 if (!E.Init(Desc,
Call,
C, State))
1582 std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State,
C);
1584 StateNotFailed = E.setStreamState(
1585 StateNotFailed, StreamState::getOpened(Desc, ErrorNone,
false));
1586 C.addTransition(StateNotFailed);
1595 StateFailed = E.setStreamState(
1596 StateFailed, StreamState::getOpened(Desc, ErrorNone | ErrorFError,
true));
1598 C.addTransition(StateFailed, E.getFailureNoteTag(
this,
C));
1601void StreamChecker::evalFtell(
const FnDescription *Desc,
const CallEvent &
Call,
1602 CheckerContext &
C)
const {
1604 StreamOperationEvaluator E(
C);
1605 if (!E.Init(Desc,
Call,
C, State))
1608 NonLoc RetVal = makeRetVal(
C, E.Elem.value()).castAs<NonLoc>();
1610 State->BindExpr(E.CE,
C.getStackFrame(), RetVal);
1612 E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(
Call));
1613 if (!StateNotFailed)
1621 C.addTransition(StateNotFailed);
1622 C.addTransition(StateFailed);
1625void StreamChecker::evalRewind(
const FnDescription *Desc,
const CallEvent &
Call,
1626 CheckerContext &
C)
const {
1628 StreamOperationEvaluator E(
C);
1629 if (!E.Init(Desc,
Call,
C, State))
1633 E.setStreamState(State, StreamState::getOpened(Desc, ErrorNone,
false));
1634 C.addTransition(State);
1637void StreamChecker::preFflush(
const FnDescription *Desc,
const CallEvent &
Call,
1638 CheckerContext &
C)
const {
1640 SVal StreamVal = getStreamArg(Desc,
Call);
1641 std::optional<DefinedSVal> Stream = StreamVal.
getAs<DefinedSVal>();
1646 std::tie(StateNotNull, StateNull) =
1647 C.getConstraintManager().assumeDual(State, *Stream);
1648 if (StateNotNull && !StateNull)
1649 ensureStreamOpened(StreamVal,
C, StateNotNull);
1652void StreamChecker::evalFflush(
const FnDescription *Desc,
const CallEvent &
Call,
1653 CheckerContext &
C)
const {
1655 SVal StreamVal = getStreamArg(Desc,
Call);
1656 std::optional<DefinedSVal> Stream = StreamVal.
getAs<DefinedSVal>();
1662 std::tie(StateNotNull, StateNull) =
1663 C.getConstraintManager().assumeDual(State, *Stream);
1664 if (StateNotNull && StateNull)
1666 if (StateNotNull && !StateNull)
1667 State = StateNotNull;
1671 const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr());
1680 auto ClearErrorInNotFailed = [&StateNotFailed, Desc](
SymbolRef Sym,
1681 const StreamState *SS) {
1682 if (SS->ErrorState & ErrorFError) {
1683 StreamErrorState NewES =
1684 (SS->ErrorState & ErrorFEof) ? ErrorFEof : ErrorNone;
1685 StreamState NewSS = StreamState::getOpened(Desc, NewES,
false);
1686 StateNotFailed = StateNotFailed->set<StreamMap>(Sym, NewSS);
1690 if (StateNotNull && !StateNull) {
1693 const StreamState *SS = State->get<StreamMap>(StreamSym);
1695 assert(SS->isOpened() &&
"Stream is expected to be opened");
1696 ClearErrorInNotFailed(StreamSym, SS);
1702 const StreamMapTy &Map = StateNotFailed->get<StreamMap>();
1703 for (
const auto &I : Map) {
1705 const StreamState &SS = I.second;
1707 ClearErrorInNotFailed(Sym, &SS);
1711 C.addTransition(StateNotFailed);
1712 C.addTransition(StateFailed);
1715void StreamChecker::evalClearerr(
const FnDescription *Desc,
1716 const CallEvent &
Call,
1717 CheckerContext &
C)
const {
1719 StreamOperationEvaluator E(
C);
1720 if (!E.Init(Desc,
Call,
C, State))
1724 State = E.setStreamState(
1726 StreamState::getOpened(Desc, ErrorNone, E.SS->FilePositionIndeterminate));
1727 C.addTransition(State);
1730void StreamChecker::evalFeofFerror(
const FnDescription *Desc,
1731 const CallEvent &
Call, CheckerContext &
C,
1732 const StreamErrorState &ErrorKind)
const {
1734 StreamOperationEvaluator E(
C);
1735 if (!E.Init(Desc,
Call,
C, State))
1738 if (E.SS->ErrorState & ErrorKind) {
1743 bindAndAssumeTrue(State,
C, E.CE, E.Elem.value());
1744 C.addTransition(E.setStreamState(
1745 TrueState, StreamState::getOpened(Desc, ErrorKind,
1746 E.SS->FilePositionIndeterminate &&
1747 !ErrorKind.isFEof())));
1749 if (StreamErrorState NewES = E.SS->ErrorState & (~ErrorKind)) {
1754 C.addTransition(E.setStreamState(
1756 StreamState::getOpened(
1757 Desc, NewES, E.SS->FilePositionIndeterminate && !NewES.isFEof())));
1761void StreamChecker::evalFileno(
const FnDescription *Desc,
const CallEvent &
Call,
1762 CheckerContext &
C)
const {
1773 StreamOperationEvaluator E(
C);
1774 if (!E.Init(Desc,
Call,
C, State))
1777 NonLoc RetVal = makeRetVal(
C, E.Elem.value()).
castAs<NonLoc>();
1778 State = State->BindExpr(E.CE,
C.getStackFrame(), RetVal);
1779 State = E.assumeBinOpNN(State, BO_GE, RetVal, E.getZeroVal(
Call));
1783 C.addTransition(State);
1786void StreamChecker::preDefault(
const FnDescription *Desc,
const CallEvent &
Call,
1787 CheckerContext &
C)
const {
1789 SVal StreamVal = getStreamArg(Desc,
Call);
1790 State = ensureStreamNonNull(StreamVal,
Call.getArgExpr(Desc->StreamArgNo),
C,
1794 State = ensureStreamOpened(StreamVal,
C, State);
1798 C.addTransition(State);
1801void StreamChecker::evalSetFeofFerror(
const FnDescription *Desc,
1802 const CallEvent &
Call, CheckerContext &
C,
1803 const StreamErrorState &ErrorKind,
1804 bool Indeterminate)
const {
1806 SymbolRef StreamSym = getStreamArg(Desc,
Call).getAsSymbol();
1807 assert(StreamSym &&
"Operation not permitted on non-symbolic stream value.");
1808 const StreamState *SS = State->get<StreamMap>(StreamSym);
1809 assert(SS &&
"Stream should be tracked by the checker.");
1810 State = State->set<StreamMap>(
1812 StreamState::getOpened(SS->LastOperation, ErrorKind, Indeterminate));
1813 C.addTransition(State);
1817StreamChecker::ensureStreamNonNull(SVal StreamVal,
const Expr *StreamE,
1820 auto Stream = StreamVal.
getAs<DefinedSVal>();
1824 ConstraintManager &CM =
C.getConstraintManager();
1827 std::tie(StateNotNull, StateNull) = CM.
assumeDual(State, *Stream);
1829 if (!StateNotNull && StateNull) {
1830 if (ExplodedNode *N =
C.generateErrorNode(StateNull)) {
1831 auto R = std::make_unique<PathSensitiveBugReport>(
1832 BT_FileNull,
"Stream pointer might be NULL.", N);
1835 C.emitReport(std::move(R));
1840 return StateNotNull;
1844class StreamClosedVisitor final :
public BugReporterVisitor {
1846 bool Satisfied =
false;
1849 explicit StreamClosedVisitor(
SymbolRef StreamSym) : StreamSym(StreamSym) {}
1851 static void *getTag() {
1856 void Profile(llvm::FoldingSetNodeID &ID)
const override {
1857 ID.AddPointer(getTag());
1858 ID.AddPointer(StreamSym);
1862 BugReporterContext &BRC,
1863 PathSensitiveBugReport &BR)
override {
1866 const StreamState *PredSS =
1868 if (PredSS && PredSS->isClosed())
1876 llvm::StringLiteral Msg =
"Stream is closed here";
1877 return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg);
1889 const StreamState *SS = State->get<StreamMap>(Sym);
1893 if (SS->isClosed()) {
1896 if (ExplodedNode *N =
C.generateErrorNode()) {
1897 auto R = std::make_unique<PathSensitiveBugReport>(
1898 BT_UseAfterClose,
"Use of a stream that might be already closed", N);
1899 R->addVisitor<StreamClosedVisitor>(Sym);
1900 C.emitReport(std::move(R));
1907 if (SS->isOpenFailed()) {
1912 ExplodedNode *N =
C.generateErrorNode();
1914 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1915 BT_UseAfterOpenFailed,
1916 "Stream might be invalid after "
1917 "(re-)opening it has failed. "
1918 "Can cause undefined behaviour.",
1929 static const char *BugMessage =
1930 "File position of the stream might be 'indeterminate' "
1931 "after a failed operation. "
1932 "Can cause undefined behavior.";
1938 const StreamState *SS = State->get<StreamMap>(Sym);
1942 assert(SS->isOpened() &&
"First ensure that stream is opened.");
1944 if (SS->FilePositionIndeterminate) {
1945 if (SS->ErrorState & ErrorFEof) {
1949 ExplodedNode *N =
C.generateNonFatalErrorNode(State);
1953 auto R = std::make_unique<PathSensitiveBugReport>(
1954 BT_IndeterminatePosition, BugMessage, N);
1955 R->markInteresting(Sym);
1956 C.emitReport(std::move(R));
1957 return State->set<StreamMap>(
1958 Sym, StreamState::getOpened(SS->LastOperation, ErrorFEof,
false));
1963 if (ExplodedNode *N =
C.generateErrorNode(State)) {
1964 auto R = std::make_unique<PathSensitiveBugReport>(
1965 BT_IndeterminatePosition, BugMessage, N);
1966 R->markInteresting(Sym);
1967 C.emitReport(std::move(R));
1977StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &
C,
1979 std::optional<nonloc::ConcreteInt> CI =
1980 WhenceVal.
getAs<nonloc::ConcreteInt>();
1984 int64_t X = CI->getValue()->getSExtValue();
1985 if (
X == SeekSetVal ||
X == SeekCurVal ||
X == SeekEndVal)
1988 if (ExplodedNode *N =
C.generateNonFatalErrorNode(State)) {
1989 C.emitReport(std::make_unique<PathSensitiveBugReport>(
1991 "The whence argument to fseek() should be "
1992 "SEEK_SET, SEEK_END, or SEEK_CUR.",
2000void StreamChecker::reportFEofWarning(
SymbolRef StreamSym, CheckerContext &
C,
2002 if (ExplodedNode *N =
C.generateNonFatalErrorNode(State)) {
2003 auto R = std::make_unique<PathSensitiveBugReport>(
2005 "Read function called when stream is in EOF state. "
2006 "Function has no effect.",
2008 R->markInteresting(StreamSym);
2009 C.emitReport(std::move(R));
2012 C.addTransition(State);
2016StreamChecker::reportLeaks(
const SmallVector<SymbolRef, 2> &LeakedSyms,
2017 CheckerContext &
C, ExplodedNode *Pred)
const {
2018 ExplodedNode *Err =
C.generateNonFatalErrorNode(
C.getState(), Pred);
2034 const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym,
C);
2035 assert(StreamOpenNode &&
"Could not find place of stream opening.");
2037 PathDiagnosticLocation LocUsedForUniqueing;
2040 StreamStmt,
C.getSourceManager(), StreamOpenNode->
getStackFrame());
2042 std::unique_ptr<PathSensitiveBugReport>
R =
2043 std::make_unique<PathSensitiveBugReport>(
2045 "Opened stream never closed. Potential resource leak.", Err,
2047 R->markInteresting(LeakSym);
2048 R->addVisitor<NoStreamStateChangeVisitor>(LeakSym,
this);
2049 C.emitReport(std::move(R));
2055void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
2056 CheckerContext &
C)
const {
2059 llvm::SmallVector<SymbolRef, 2> LeakedSyms;
2061 const StreamMapTy &Map = State->get<StreamMap>();
2062 for (
const auto &I : Map) {
2064 const StreamState &SS = I.second;
2065 if (!SymReaper.
isDead(Sym))
2068 LeakedSyms.push_back(Sym);
2069 State = State->remove<StreamMap>(Sym);
2072 ExplodedNode *N =
C.getPredecessor();
2073 if (!LeakedSyms.empty())
2074 N = reportLeaks(LeakedSyms,
C, N);
2076 C.addTransition(State, N);
2096 State = State->remove<StreamMap>(Sym);
2101static const VarDecl *
2113 for (
const Decl *D : LookupRes) {
2114 if (
auto *VD = dyn_cast_or_null<VarDecl>(D)) {
2115 if (SM.isInSystemHeader(VD->getLocation()) && VD->hasExternalStorage() &&
2116 VD->getType().getCanonicalType() == FilePtrTy) {
2124void StreamChecker::checkASTDecl(
const TranslationUnitDecl *TU,
2125 AnalysisManager &Mgr, BugReporter &)
const {
2137void ento::registerStreamChecker(CheckerManager &Mgr) {
2139 Checker->PedanticMode =
2143bool ento::shouldRegisterStreamChecker(
const CheckerManager &Mgr) {
2147void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
2148 auto *Checker = Mgr.
getChecker<StreamChecker>();
2149 Checker->TestMode =
true;
2152bool ento::shouldRegisterStreamTesterChecker(
const CheckerManager &Mgr) {
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static const VarDecl * getGlobalStreamPointerByName(const TranslationUnitDecl *TU, StringRef VarName)
static ProgramStateRef tryToInvalidateFReadBufferByElements(ProgramStateRef State, CheckerContext &C, const CallEvent &Call, NonLoc SizeVal, NonLoc NMembVal)
static QualType getPointeeType(const MemRegion *R)
static std::optional< int64_t > getKnownValue(ProgramStateRef State, SVal V)
static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C, const CallEvent &Call, ArrayRef< unsigned int > EscapingArgs)
static std::optional< NonLoc > getStartIndex(SValBuilder &SVB, const MemRegion *R)
static ProgramStateRef escapeByStartIndexAndCount(ProgramStateRef State, const CallEvent &Call, unsigned BlockCount, const SubRegion *Buffer, QualType ElemType, int64_t StartIndex, int64_t ElementCount)
Invalidate only the requested elements instead of the whole buffer.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
SourceManager & getSourceManager()
QualType getBuiltinVaListType() const
Retrieve the type of the __builtin_va_list type.
QualType getFILEType() const
Retrieve the C FILE type.
QualType getPointerType(QualType T) const
Return the uniqued reference to the type for a pointer to the specified type.
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
BinaryOperatorKind Opcode
QualType getCallReturnType(const ASTContext &Ctx) const
getCallReturnType - Get the return type of the call expr.
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
Decl - This represents one declaration (or definition), e.g.
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
A (possibly-)qualified type.
bool isNull() const
Return true if this QualType doesn't point to a type yet.
QualType getCanonicalType() const
It represents a stack frame of the call stack.
const Decl * getDecl() const
The top declaration context.
ASTContext & getASTContext() const
bool isPointerType() const
bool isIntegralOrEnumerationType() const
Determine whether this type is an integral or enumeration type.
Preprocessor & getPreprocessor() override
APSIntPtr getIntValue(uint64_t X, bool isUnsigned)
const BugType & getBugType() const
const SourceManager & getSourceManager() const
const T * lookup(const CallEvent &Call) const
bool matchesAsWritten(const CallExpr &CE) const
Returns true if the CallExpr is a call to a function that matches the CallDescription.
Represents an abstract call to a function or method along a particular path.
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
CHECKER * getChecker(AT &&...Args)
If the the singleton instance of a checker class is not yet constructed, then construct it (with the ...
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false,...
std::pair< ProgramStateRef, ProgramStateRef > ProgramStatePair
const ProgramStateRef & getState() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
ExplodedNode * getFirstPred()
const StackFrame * getStackFrame() const
MemRegion - The root abstract class for all memory regions.
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
void markNotInteresting(SymbolRef sym)
bool isInteresting(SymbolRef sym) const
SValBuilder & getSValBuilder()
Information about invalidation for a particular region/symbol.
void setTrait(SymbolRef Sym, InvalidationKinds IK)
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing '0' for the specified type.
virtual const llvm::APSInt * getKnownValue(ProgramStateRef state, SVal val)=0
Evaluates a given SVal.
BasicValueFactory & getBasicValueFactory()
ProgramStateManager & getStateManager()
NonLoc makeArrayIndex(uint64_t idx)
nonloc::ConcreteInt makeIntVal(const IntegerLiteral *integer)
virtual SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, NonLoc lhs, NonLoc rhs, QualType resultTy)=0
Create a new value which represents a binary expression with two non- location operands.
QualType getConditionType() const
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
SubRegion - A region that subsets another larger region.
MemRegionManager & getMemRegionManager() const override
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
Value representing integer constant.
__inline void unsigned int _2
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
SmallVector< BoundNodes, 1 > match(MatcherT Matcher, const NodeT &Node, ASTContext &Context)
Returns the results of matching Matcher on Node.
internal::Matcher< T > findAll(const internal::Matcher< T > &Matcher)
Matches if the node or any descendant matches.
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
PointerEscapeKind
Describes the different reasons a pointer escapes during analysis.
@ PSK_DirectEscapeOnCall
The pointer has been passed to a function call directly.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
std::optional< SVal > getPointeeVal(SVal PtrSVal, ProgramStateRef State)
std::optional< int > tryExpandAsInteger(StringRef Macro, const Preprocessor &PP)
Try to parse the value of a defined preprocessor macro.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
Top level wrappers for InstallAPI frontend operations.
@ Match
This is not an overload because the signature exactly matches an existing declaration.
bool isa(CodeGen::Address addr)
CFGBlock::ConstCFGElementRef ConstCFGElementRef
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
DiagnosticLevelMask operator&(DiagnosticLevelMask LHS, DiagnosticLevelMask RHS)
DiagnosticLevelMask operator~(DiagnosticLevelMask M)
const FunctionProtoType * T
DiagnosticLevelMask operator|(DiagnosticLevelMask LHS, DiagnosticLevelMask RHS)
bool operator!=(CanQual< T > x, CanQual< U > y)
int const char * function