0}},
};
+ /// Expanded value of EOF, empty before initialization.
mutable std::optional<int> EofVal;
+ /// Expanded value of SEEK_SET, 0 if not found.
+ mutable int SeekSetVal = 0;
+ /// Expanded value of SEEK_CUR, 1 if not found.
+ mutable int SeekCurVal = 1;
+ /// Expanded value of SEEK_END, 2 if not found.
+ mutable int SeekEndVal = 2;
void evalFopen(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
});
}
- void initEof(CheckerContext &C) const {
+ void initMacroValues(CheckerContext &C) const {
if (EofVal)
return;
EofVal = *OptInt;
else
EofVal = -1;
+ if (const std::optional<int> OptInt =
+ tryExpandAsInteger("SEEK_SET", C.getPreprocessor()))
+ SeekSetVal = *OptInt;
+ if (const std::optional<int> OptInt =
+ tryExpandAsInteger("SEEK_END", C.getPreprocessor()))
+ SeekEndVal = *OptInt;
+ if (const std::optional<int> OptInt =
+ tryExpandAsInteger("SEEK_CUR", C.getPreprocessor()))
+ SeekCurVal = *OptInt;
}
/// Searches for the ExplodedNode where the file descriptor was acquired for
void StreamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- initEof(C);
+ initMacroValues(C);
const FnDescription *Desc = lookupFn(Call);
if (!Desc || !Desc->PreFn)
if (!State->get<StreamMap>(StreamSym))
return;
+ const llvm::APSInt *PosV =
+ C.getSValBuilder().getKnownValue(State, Call.getArgSVal(1));
+ const llvm::APSInt *WhenceV =
+ C.getSValBuilder().getKnownValue(State, Call.getArgSVal(2));
+
DefinedSVal RetVal = makeRetVal(C, CE);
// Make expression result.
// It is possible that fseek fails but sets none of the error flags.
// If fseek failed, assume that the file position becomes indeterminate in any
// case.
+ StreamErrorState NewErrS = ErrorNone | ErrorFError;
+ // Setting the position to start of file never produces EOF error.
+ if (!(PosV && *PosV == 0 && WhenceV && *WhenceV == SeekSetVal))
+ NewErrS = NewErrS | ErrorFEof;
StateFailed = StateFailed->set<StreamMap>(
- StreamSym,
- StreamState::getOpened(Desc, ErrorNone | ErrorFEof | ErrorFError, true));
+ StreamSym, StreamState::getOpened(Desc, NewErrS, true));
C.addTransition(StateNotFailed);
C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
return State;
int64_t X = CI->getValue().getSExtValue();
- if (X >= 0 && X <= 2)
+ if (X == SeekSetVal || X == SeekCurVal || X == SeekEndVal)
return State;
if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
FILE *F = fopen("file", "r");
if (!F)
return;
- int rc = fseek(F, 0, SEEK_SET);
+ int rc = fseek(F, 1, SEEK_SET);
if (rc) {
int IsFEof = feof(F), IsFError = ferror(F);
// Get feof or ferror or no error.
fclose(F);
}
+void error_fseek_0(void) {
+ FILE *F = fopen("file", "r");
+ if (!F)
+ return;
+ int rc = fseek(F, 0, SEEK_SET);
+ if (rc) {
+ int IsFEof = feof(F), IsFError = ferror(F);
+ // Get ferror or no error, but not feof.
+ clang_analyzer_eval(IsFError);
+ // expected-warning@-1 {{FALSE}}
+ // expected-warning@-2 {{TRUE}}
+ clang_analyzer_eval(IsFEof);
+ // expected-warning@-1 {{FALSE}}
+ // Error flags should not change.
+ clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
+ if (IsFError)
+ clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
+ else
+ clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+ } else {
+ clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
+ clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+ // Error flags should not change.
+ clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
+ clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
+ }
+ fclose(F);
+}
+
void error_indeterminate(void) {
FILE *F = fopen("file", "r+");
if (!F)