|
21 | 21 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
|
22 | 22 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
|
23 | 23 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
|
| 24 | +#include "llvm/ADT/Sequence.h" |
24 | 25 | #include <functional>
|
25 | 26 | #include <optional>
|
26 | 27 |
|
@@ -629,6 +630,21 @@ const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
|
629 | 630 | return nullptr;
|
630 | 631 | }
|
631 | 632 |
|
| 633 | +static ProgramStateRef escapeArgs(ProgramStateRef State, CheckerContext &C, |
| 634 | + const CallEvent &Call, |
| 635 | + ArrayRef<unsigned int> EscapingArgs) { |
| 636 | + const auto *CE = Call.getOriginExpr(); |
| 637 | + |
| 638 | + SmallVector<SVal> EscapingVals; |
| 639 | + EscapingVals.reserve(EscapingArgs.size()); |
| 640 | + for (auto EscArgIdx : EscapingArgs) |
| 641 | + EscapingVals.push_back(Call.getArgSVal(EscArgIdx)); |
| 642 | + State = State->invalidateRegions(EscapingVals, CE, C.blockCount(), |
| 643 | + C.getLocationContext(), |
| 644 | + /*CausesPointerEscape=*/false); |
| 645 | + return State; |
| 646 | +} |
| 647 | + |
632 | 648 | //===----------------------------------------------------------------------===//
|
633 | 649 | // Methods of StreamChecker.
|
634 | 650 | //===----------------------------------------------------------------------===//
|
@@ -819,6 +835,11 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
|
819 | 835 | return;
|
820 | 836 | }
|
821 | 837 |
|
| 838 | + // At read, invalidate the buffer in any case of error or success, |
| 839 | + // except if EOF was already present. |
| 840 | + if (IsFread && !E.isStreamEof()) |
| 841 | + State = escapeArgs(State, C, Call, {0}); |
| 842 | + |
822 | 843 | // Generate a transition for the success state.
|
823 | 844 | // If we know the state to be FEOF at fread, do not add a success state.
|
824 | 845 | if (!IsFread || !E.isStreamEof()) {
|
@@ -863,6 +884,9 @@ void StreamChecker::evalFgetx(const FnDescription *Desc, const CallEvent &Call,
|
863 | 884 | return;
|
864 | 885 |
|
865 | 886 | if (!E.isStreamEof()) {
|
| 887 | + // If there was already EOF, assume that read buffer is not changed. |
| 888 | + // Otherwise it may change at success or failure. |
| 889 | + State = escapeArgs(State, C, Call, {0}); |
866 | 890 | if (SingleChar) {
|
867 | 891 | // Generate a transition for the success state of `fgetc`.
|
868 | 892 | NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
|
@@ -1011,6 +1035,14 @@ void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent &Call,
|
1011 | 1035 | State->BindExpr(E.CE, C.getLocationContext(), RetVal);
|
1012 | 1036 | StateNotFailed =
|
1013 | 1037 | E.assumeBinOpNN(StateNotFailed, BO_GE, RetVal, E.getZeroVal(Call));
|
| 1038 | + if (!StateNotFailed) |
| 1039 | + return; |
| 1040 | + |
| 1041 | + SmallVector<unsigned int> EscArgs; |
| 1042 | + for (auto EscArg : llvm::seq(2u, Call.getNumArgs())) |
| 1043 | + EscArgs.push_back(EscArg); |
| 1044 | + StateNotFailed = escapeArgs(StateNotFailed, C, Call, EscArgs); |
| 1045 | + |
1014 | 1046 | if (StateNotFailed)
|
1015 | 1047 | C.addTransition(StateNotFailed);
|
1016 | 1048 | }
|
@@ -1073,8 +1105,12 @@ void StreamChecker::evalGetdelim(const FnDescription *Desc,
|
1073 | 1105 | // return -1.
|
1074 | 1106 | // If an error occurs, the function shall return -1 and set 'errno'.
|
1075 | 1107 |
|
1076 |
| - // Add transition for the successful state. |
1077 | 1108 | if (!E.isStreamEof()) {
|
| 1109 | + // Escape buffer and size (may change by the call). |
| 1110 | + // May happen even at error (partial read?). |
| 1111 | + State = escapeArgs(State, C, Call, {0, 1}); |
| 1112 | + |
| 1113 | + // Add transition for the successful state. |
1078 | 1114 | NonLoc RetVal = makeRetVal(C, E.CE).castAs<NonLoc>();
|
1079 | 1115 | ProgramStateRef StateNotFailed =
|
1080 | 1116 | State->BindExpr(E.CE, C.getLocationContext(), RetVal);
|
@@ -1161,6 +1197,7 @@ void StreamChecker::evalFgetpos(const FnDescription *Desc,
|
1161 | 1197 |
|
1162 | 1198 | ProgramStateRef StateNotFailed, StateFailed;
|
1163 | 1199 | std::tie(StateFailed, StateNotFailed) = E.makeRetValAndAssumeDual(State, C);
|
| 1200 | + StateNotFailed = escapeArgs(StateNotFailed, C, Call, {1}); |
1164 | 1201 |
|
1165 | 1202 | // This function does not affect the stream state.
|
1166 | 1203 | // Still we add success and failure state with the appropriate return value.
|
|
0 commit comments