[cfe-commits] r108669 - in /cfe/trunk: lib/Checker/StreamChecker.cpp test/Analysis/stream.c

Zhongxing Xu xuzhongxing at gmail.com
Sun Jul 18 18:52:29 PDT 2010


Author: zhongxingxu
Date: Sun Jul 18 20:52:29 2010
New Revision: 108669

URL: http://llvm.org/viewvc/llvm-project?rev=108669&view=rev
Log:
Add double close check to StreamChecker. Patch by Lei Zhang.

Modified:
    cfe/trunk/lib/Checker/StreamChecker.cpp
    cfe/trunk/test/Analysis/stream.c

Modified: cfe/trunk/lib/Checker/StreamChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/StreamChecker.cpp?rev=108669&r1=108668&r2=108669&view=diff
==============================================================================
--- cfe/trunk/lib/Checker/StreamChecker.cpp (original)
+++ cfe/trunk/lib/Checker/StreamChecker.cpp Sun Jul 18 20:52:29 2010
@@ -23,18 +23,42 @@
 
 namespace {
 
+struct StreamState {
+  enum Kind { Opened, Closed, OpenFailed } K;
+  const Stmt *S;
+
+  StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
+
+  bool isOpened() const { return K == Opened; }
+  bool isClosed() const { return K == Closed; }
+  bool isOpenFailed() const { return K == OpenFailed; }
+
+  bool operator==(const StreamState &X) const {
+    return K == X.K && S == X.S;
+  }
+
+  static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
+  static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
+  static StreamState getOpenFailed(const Stmt *s) { return StreamState(OpenFailed, s); }
+
+  void Profile(llvm::FoldingSetNodeID &ID) const {
+    ID.AddInteger(K);
+    ID.AddPointer(S);
+  }
+};
+
 class StreamChecker : public CheckerVisitor<StreamChecker> {
-  IdentifierInfo *II_fopen, *II_fread, *II_fwrite, 
+  IdentifierInfo *II_fopen, *II_fclose,*II_fread, *II_fwrite, 
                  *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,  
                  *II_clearerr, *II_feof, *II_ferror, *II_fileno;
-  BuiltinBug *BT_nullfp, *BT_illegalwhence;
+  BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose;
 
 public:
   StreamChecker() 
-    : II_fopen(0), II_fread(0), II_fwrite(0), 
+    : II_fopen(0), II_fclose(0), II_fread(0), II_fwrite(0), 
       II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0), 
       II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0), 
-      BT_nullfp(0), BT_illegalwhence(0) {}
+      BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0) {}
 
   static void *getTag() {
     static int x;
@@ -45,6 +69,7 @@
 
 private:
   void Fopen(CheckerContext &C, const CallExpr *CE);
+  void Fclose(CheckerContext &C, const CallExpr *CE);
   void Fread(CheckerContext &C, const CallExpr *CE);
   void Fwrite(CheckerContext &C, const CallExpr *CE);
   void Fseek(CheckerContext &C, const CallExpr *CE);
@@ -60,10 +85,20 @@
   // Return true indicates the stream pointer is NULL.
   const GRState *CheckNullStream(SVal SV, const GRState *state, 
                                  CheckerContext &C);
+  const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state, 
+                                 CheckerContext &C);
 };
 
 } // end anonymous namespace
 
+namespace clang {
+  template <>
+  struct GRStateTrait<StreamState> 
+    : public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, StreamState> > {
+    static void *GDMIndex() { return StreamChecker::getTag(); }
+  };
+}
+
 void clang::RegisterStreamChecker(GRExprEngine &Eng) {
   Eng.registerCheck(new StreamChecker());
 }
@@ -79,6 +114,8 @@
   ASTContext &Ctx = C.getASTContext();
   if (!II_fopen)
     II_fopen = &Ctx.Idents.get("fopen");
+  if (!II_fclose)
+    II_fclose = &Ctx.Idents.get("fclose");
   if (!II_fread)
     II_fread = &Ctx.Idents.get("fread");
   if (!II_fwrite)
@@ -106,6 +143,10 @@
     Fopen(C, CE);
     return true;
   }
+  if (FD->getIdentifier() == II_fclose) {
+    Fclose(C, CE);
+    return true;
+  }
   if (FD->getIdentifier() == II_fread) {
     Fread(C, CE);
     return true;
@@ -168,10 +209,23 @@
   const GRState *stateNotNull, *stateNull;
   llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, RetVal);
 
+  SymbolRef Sym = RetVal.getAsSymbol();
+  assert(Sym);
+
+  // if RetVal is not NULL, set the symbol's state to Opened.
+  stateNotNull = stateNotNull->set<StreamState>(Sym, StreamState::getOpened(CE));
+  stateNull = stateNull->set<StreamState>(Sym, StreamState::getOpenFailed(CE));
+
   C.addTransition(stateNotNull);
   C.addTransition(stateNull);
 }
 
+void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) {
+  const GRState *state = CheckDoubleClose(CE, C.getState(), C);
+  if (state)
+    C.addTransition(state);
+}
+
 void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) {
   const GRState *state = C.getState();
   if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C))
@@ -285,3 +339,32 @@
   }
   return stateNotNull;
 }
+
+const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE,
+					       const GRState *state,
+					       CheckerContext &C) {
+  SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol();
+  assert(Sym);
+  
+  const StreamState *SS = state->get<StreamState>(Sym);
+  assert(SS);
+  
+  // Check: Double close a File Descriptor could cause undefined behaviour.
+  // Conforming to man-pages
+  if (SS->isClosed()) {
+    ExplodedNode *N = C.GenerateSink();
+    if (N) {
+      if (!BT_doubleclose)
+	BT_doubleclose = new BuiltinBug("Double fclose",
+					"Try to close a file Descriptor already"
+					" closed. Cause undefined behaviour.");
+      BugReport *R = new BugReport(*BT_doubleclose,
+				   BT_doubleclose->getDescription(), N);
+      C.EmitReport(R);
+    }
+    return NULL;
+  }
+  
+  // Close the File Descriptor.
+  return state->set<StreamState>(Sym, StreamState::getClosed(CE));
+}

Modified: cfe/trunk/test/Analysis/stream.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/stream.c?rev=108669&r1=108668&r2=108669&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/stream.c (original)
+++ cfe/trunk/test/Analysis/stream.c Sun Jul 18 20:52:29 2010
@@ -6,6 +6,7 @@
 #define SEEK_CUR	1	/* Seek from current position.  */
 #define SEEK_END	2	/* Seek from end of file.  */
 extern FILE *fopen(const char *path, const char *mode);
+extern int fclose(FILE *fp);
 extern size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
 extern int fseek (FILE *__stream, long int __off, int __whence);
 extern long int ftell (FILE *__stream);
@@ -39,3 +40,9 @@
   fseek(p, 1, SEEK_SET); // no-warning
   fseek(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR.}}
 }
+
+void f6(void) {
+  FILE *p = fopen("foo", "r");
+  fclose(p); 
+  fclose(p); // expected-warning {{Try to close a file Descriptor already closed. Cause Undefined Behaviour.}}
+}





More information about the cfe-commits mailing list