diff options
author | Balázs Kéri <balazs.keri@ericsson.com> | 2024-01-12 17:00:14 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-12 17:00:14 +0100 |
commit | 8550e8845c4fe1aea3bd3d69bcc33d33040b1f13 (patch) | |
tree | a79e261b26a73e90150aec5db1e0267bdbf44857 /clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | |
parent | a300b2403784f416f36a1cee8d0425975f790b45 (diff) | |
download | llvm-8550e8845c4fe1aea3bd3d69bcc33d33040b1f13.zip llvm-8550e8845c4fe1aea3bd3d69bcc33d33040b1f13.tar.gz llvm-8550e8845c4fe1aea3bd3d69bcc33d33040b1f13.tar.bz2 |
[clang][analyzer] Add function 'fprintf' to StreamChecker. (#77613)
[clang][analyzer] Add function 'fprintf' to StreamChecker.
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 742426a..95c7503 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -263,6 +263,9 @@ private: {{{"fputs"}, 2}, {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), std::bind(&StreamChecker::evalFputx, _1, _2, _3, _4, false), 1}}, + {{{"fprintf"}}, + {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), + std::bind(&StreamChecker::evalFprintf, _1, _2, _3, _4), 0}}, {{{"ungetc"}, 2}, {std::bind(&StreamChecker::preReadWrite, _1, _2, _3, _4, false), std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}}, @@ -339,6 +342,9 @@ private: void evalFputx(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C, bool IsSingleChar) const; + void evalFprintf(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const; + void evalUngetc(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const; @@ -926,6 +932,49 @@ void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, C.addTransition(StateFailed); } +void StreamChecker::evalFprintf(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (Call.getNumArgs() < 2) + return; + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) + return; + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + const StreamState *OldSS = State->get<StreamMap>(StreamSym); + if (!OldSS) + return; + + assertStreamStateOpened(OldSS); + + NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>(); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); + auto &ACtx = C.getASTContext(); + auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ACtx.IntTy), + SVB.getConditionType()) + .getAs<DefinedOrUnknownSVal>(); + if (!Cond) + return; + ProgramStateRef StateNotFailed, StateFailed; + std::tie(StateNotFailed, StateFailed) = State->assume(*Cond); + + StateNotFailed = + StateNotFailed->set<StreamMap>(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + + // Add transition for the failed state. The resulting value of the file + // position indicator for the stream is indeterminate. + StateFailed = StateFailed->set<StreamMap>( + StreamSym, StreamState::getOpened(Desc, ErrorFError, true)); + C.addTransition(StateFailed); +} + void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); |