Skip to content

[clang][analyzer] Improve modeling of 'fseeko' and 'ftello' in StdLibraryFunctionsChecker #77902

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

Merged
merged 2 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2220,6 +2220,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
0, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))
.ArgConstraint(NotNull(ArgNo(1))));

std::optional<QualType> Off_tTy = lookupTy("off_t");
std::optional<RangeInt> Off_tMax = getMaxValue(Off_tTy);

// int fseek(FILE *stream, long offset, int whence);
// FIXME: It can be possible to get the 'SEEK_' values (like EOFv) and use
// these for condition of arg 2.
Expand All @@ -2232,6 +2235,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}})));

// int fseeko(FILE *stream, off_t offset, int whence);
addToFunctionSummaryMap(
"fseeko",
Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}),
Summary(NoEvalCall)
.Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg)
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}})));

// int fgetpos(FILE *restrict stream, fpos_t *restrict pos);
// From 'The Open Group Base Specifications Issue 7, 2018 edition':
// "The fgetpos() function shall not change the setting of errno if
Expand Down Expand Up @@ -2279,6 +2292,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));

// off_t ftello(FILE *stream);
addToFunctionSummaryMap(
"ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}),
Summary(NoEvalCall)
.Case({ReturnValueCondition(WithinRange, Range(0, Off_tMax))},
ErrnoMustNotBeChecked, GenericSuccessMsg)
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)
.ArgConstraint(NotNull(ArgNo(0))));

// int fileno(FILE *stream);
addToFunctionSummaryMap(
"fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
Expand Down Expand Up @@ -2410,8 +2432,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));

std::optional<QualType> Off_tTy = lookupTy("off_t");

// int truncate(const char *path, off_t length);
addToFunctionSummaryMap(
"truncate",
Expand Down Expand Up @@ -2854,19 +2874,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));

// int fseeko(FILE *stream, off_t offset, int whence);
addToFunctionSummaryMap(
"fseeko",
Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}),
Summary(NoEvalCall)
.Case(ReturnsZeroOrMinusOne, ErrnoIrrelevant)
.ArgConstraint(NotNull(ArgNo(0))));

// off_t ftello(FILE *stream);
addToFunctionSummaryMap(
"ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));

// void *mmap(void *addr, size_t length, int prot, int flags, int fd,
// off_t offset);
// FIXME: Improve for errno modeling.
Expand Down
4 changes: 2 additions & 2 deletions clang/test/Analysis/std-c-library-functions-POSIX.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
// CHECK: Loaded summary for: FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *restrict stream)
// CHECK: Loaded summary for: int fclose(FILE *stream)
// CHECK: Loaded summary for: int fseek(FILE *stream, long offset, int whence)
// CHECK: Loaded summary for: int fseeko(FILE *stream, off_t offset, int whence)
// CHECK: Loaded summary for: off_t ftello(FILE *stream)
// CHECK: Loaded summary for: int fileno(FILE *stream)
// CHECK: Loaded summary for: long a64l(const char *str64)
// CHECK: Loaded summary for: char *l64a(long value)
Expand Down Expand Up @@ -80,8 +82,6 @@
// CHECK: Loaded summary for: void rewinddir(DIR *dir)
// CHECK: Loaded summary for: void seekdir(DIR *dirp, long loc)
// CHECK: Loaded summary for: int rand_r(unsigned int *seedp)
// CHECK: Loaded summary for: int fseeko(FILE *stream, off_t offset, int whence)
// CHECK: Loaded summary for: off_t ftello(FILE *stream)
// CHECK: Loaded summary for: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
// CHECK: Loaded summary for: void *mmap64(void *addr, size_t length, int prot, int flags, int fd, off64_t offset)
// CHECK: Loaded summary for: int pipe(int fildes[2])
Expand Down
31 changes: 31 additions & 0 deletions clang/test/Analysis/stream-errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,29 @@ void check_fseek(void) {
int S = fseek(F, 11, SEEK_SET);
if (S != 0) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
clang_analyzer_eval(S == -1); // expected-warning{{TRUE}}
if (errno) {} // no-warning
fclose(F);
return;
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}

void check_fseeko(void) {
FILE *F = tmpfile();
if (!F)
return;
int S = fseeko(F, 11, SEEK_SET);
if (S == -1) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
} else {
clang_analyzer_eval(S == 0); // expected-warning{{TRUE}}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
}
fclose(F);
}

void check_no_errno_change(void) {
FILE *F = tmpfile();
if (!F)
Expand Down Expand Up @@ -197,6 +213,21 @@ void check_ftell(void) {
fclose(F);
}

void check_ftello(void) {
FILE *F = tmpfile();
if (!F)
return;
off_t Ret = ftello(F);
if (Ret >= 0) {
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
} else {
clang_analyzer_eval(Ret == -1); // expected-warning{{TRUE}}
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
if (errno) {} // no-warning
}
fclose(F);
}

void check_rewind(void) {
FILE *F = tmpfile();
if (!F)
Expand Down