balazske created this revision.
Herald added subscribers: cfe-commits, gamesh411, Szelethus, dkrupp.
Herald added a project: clang.

The checker now recognizes 'freopen' as file opening function.
Added support for binding the return value to the passed stream.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D67480

Files:
  clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
  clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h
  clang/test/Analysis/simple-stream-checks.c

Index: clang/test/Analysis/simple-stream-checks.c
===================================================================
--- clang/test/Analysis/simple-stream-checks.c
+++ clang/test/Analysis/simple-stream-checks.c
@@ -94,3 +94,40 @@
   FILE *fp = fopen("myfile.txt", "w");
   fp = 0;
 } // expected-warning {{Opened file is never closed; potential resource leak}}
+
+void testReopenAndClose() {
+  FILE *F = fopen("myfile.txt", "w");
+  freopen("myfile1.txt", "w", F);
+  fclose(F);
+} // no-warning
+
+void testReopenAndNoClose() {
+  FILE *F = fopen("myfile.txt", "w");
+  freopen("myfile1.txt", "w", F);
+} // expected-warning {{Opened file is never closed; potential resource leak}}
+
+void testCloseAndReopen() {
+  FILE *F = fopen("myfile.txt", "w");
+  fclose(F);
+  freopen("myfile1.txt", "w", F);
+} // expected-warning {{Opened file is never closed; potential resource leak}}
+
+void testCloseAndReopenAndClose() {
+  FILE *F = fopen("myfile.txt", "w");
+  fclose(F);
+  freopen("myfile1.txt", "w", F);
+  fclose(F);
+} // no-warning
+
+void testBindingOfReopenReturnValue() {
+  FILE *F = fopen("myfile.txt", "w");
+  FILE *F1 = freopen("", "r", F);
+  fclose(F);
+  fclose(F1); // expected-warning {{Closing a previously closed file stream}}
+}
+
+void testEscapeAfterReopen() {
+  FILE *F = fopen("myfile.txt", "w");
+  FILE *F1 = freopen("", "r", F);
+  myfclose(F);
+} // no-warning
Index: clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h
===================================================================
--- clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h
+++ clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h
@@ -9,6 +9,7 @@
   unsigned char *_p;
 } FILE;
 FILE *fopen(const char * restrict, const char * restrict) __asm("_" "fopen" );
+FILE *freopen(const char *restrict, const char *restrict, FILE *restrict);
 int fputc(int, FILE *);
 int fputs(const char * restrict, FILE * restrict) __asm("_" "fputs" );
 int fclose(FILE *);
Index: clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -51,7 +51,7 @@
                                            check::PreCall,
                                            check::DeadSymbols,
                                            check::PointerEscape> {
-  CallDescription OpenFn, CloseFn;
+  CallDescription OpenFn, CloseFn, ReopenFn;
 
   std::unique_ptr<BugType> DoubleCloseBugType;
   std::unique_ptr<BugType> LeakBugType;
@@ -68,7 +68,7 @@
 public:
   SimpleStreamChecker();
 
-  /// Process fopen.
+  /// Process fopen and freopen.
   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
   /// Process fclose.
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -103,7 +103,7 @@
 } // end anonymous namespace
 
 SimpleStreamChecker::SimpleStreamChecker()
-    : OpenFn("fopen"), CloseFn("fclose", 1) {
+    : OpenFn("fopen"), CloseFn("fclose", 1), ReopenFn("freopen", 3) {
   // Initialize the bug types.
   DoubleCloseBugType.reset(
       new BugType(this, "Double fclose", "Unix Stream API Error"));
@@ -114,22 +114,60 @@
                   /*SuppressOnSink=*/true));
 }
 
+static ProgramStateRef tryBindReopenResult(const CallEvent &Call,
+                                           CheckerContext &C,
+                                           ProgramStateRef State) {
+  // Get value of file descriptor input argument.
+  SymbolRef InputDesc = Call.getArgSVal(2).getAsSymbol();
+  if (!InputDesc)
+    return State;
+
+  // Get the return value.
+  SymbolRef RetDesc = Call.getReturnValue().getAsSymbol();
+  if (!RetDesc)
+    return State;
+
+  // Bind the input to the return value.
+  const Expr *E = Call.getOriginExpr();
+  if (!E)
+    return State;
+
+  return State->BindExpr(E, C.getLocationContext(),
+                         C.getSValBuilder().makeSymbolVal(InputDesc));
+}
+
 void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
                                         CheckerContext &C) const {
   if (!Call.isGlobalCFunction())
     return;
 
-  if (!Call.isCalled(OpenFn))
-    return;
+  ProgramStateRef State = C.getState();
 
-  // Get the symbolic value corresponding to the file handle.
-  SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
-  if (!FileDesc)
+  if (Call.isCalled(OpenFn)) {
+    // Get the symbolic value corresponding to the file handle.
+    SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
+    if (!FileDesc)
+      return;
+
+    // Generate the next transition (an edge in the exploded graph).
+    State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
+  } else if (Call.isCalled(ReopenFn)) {
+    // Try to tell the analyzer that the return value of 'freopen' is the same
+    // as argument 2 (if no error occurs).
+    State = tryBindReopenResult(Call, C, State);
+
+    // Set the state to opened regardless of what is was before.
+    SymbolRef InputDesc = Call.getArgSVal(2).getAsSymbol();
+    if (InputDesc) {
+      const StreamState *SS = State->get<StreamMap>(InputDesc);
+      if (!SS || !SS->isOpened())
+        State = State->set<StreamMap>(InputDesc, StreamState::getOpened());
+    }
+  } else {
     return;
+  }
 
   // Generate the next transition (an edge in the exploded graph).
-  ProgramStateRef State = C.getState();
-  State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
   C.addTransition(State);
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to