Please use GitHub pull requests for new patches. Avoid migrating existing patches. Phabricator shutdown timeline
Changeset View
Changeset View
Standalone View
Standalone View
clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Show All 11 Lines | |||||
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" | ||||
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | ||||
#include "clang/StaticAnalyzer/Core/Checker.h" | #include "clang/StaticAnalyzer/Core/Checker.h" | ||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h" | #include "clang/StaticAnalyzer/Core/CheckerManager.h" | ||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" | ||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | ||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | ||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" | |||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" | ||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" | ||||
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" | ||||
#include <functional> | #include <functional> | ||||
using namespace clang; | using namespace clang; | ||||
using namespace ento; | using namespace ento; | ||||
using namespace std::placeholders; | using namespace std::placeholders; | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
const StreamErrorState ErrorNone{true, false, false}; | const StreamErrorState ErrorNone{true, false, false}; | ||||
const StreamErrorState ErrorFEof{false, true, false}; | const StreamErrorState ErrorFEof{false, true, false}; | ||||
const StreamErrorState ErrorFError{false, false, true}; | const StreamErrorState ErrorFError{false, false, true}; | ||||
/// Full state information about a stream pointer. | /// Full state information about a stream pointer. | ||||
struct StreamState { | struct StreamState { | ||||
/// The last file operation called in the stream. | /// The last file operation called in the stream. | ||||
/// Can be nullptr. | |||||
Szelethus: When can this occur? | |||||
const FnDescription *LastOperation; | const FnDescription *LastOperation; | ||||
/// State of a stream symbol. | /// State of a stream symbol. | ||||
/// FIXME: We need maybe an "escaped" state later. | |||||
enum KindTy { | enum KindTy { | ||||
Opened, /// Stream is opened. | Opened, /// Stream is opened. | ||||
Closed, /// Closed stream (an invalid stream pointer after it was closed). | Closed, /// Closed stream (an invalid stream pointer after it was closed). | ||||
OpenFailed /// The last open operation has failed. | OpenFailed /// The last open operation has failed. | ||||
} State; | } State; | ||||
/// State of the error flags. | /// State of the error flags. | ||||
/// Ignored in non-opened stream state but must be NoError. | /// Ignored in non-opened stream state but must be NoError. | ||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C, | ||||
State = State->assume(RetVal, true); | State = State->assume(RetVal, true); | ||||
assert(State && "Assumption on new value should not fail."); | assert(State && "Assumption on new value should not fail."); | ||||
return State; | return State; | ||||
} | } | ||||
ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, | ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State, | ||||
CheckerContext &C, const CallExpr *CE) { | CheckerContext &C, const CallExpr *CE) { | ||||
State = State->BindExpr(CE, C.getLocationContext(), | State = State->BindExpr(CE, C.getLocationContext(), | ||||
C.getSValBuilder().makeIntVal(Value, false)); | C.getSValBuilder().makeIntVal(Value, CE->getType())); | ||||
return State; | return State; | ||||
} | } | ||||
class StreamChecker : public Checker<check::PreCall, eval::Call, | class StreamChecker : public Checker<check::PreCall, eval::Call, | ||||
check::DeadSymbols, check::PointerEscape> { | check::DeadSymbols, check::PointerEscape> { | ||||
BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; | BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"}; | ||||
BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; | BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"}; | ||||
BugType BT_UseAfterOpenFailed{this, "Invalid stream", | BugType BT_UseAfterOpenFailed{this, "Invalid stream", | ||||
Show All 31 Lines | CallDescriptionMap<FnDescription> FnDescriptions = { | ||||
{{{"fread"}, 4}, | {{{"fread"}, 4}, | ||||
{&StreamChecker::preFread, | {&StreamChecker::preFread, | ||||
std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, | std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}}, | ||||
{{{"fwrite"}, 4}, | {{{"fwrite"}, 4}, | ||||
{&StreamChecker::preFwrite, | {&StreamChecker::preFwrite, | ||||
std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, | std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}}, | ||||
{{{"fseek"}, 3}, | {{{"fseek"}, 3}, | ||||
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, | {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}}, | ||||
{{{"ftell"}, 1}, {&StreamChecker::preDefault, nullptr, 0}}, | {{{"ftell"}, 1}, | ||||
{{{"rewind"}, 1}, {&StreamChecker::preDefault, nullptr, 0}}, | {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}}, | ||||
{{{"fgetpos"}, 2}, {&StreamChecker::preDefault, nullptr, 0}}, | {{{"rewind"}, 1}, | ||||
{{{"fsetpos"}, 2}, {&StreamChecker::preDefault, nullptr, 0}}, | {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}}, | ||||
{{{"fgetpos"}, 2}, | |||||
{&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}}, | |||||
{{{"fsetpos"}, 2}, | |||||
{&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}}, | |||||
{{{"clearerr"}, 1}, | {{{"clearerr"}, 1}, | ||||
{&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, | {&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}}, | ||||
{{{"feof"}, 1}, | {{{"feof"}, 1}, | ||||
{&StreamChecker::preDefault, | {&StreamChecker::preDefault, | ||||
std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), | std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof), | ||||
0}}, | 0}}, | ||||
{{{"ferror"}, 1}, | {{{"ferror"}, 1}, | ||||
{&StreamChecker::preDefault, | {&StreamChecker::preDefault, | ||||
Show All 9 Lines | CallDescriptionMap<FnDescription> FnTestDescriptions = { | ||||
0}}, | 0}}, | ||||
{{{"StreamTesterChecker_make_ferror_stream"}, 1}, | {{{"StreamTesterChecker_make_ferror_stream"}, 1}, | ||||
{nullptr, | {nullptr, | ||||
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, | std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, | ||||
ErrorFError), | ErrorFError), | ||||
0}}, | 0}}, | ||||
}; | }; | ||||
mutable Optional<int> EofVal; | |||||
void evalFopen(const FnDescription *Desc, const CallEvent &Call, | void evalFopen(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C) const; | CheckerContext &C) const; | ||||
void preFreopen(const FnDescription *Desc, const CallEvent &Call, | void preFreopen(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C) const; | CheckerContext &C) const; | ||||
void evalFreopen(const FnDescription *Desc, const CallEvent &Call, | void evalFreopen(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C) const; | CheckerContext &C) const; | ||||
Show All 9 Lines | private: | ||||
void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, | void evalFreadFwrite(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C, bool IsFread) const; | CheckerContext &C, bool IsFread) const; | ||||
void preFseek(const FnDescription *Desc, const CallEvent &Call, | void preFseek(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C) const; | CheckerContext &C) const; | ||||
void evalFseek(const FnDescription *Desc, const CallEvent &Call, | void evalFseek(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C) const; | CheckerContext &C) const; | ||||
void evalFgetpos(const FnDescription *Desc, const CallEvent &Call, | |||||
CheckerContext &C) const; | |||||
void evalFsetpos(const FnDescription *Desc, const CallEvent &Call, | |||||
CheckerContext &C) const; | |||||
void evalFtell(const FnDescription *Desc, const CallEvent &Call, | |||||
CheckerContext &C) const; | |||||
void evalRewind(const FnDescription *Desc, const CallEvent &Call, | |||||
CheckerContext &C) const; | |||||
void preDefault(const FnDescription *Desc, const CallEvent &Call, | void preDefault(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C) const; | CheckerContext &C) const; | ||||
void evalClearerr(const FnDescription *Desc, const CallEvent &Call, | void evalClearerr(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C) const; | CheckerContext &C) const; | ||||
void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, | void evalFeofFerror(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C, | CheckerContext &C, | ||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | return C.getNoteTag([this, StreamSym](PathSensitiveBugReport &BR) { | ||||
return ""; | return ""; | ||||
BR.markNotInteresting(StreamSym); | BR.markNotInteresting(StreamSym); | ||||
return "Assuming stream reaches end-of-file here"; | return "Assuming stream reaches end-of-file here"; | ||||
}); | }); | ||||
} | } | ||||
void initEof(CheckerContext &C) const { | |||||
if (EofVal) | |||||
return; | |||||
if (const llvm::Optional<int> OptInt = | |||||
tryExpandAsInteger("EOF", C.getPreprocessor())) | |||||
EofVal = *OptInt; | |||||
else | |||||
EofVal = -1; | |||||
} | |||||
/// Searches for the ExplodedNode where the file descriptor was acquired for | /// Searches for the ExplodedNode where the file descriptor was acquired for | ||||
/// StreamSym. | /// StreamSym. | ||||
static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, | static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N, | ||||
SymbolRef StreamSym, | SymbolRef StreamSym, | ||||
CheckerContext &C); | CheckerContext &C); | ||||
}; | }; | ||||
} // end anonymous namespace | } // end anonymous namespace | ||||
// This map holds the state of a stream. | // This map holds the state of a stream. | ||||
// The stream is identified with a SymbolRef that is created when a stream | // The stream is identified with a SymbolRef that is created when a stream | ||||
// opening function is modeled by the checker. | // opening function is modeled by the checker. | ||||
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) | REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) | ||||
inline void assertStreamStateOpened(const StreamState *SS) { | inline void assertStreamStateOpened(const StreamState *SS) { | ||||
assert(SS->isOpened() && | assert(SS->isOpened() && "Stream is expected to be opened"); | ||||
"Previous create of error node for non-opened stream failed?"); | |||||
} | } | ||||
const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, | const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N, | ||||
SymbolRef StreamSym, | SymbolRef StreamSym, | ||||
CheckerContext &C) { | CheckerContext &C) { | ||||
ProgramStateRef State = N->getState(); | ProgramStateRef State = N->getState(); | ||||
// When bug type is resource leak, exploded node N may not have state info | // When bug type is resource leak, exploded node N may not have state info | ||||
// for leaked file descriptor, but predecessor should have it. | // for leaked file descriptor, but predecessor should have it. | ||||
Show All 13 Lines | |||||
} | } | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
// Methods of StreamChecker. | // Methods of StreamChecker. | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
void StreamChecker::checkPreCall(const CallEvent &Call, | void StreamChecker::checkPreCall(const CallEvent &Call, | ||||
CheckerContext &C) const { | CheckerContext &C) const { | ||||
initEof(C); | |||||
const FnDescription *Desc = lookupFn(Call); | const FnDescription *Desc = lookupFn(Call); | ||||
if (!Desc || !Desc->PreFn) | if (!Desc || !Desc->PreFn) | ||||
return; | return; | ||||
Desc->PreFn(this, Desc, Call, C); | Desc->PreFn(this, Desc, Call, C); | ||||
} | } | ||||
bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { | bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const { | ||||
▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call, | ||||
SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); | SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); | ||||
if (!Sym) | if (!Sym) | ||||
return; | return; | ||||
const StreamState *SS = State->get<StreamMap>(Sym); | const StreamState *SS = State->get<StreamMap>(Sym); | ||||
if (!SS) | if (!SS) | ||||
return; | return; | ||||
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); | |||||
if (!CE) | |||||
return; | |||||
assertStreamStateOpened(SS); | assertStreamStateOpened(SS); | ||||
// Close the File Descriptor. | // Close the File Descriptor. | ||||
// Regardless if the close fails or not, stream becomes "closed" | // Regardless if the close fails or not, stream becomes "closed" | ||||
// and can not be used any more. | // and can not be used any more. | ||||
State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); | State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc)); | ||||
C.addTransition(State); | // Return 0 on success, EOF on failure. | ||||
SValBuilder &SVB = C.getSValBuilder(); | |||||
ProgramStateRef StateSuccess = State->BindExpr( | |||||
CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy)); | |||||
ProgramStateRef StateFailure = | |||||
State->BindExpr(CE, C.getLocationContext(), | |||||
SVB.makeIntVal(*EofVal, C.getASTContext().IntTy)); | |||||
C.addTransition(StateSuccess); | |||||
C.addTransition(StateFailure); | |||||
} | } | ||||
void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, | void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call, | ||||
CheckerContext &C) const { | CheckerContext &C) const { | ||||
ProgramStateRef State = C.getState(); | ProgramStateRef State = C.getState(); | ||||
SVal StreamVal = getStreamArg(Desc, Call); | SVal StreamVal = getStreamArg(Desc, Call); | ||||
State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, | State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, | ||||
State); | State); | ||||
▲ Show 20 Lines • Show All 168 Lines • ▼ Show 20 Lines | void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call, | ||||
StateFailed = StateFailed->set<StreamMap>( | StateFailed = StateFailed->set<StreamMap>( | ||||
StreamSym, | StreamSym, | ||||
StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); | StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true)); | ||||
C.addTransition(StateNotFailed); | C.addTransition(StateNotFailed); | ||||
C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); | C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); | ||||
} | } | ||||
void StreamChecker::evalFgetpos(const FnDescription *Desc, | |||||
const CallEvent &Call, | |||||
CheckerContext &C) const { | |||||
ProgramStateRef State = C.getState(); | |||||
SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); | |||||
if (!Sym) | |||||
return; | |||||
// Do not evaluate if stream is not found. | |||||
if (!State->get<StreamMap>(Sym)) | |||||
return; | |||||
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); | |||||
if (!CE) | |||||
return; | |||||
DefinedSVal RetVal = makeRetVal(C, CE); | |||||
State = State->BindExpr(CE, C.getLocationContext(), RetVal); | |||||
ProgramStateRef StateNotFailed, StateFailed; | |||||
std::tie(StateFailed, StateNotFailed) = | |||||
C.getConstraintManager().assumeDual(State, RetVal); | |||||
// This function does not affect the stream state. | |||||
// Still we add success and failure state with the appropriate return value. | |||||
// StdLibraryFunctionsChecker can change these states (set the 'errno' state). | |||||
C.addTransition(StateNotFailed); | |||||
C.addTransition(StateFailed); | |||||
} | |||||
void StreamChecker::evalFsetpos(const FnDescription *Desc, | |||||
const CallEvent &Call, | |||||
CheckerContext &C) const { | |||||
ProgramStateRef State = C.getState(); | |||||
SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); | |||||
if (!StreamSym) | |||||
return; | |||||
const StreamState *SS = State->get<StreamMap>(StreamSym); | |||||
if (!SS) | |||||
return; | |||||
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); | |||||
if (!CE) | |||||
return; | |||||
assertStreamStateOpened(SS); | |||||
DefinedSVal RetVal = makeRetVal(C, CE); | |||||
State = State->BindExpr(CE, C.getLocationContext(), RetVal); | |||||
ProgramStateRef StateNotFailed, StateFailed; | |||||
std::tie(StateFailed, StateNotFailed) = | |||||
C.getConstraintManager().assumeDual(State, RetVal); | |||||
StateNotFailed = StateNotFailed->set<StreamMap>( | |||||
StreamSym, StreamState::getOpened(Desc, ErrorNone, false)); | |||||
// At failure ferror could be set. | |||||
// The standards do not tell what happens with the file position at failure. | |||||
// But we can assume that it is dangerous to make a next I/O operation after | |||||
// the position was not set correctly (similar to 'fseek'). | |||||
StateFailed = StateFailed->set<StreamMap>( | |||||
StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true)); | |||||
C.addTransition(StateNotFailed); | |||||
C.addTransition(StateFailed); | |||||
} | |||||
void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, | |||||
CheckerContext &C) const { | |||||
ProgramStateRef State = C.getState(); | |||||
SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol(); | |||||
if (!Sym) | |||||
return; | |||||
if (!State->get<StreamMap>(Sym)) | |||||
return; | |||||
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); | |||||
if (!CE) | |||||
return; | |||||
SValBuilder &SVB = C.getSValBuilder(); | |||||
NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); | |||||
ProgramStateRef StateNotFailed = | |||||
State->BindExpr(CE, C.getLocationContext(), RetVal); | |||||
auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, | |||||
SVB.makeZeroVal(C.getASTContext().LongTy), | |||||
SVB.getConditionType()) | |||||
.getAs<DefinedOrUnknownSVal>(); | |||||
if (!Cond) | |||||
return; | |||||
StateNotFailed = StateNotFailed->assume(*Cond, true); | |||||
if (!StateNotFailed) | |||||
return; | |||||
ProgramStateRef StateFailed = State->BindExpr( | |||||
CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy)); | |||||
C.addTransition(StateNotFailed); | |||||
C.addTransition(StateFailed); | |||||
} | |||||
void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call, | |||||
CheckerContext &C) const { | |||||
ProgramStateRef State = C.getState(); | |||||
SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); | |||||
if (!StreamSym) | |||||
return; | |||||
const StreamState *SS = State->get<StreamMap>(StreamSym); | |||||
if (!SS) | |||||
return; | |||||
auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); | |||||
if (!CE) | |||||
return; | |||||
assertStreamStateOpened(SS); | |||||
State = State->set<StreamMap>(StreamSym, | |||||
StreamState::getOpened(Desc, ErrorNone, false)); | |||||
C.addTransition(State); | |||||
} | |||||
void StreamChecker::evalClearerr(const FnDescription *Desc, | void StreamChecker::evalClearerr(const FnDescription *Desc, | ||||
const CallEvent &Call, | const CallEvent &Call, | ||||
CheckerContext &C) const { | CheckerContext &C) const { | ||||
ProgramStateRef State = C.getState(); | ProgramStateRef State = C.getState(); | ||||
SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); | SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); | ||||
if (!StreamSym) | if (!StreamSym) | ||||
return; | return; | ||||
const StreamState *SS = State->get<StreamMap>(StreamSym); | const StreamState *SS = State->get<StreamMap>(StreamSym); | ||||
if (!SS) | if (!SS) | ||||
return; | return; | ||||
assertStreamStateOpened(SS); | assertStreamStateOpened(SS); | ||||
// FilePositionIndeterminate is not cleared. | // FilePositionIndeterminate is not cleared. | ||||
Why the comment? Seems like its solely StdLibraryFunctionChecker's job to handle errno. Szelethus: Why the comment? Seems like its solely `StdLibraryFunctionChecker`'s job to handle errno. | |||||
State = State->set<StreamMap>( | State = State->set<StreamMap>( | ||||
StreamSym, | StreamSym, | ||||
StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); | StreamState::getOpened(Desc, ErrorNone, SS->FilePositionIndeterminate)); | ||||
C.addTransition(State); | C.addTransition(State); | ||||
} | } | ||||
void StreamChecker::evalFeofFerror(const FnDescription *Desc, | void StreamChecker::evalFeofFerror(const FnDescription *Desc, | ||||
const CallEvent &Call, CheckerContext &C, | const CallEvent &Call, CheckerContext &C, | ||||
const StreamErrorState &ErrorKind) const { | const StreamErrorState &ErrorKind) const { | ||||
ProgramStateRef State = C.getState(); | ProgramStateRef State = C.getState(); | ||||
SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); | SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); | ||||
if (!StreamSym) | if (!StreamSym) | ||||
return; | return; | ||||
const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); | const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); | ||||
if (!CE) | if (!CE) | ||||
return; | return; | ||||
const StreamState *SS = State->get<StreamMap>(StreamSym); | const StreamState *SS = State->get<StreamMap>(StreamSym); | ||||
if (!SS) | if (!SS) | ||||
return; | return; | ||||
assertStreamStateOpened(SS); | assertStreamStateOpened(SS); | ||||
if (SS->ErrorState & ErrorKind) { | if (SS->ErrorState & ErrorKind) { | ||||
Same. Szelethus: Same. | |||||
// Execution path with error of ErrorKind. | // Execution path with error of ErrorKind. | ||||
// Function returns true. | // Function returns true. | ||||
// From now on it is the only one error state. | // From now on it is the only one error state. | ||||
ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); | ProgramStateRef TrueState = bindAndAssumeTrue(State, C, CE); | ||||
C.addTransition(TrueState->set<StreamMap>( | C.addTransition(TrueState->set<StreamMap>( | ||||
StreamSym, StreamState::getOpened(Desc, ErrorKind, | StreamSym, StreamState::getOpened(Desc, ErrorKind, | ||||
SS->FilePositionIndeterminate && | SS->FilePositionIndeterminate && | ||||
!ErrorKind.isFEof()))); | !ErrorKind.isFEof()))); | ||||
▲ Show 20 Lines • Show All 302 Lines • Show Last 20 Lines |
When can this occur?