-
Notifications
You must be signed in to change notification settings - Fork 10.8k
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] Improve modeling of fileno
in the StreamChecker
#76207
Conversation
@llvm/pr-subscribers-clang-static-analyzer-1 Author: Ben Shi (benshi001) ChangesFull diff: https://github.com/llvm/llvm-project/pull/76207.diff 3 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 254b36ed03968d..80ac910060720f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -265,7 +265,11 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
{{{"fseek"}, 3},
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{{"ftell"}, 1},
- {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
+ {&StreamChecker::preDefault,
+ std::bind(&StreamChecker::evalFtellFileno, _1, _2, _3, _4, true), 0}},
+ {{{"fileno"}, 1},
+ {&StreamChecker::preDefault,
+ std::bind(&StreamChecker::evalFtellFileno, _1, _2, _3, _4, false), 0}},
{{{"fflush"}, 1},
{&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
{{{"rewind"}, 1},
@@ -284,7 +288,6 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
{&StreamChecker::preDefault,
std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
0}},
- {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
};
CallDescriptionMap<FnDescription> FnTestDescriptions = {
@@ -342,8 +345,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
- void evalFtell(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const;
+ void evalFtellFileno(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C, bool IsRetLongTy) const;
void evalRewind(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@@ -1049,8 +1052,9 @@ void StreamChecker::evalFsetpos(const FnDescription *Desc,
C.addTransition(StateFailed);
}
-void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const {
+void StreamChecker::evalFtellFileno(const FnDescription *Desc,
+ const CallEvent &Call, CheckerContext &C,
+ bool IsRetLongTy) const {
ProgramStateRef State = C.getState();
SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
if (!Sym)
@@ -1067,10 +1071,12 @@ 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(IsRetLongTy ? C.getASTContext().LongTy
+ : C.getASTContext().IntTy),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
if (!Cond)
return;
StateNotFailed = StateNotFailed->assume(*Cond, true);
@@ -1078,7 +1084,9 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
return;
ProgramStateRef StateFailed = State->BindExpr(
- CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
+ CE, C.getLocationContext(),
+ SVB.makeIntVal(-1, IsRetLongTy ? C.getASTContext().LongTy
+ : C.getASTContext().IntTy));
// This function does not affect the stream state.
// Still we add success and failure state with the appropriate return value.
diff --git a/clang/test/Analysis/stream-errno.c b/clang/test/Analysis/stream-errno.c
index bf0a61db2424f9..62befbc78e5a75 100644
--- a/clang/test/Analysis/stream-errno.c
+++ b/clang/test/Analysis/stream-errno.c
@@ -216,9 +216,10 @@ void check_fileno(void) {
int N = fileno(F);
if (N == -1) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
- if (errno) {} // no-warning
- fclose(F);
- return;
+ if (errno) {} // no-warning
+ } else {
+ clang_analyzer_eval(N >= 0); // expected-warning{{TRUE}}
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
+ fclose(F);
}
diff --git a/clang/test/Analysis/stream-noopen.c b/clang/test/Analysis/stream-noopen.c
index 2daf640c18a1d4..198b5dc45e35da 100644
--- a/clang/test/Analysis/stream-noopen.c
+++ b/clang/test/Analysis/stream-noopen.c
@@ -129,6 +129,18 @@ void check_ftell(FILE *F) {
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
+void check_fileno(FILE *F) {
+ int Ret = fileno(F);
+ clang_analyzer_eval(F != NULL); // expected-warning{{TRUE}}
+ if (!(Ret >= 0)) {
+ clang_analyzer_eval(Ret == -1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
+ if (errno) {} // no-warning
+ }
+ clang_analyzer_eval(feof(F)); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(ferror(F)); // expected-warning{{UNKNOWN}}
+}
+
void test_rewind(FILE *F) {
errno = 0;
rewind(F);
|
@llvm/pr-subscribers-clang Author: Ben Shi (benshi001) ChangesFull diff: https://github.com/llvm/llvm-project/pull/76207.diff 3 Files Affected:
diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 254b36ed03968d..80ac910060720f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -265,7 +265,11 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
{{{"fseek"}, 3},
{&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
{{{"ftell"}, 1},
- {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
+ {&StreamChecker::preDefault,
+ std::bind(&StreamChecker::evalFtellFileno, _1, _2, _3, _4, true), 0}},
+ {{{"fileno"}, 1},
+ {&StreamChecker::preDefault,
+ std::bind(&StreamChecker::evalFtellFileno, _1, _2, _3, _4, false), 0}},
{{{"fflush"}, 1},
{&StreamChecker::preFflush, &StreamChecker::evalFflush, 0}},
{{{"rewind"}, 1},
@@ -284,7 +288,6 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
{&StreamChecker::preDefault,
std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
0}},
- {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
};
CallDescriptionMap<FnDescription> FnTestDescriptions = {
@@ -342,8 +345,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
- void evalFtell(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const;
+ void evalFtellFileno(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C, bool IsRetLongTy) const;
void evalRewind(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@@ -1049,8 +1052,9 @@ void StreamChecker::evalFsetpos(const FnDescription *Desc,
C.addTransition(StateFailed);
}
-void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
- CheckerContext &C) const {
+void StreamChecker::evalFtellFileno(const FnDescription *Desc,
+ const CallEvent &Call, CheckerContext &C,
+ bool IsRetLongTy) const {
ProgramStateRef State = C.getState();
SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
if (!Sym)
@@ -1067,10 +1071,12 @@ 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(IsRetLongTy ? C.getASTContext().LongTy
+ : C.getASTContext().IntTy),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
if (!Cond)
return;
StateNotFailed = StateNotFailed->assume(*Cond, true);
@@ -1078,7 +1084,9 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
return;
ProgramStateRef StateFailed = State->BindExpr(
- CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
+ CE, C.getLocationContext(),
+ SVB.makeIntVal(-1, IsRetLongTy ? C.getASTContext().LongTy
+ : C.getASTContext().IntTy));
// This function does not affect the stream state.
// Still we add success and failure state with the appropriate return value.
diff --git a/clang/test/Analysis/stream-errno.c b/clang/test/Analysis/stream-errno.c
index bf0a61db2424f9..62befbc78e5a75 100644
--- a/clang/test/Analysis/stream-errno.c
+++ b/clang/test/Analysis/stream-errno.c
@@ -216,9 +216,10 @@ void check_fileno(void) {
int N = fileno(F);
if (N == -1) {
clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
- if (errno) {} // no-warning
- fclose(F);
- return;
+ if (errno) {} // no-warning
+ } else {
+ clang_analyzer_eval(N >= 0); // expected-warning{{TRUE}}
}
if (errno) {} // expected-warning{{An undefined value may be read from 'errno'}}
+ fclose(F);
}
diff --git a/clang/test/Analysis/stream-noopen.c b/clang/test/Analysis/stream-noopen.c
index 2daf640c18a1d4..198b5dc45e35da 100644
--- a/clang/test/Analysis/stream-noopen.c
+++ b/clang/test/Analysis/stream-noopen.c
@@ -129,6 +129,18 @@ void check_ftell(FILE *F) {
clang_analyzer_eval(ferror(F)); // expected-warning {{UNKNOWN}}
}
+void check_fileno(FILE *F) {
+ int Ret = fileno(F);
+ clang_analyzer_eval(F != NULL); // expected-warning{{TRUE}}
+ if (!(Ret >= 0)) {
+ clang_analyzer_eval(Ret == -1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
+ if (errno) {} // no-warning
+ }
+ clang_analyzer_eval(feof(F)); // expected-warning{{UNKNOWN}}
+ clang_analyzer_eval(ferror(F)); // expected-warning{{UNKNOWN}}
+}
+
void test_rewind(FILE *F) {
errno = 0;
rewind(F);
|
The differences are
|
I do not see much benefit of adding |
No description provided.