Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang][analyzer] Support 'tello' and 'fseeko' in the StreamChecker #77580

Merged
merged 5 commits into from Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 9 additions & 5 deletions clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
Expand Up @@ -268,8 +268,12 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
std::bind(&StreamChecker::evalUngetc, _1, _2, _3, _4), 1}},
{{{"fseek"}, 3},
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{{"fseeko"}, 3},
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{{"ftell"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
{{{"ftello"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
{{{"fflush"}, 1},
{&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
{{{"rewind"}, 1},
Expand Down Expand Up @@ -1113,18 +1117,18 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
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>();
auto Cond =
SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(Call.getResultType()),
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));
CE, C.getLocationContext(), SVB.makeIntVal(-1, Call.getResultType()));

// This function does not affect the stream state.
// Still we add success and failure state with the appropriate return value.
Expand Down
3 changes: 3 additions & 0 deletions clang/test/Analysis/Inputs/system-header-simulator.h
Expand Up @@ -13,6 +13,7 @@ typedef __typeof(sizeof(int)) size_t;
typedef long long __int64_t;
typedef __int64_t __darwin_off_t;
typedef __darwin_off_t fpos_t;
typedef int off_t;

typedef struct _FILE FILE;
#define SEEK_SET 0 /* Seek from beginning of file. */
Expand Down Expand Up @@ -55,7 +56,9 @@ int fputc(int ch, FILE *stream);
int fputs(const char *restrict s, FILE *restrict stream);
int ungetc(int c, FILE *stream);
int fseek(FILE *__stream, long int __off, int __whence);
int fseeko(FILE *__stream, off_t __off, int __whence);
long int ftell(FILE *__stream);
off_t ftello(FILE *__stream);
void rewind(FILE *__stream);
int fgetpos(FILE *restrict stream, fpos_t *restrict pos);
int fsetpos(FILE *stream, const fpos_t *pos);
Expand Down
73 changes: 73 additions & 0 deletions clang/test/Analysis/stream-error.c
Expand Up @@ -295,6 +295,25 @@ void error_fseek(void) {
fclose(F);
}

void error_fseeko(void) {
FILE *F = fopen("file", "r");
if (!F)
return;
int rc = fseeko(F, 1, SEEK_SET);
if (rc) {
int IsFEof = feof(F), IsFError = ferror(F);
// Get feof or ferror or no error.
clang_analyzer_eval(IsFEof || IsFError);
// expected-warning@-1 {{FALSE}}
// expected-warning@-2 {{TRUE}}
clang_analyzer_eval(IsFEof && IsFError); // expected-warning {{FALSE}}
} else {
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
}
fclose(F);
}

void error_fseek_0(void) {
FILE *F = fopen("file", "r");
if (!F)
Expand Down Expand Up @@ -324,6 +343,60 @@ void error_fseek_0(void) {
fclose(F);
}

void error_fseeko_0(void) {
FILE *F = fopen("file", "r");
if (!F)
return;
int rc = fseeko(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}}
} else {
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
}
fclose(F);
}

void error_ftell(void) {
FILE *F = fopen("file", "r");
if (!F)
return;
long rc = ftell(F);
if (rc >= 0)
clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
else
clang_analyzer_eval(rc == -1); // expected-warning {{TRUE}}
clang_analyzer_eval(feof(F) && ferror(F)); // expected-warning {{FALSE}}
StreamTesterChecker_make_feof_stream(F);
rc = ftell(F);
clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}}
StreamTesterChecker_make_ferror_stream(F);
rc = ftell(F);
clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}}
clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}}
fclose(F);
}

void error_ftello(void) {
FILE *F = tmpfile();
if (!F)
return;
long rc = ftello(F);
if (rc >= 0)
clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
else
clang_analyzer_eval(rc == -1); // expected-warning {{TRUE}}
clang_analyzer_eval(feof(F) && ferror(F)); // expected-warning {{FALSE}}
benshi001 marked this conversation as resolved.
Show resolved Hide resolved
fclose(F);
}

benshi001 marked this conversation as resolved.
Show resolved Hide resolved
void error_fflush_after_fclose(void) {
FILE *F = tmpfile();
int Ret;
Expand Down