This revision was automatically updated to reflect the committed changes.
Closed by commit rG7eafde981c69: [Checkers] Added support for freopen to
StreamChecker. (authored by balazske).
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D69948/new/
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,37 @@
void pr8081(FILE *stream, long offset, int whence) {
fseek(stream, offset, whence);
}
+
+void check_freopen_1() {
+ FILE *f1 = freopen("foo.c", "r", (FILE *)0); // expected-warning {{Stream pointer might be NULL}}
+ f1 = freopen(0, "w", (FILE *)0x123456); // Do not report this as error.
+}
+
+void check_freopen_2() {
+ FILE *f1 = fopen("foo.c", "r");
+ if (f1) {
+ FILE *f2 = freopen(0, "w", f1);
+ if (f2) {
+ // Check if f1 and f2 point to the same stream.
+ fclose(f1);
+ fclose(f2); // expected-warning {{Try to close a file Descriptor already closed. Cause undefined behaviour}}
+ } else {
+ // Reopen failed.
+ // f1 points now to a possibly invalid stream but this condition is currently not checked.
+ // f2 is NULL.
+ rewind(f1);
+ rewind(f2); // expected-warning {{Stream pointer might be NULL}}
+ }
+ }
+}
+
+void check_freopen_3() {
+ FILE *f1 = fopen("foo.c", "r");
+ if (f1) {
+ // Unchecked result of freopen.
+ // The f1 may be invalid after this call (not checked by the checker).
+ 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
@@ -64,6 +64,7 @@
CallDescriptionMap<FnCheck> Callbacks = {
{{"fopen"}, &StreamChecker::evalFopen},
+ {{"freopen", 3}, &StreamChecker::evalFreopen},
{{"tmpfile"}, &StreamChecker::evalFopen},
{{"fclose", 1}, &StreamChecker::evalFclose},
{{"fread", 4},
@@ -90,6 +91,7 @@
};
void evalFopen(const CallEvent &Call, CheckerContext &C) const;
+ void evalFreopen(const CallEvent &Call, CheckerContext &C) const;
void evalFclose(const CallEvent &Call, CheckerContext &C) const;
void evalFseek(const CallEvent &Call, CheckerContext &C) const;
@@ -160,6 +162,49 @@
C.addTransition(stateNull);
}
+void StreamChecker::evalFreopen(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ Optional<DefinedSVal> StreamVal = Call.getArgSVal(2).getAs<DefinedSVal>();
+ if (!StreamVal)
+ return;
+ // Do not allow NULL as passed stream pointer.
+ // This is not specified in the man page but may crash on some system.
+ checkNullStream(*StreamVal, C, State);
+ // Check if error was generated.
+ if (C.isDifferent())
+ return;
+
+ SymbolRef StreamSym = StreamVal->getAsSymbol();
+ // Do not care about special values for stream ("(FILE *)0x12345"?).
+ if (!StreamSym)
+ return;
+
+ // Generate state for non-failed case.
+ // Return value is the passed stream pointer.
+ // According to the documentations, the stream is closed first
+ // but any close error is ignored. The state changes to (or remains) opened.
+ ProgramStateRef StateRetNotNull =
+ State->BindExpr(CE, C.getLocationContext(), *StreamVal);
+ // Generate state for NULL return value.
+ // Stream switches to OpenFailed state.
+ ProgramStateRef StateRetNull = State->BindExpr(CE, C.getLocationContext(),
+ C.getSValBuilder().makeNull());
+
+ StateRetNotNull =
+ StateRetNotNull->set<StreamMap>(StreamSym, StreamState::getOpened());
+ StateRetNull =
+ StateRetNull->set<StreamMap>(StreamSym, StreamState::getOpenFailed());
+
+ C.addTransition(StateRetNotNull);
+ C.addTransition(StateRetNull);
+}
+
void StreamChecker::evalFclose(const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef State = C.getState();
if (checkDoubleClose(Call, C, State))
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits