balazske created this revision.
Herald added subscribers: cfe-commits, gamesh411, Szelethus, dkrupp.
Herald added a project: clang.
Extend StreamChecker with a new evaluation function for API call 'freopen'.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D69948
Files:
clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
clang/test/Analysis/stream.c
Index: clang/test/Analysis/stream.c
===================================================================
--- clang/test/Analysis/stream.c
+++ clang/test/Analysis/stream.c
@@ -20,6 +20,7 @@
extern int feof(FILE *stream);
extern int ferror(FILE *stream);
extern int fileno(FILE *stream);
+extern FILE *freopen(const char *pathname, const char *mode, FILE *stream);
void check_fread() {
FILE *fp = tmpfile();
@@ -111,6 +112,13 @@
fclose(p); // expected-warning {{Try to close a file Descriptor already closed. Cause undefined behaviour}}
}
+void f_double_close_alias(void) {
+ FILE *p1 = fopen("foo", "r");
+ FILE *p2 = p1;
+ fclose(p1);
+ fclose(p2); // expected-warning {{Try to close a file Descriptor already closed. Cause undefined behaviour}}
+}
+
void f_leak(int c) {
FILE *p = fopen("foo.c", "r");
if(c)
@@ -134,3 +142,36 @@
void pr8081(FILE *stream, long offset, int whence) {
fseek(stream, offset, whence);
}
+
+void check_freopen_1() {
+ FILE *f1 = freopen("foo.c", "r", 0);
+ if (f1) {
+ fclose(f1); // no-warning
+ fclose(f1); // expected-warning {{Try to close a file Descriptor already closed. Cause undefined behaviour}}
+ } else {
+ rewind(f1); // expected-warning {{Stream pointer might be NULL}}
+ }
+}
+
+void check_freopen_2() {
+ FILE *f1 = fopen("foo.c", "r");
+ if (f1) {
+ FILE *f2 = freopen(0, "w", f1);
+ if (f2) {
+ fclose(f1);
+ fclose(f2); // expected-warning {{Try to close a file Descriptor already closed. Cause undefined behaviour}}
+ } else {
+ rewind(f1);
+ rewind(f2); // expected-warning {{Stream pointer might be NULL}}
+ }
+ }
+}
+
+void check_freopen_3() {
+ FILE *f1 = fopen("foo.c", "r");
+ if (f1) {
+ freopen(0, "w", f1);
+ rewind(f1);
+ fclose(f1);
+ }
+}
Index: clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -66,6 +66,7 @@
{{"fopen"}, &StreamChecker::evalFopen},
{{"tmpfile"}, &StreamChecker::evalFopen},
{{"fclose", 1}, &StreamChecker::evalFclose},
+ {{"freopen", 3}, &StreamChecker::evalFreopen},
{{"fread", 4},
std::bind(&StreamChecker::checkArgNullStream, _1, _2, _3, 3)},
{{"fwrite", 4},
@@ -91,6 +92,7 @@
void evalFopen(const CallEvent &Call, CheckerContext &C) const;
void evalFclose(const CallEvent &Call, CheckerContext &C) const;
+ void evalFreopen(const CallEvent &Call, CheckerContext &C) const;
void evalFseek(const CallEvent &Call, CheckerContext &C) const;
void checkArgNullStream(const CallEvent &Call, CheckerContext &C,
@@ -166,6 +168,72 @@
C.addTransition(State);
}
+void StreamChecker::evalFreopen(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SValBuilder &SValBuilder = C.getSValBuilder();
+ const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
+ ConstraintManager &CM = C.getConstraintManager();
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ Optional<DefinedSVal> StreamVal = Call.getArgSVal(2).getAs<DefinedSVal>();
+ if (!StreamVal)
+ return;
+
+ DefinedSVal RetVal =
+ SValBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount())
+ .castAs<DefinedSVal>();
+ SymbolRef RetSym = RetVal.getAsSymbol();
+ assert(RetSym && "RetVal must be a symbol here.");
+
+ ProgramStateRef StateStreamNotNull, StateStreamNull;
+ std::tie(StateStreamNotNull, StateStreamNull) =
+ CM.assumeDual(State, *StreamVal);
+
+ if (StateStreamNull) {
+ StateStreamNull =
+ StateStreamNull->BindExpr(CE, C.getLocationContext(), RetVal);
+
+ ProgramStateRef StateRetNotNull, StateRetNull;
+ std::tie(StateRetNotNull, StateRetNull) =
+ CM.assumeDual(StateStreamNull, RetVal);
+ if (StateRetNull) {
+ StateRetNull =
+ StateRetNull->set<StreamMap>(RetSym, StreamState::getOpenFailed());
+ C.addTransition(StateRetNull);
+ }
+ if (StateRetNotNull) {
+ StateRetNotNull =
+ StateRetNotNull->set<StreamMap>(RetSym, StreamState::getOpened());
+ C.addTransition(StateRetNotNull);
+ }
+ }
+
+ if (StateStreamNotNull) {
+ SymbolRef StreamSym = StreamVal->getAsSymbol();
+
+ ProgramStateRef StateRetNull =
+ StateStreamNotNull->BindExpr(CE, C.getLocationContext(), RetVal);
+ StateRetNull = CM.assume(StateRetNull, RetVal, false);
+ assert(StateRetNull && "This assumption should not fail.");
+ StateRetNull =
+ StateRetNull->set<StreamMap>(RetSym, StreamState::getOpenFailed());
+ if (StreamSym)
+ StateRetNull =
+ StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed());
+ C.addTransition(StateRetNull);
+
+ ProgramStateRef StateRetNotNull =
+ StateStreamNotNull->BindExpr(CE, C.getLocationContext(), *StreamVal);
+ if (StreamSym)
+ StateRetNotNull =
+ StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened());
+ C.addTransition(StateRetNotNull);
+ }
+}
+
void StreamChecker::evalFseek(const CallEvent &Call, CheckerContext &C) const {
const Expr *AE2 = Call.getArgExpr(2);
if (!AE2)
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits