Skip to content

Commit

Permalink
[clang][analyzer] Improve modeling of 'fseeko' and 'ftello' in StdLib…
Browse files Browse the repository at this point in the history
…raryFunctionsChecker (#77902)
  • Loading branch information
benshi001 committed Jan 16, 2024
1 parent f725bb9 commit 27d963a
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 17 deletions.
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

0 comments on commit 27d963a

Please sign in to comment.