]> granicus.if.org Git - clang/commitdiff
Add StreamChecker. This checker models and checks stream manipulation functions.
authorZhongxing Xu <xuzhongxing@gmail.com>
Wed, 16 Jun 2010 05:38:05 +0000 (05:38 +0000)
committerZhongxing Xu <xuzhongxing@gmail.com>
Wed, 16 Jun 2010 05:38:05 +0000 (05:38 +0000)
This is the start.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106082 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Checker/PathSensitive/Checker.h
lib/Checker/CMakeLists.txt
lib/Checker/GRExprEngineExperimentalChecks.cpp
lib/Checker/GRExprEngineExperimentalChecks.h
lib/Checker/StreamChecker.cpp [new file with mode: 0644]
test/Analysis/stream.c [new file with mode: 0644]

index 8cb9cc8337c6aafbefbf3d876bde213372af65c0..dcb4ca62b83c58f8f4ac14e312f53b711cbc6d8d 100644 (file)
@@ -144,6 +144,7 @@ public:
     // If the 'state' is not new, we need to check if the cached state 'ST'
     // is new.
     if (state != getState() || (ST && ST != B.GetState(Pred)))
+      // state is new or equals to ST.
       GenerateNode(state, true);
     else
       Dst.Add(Pred);
index 620dab2e55c1e47aab6e6b67573ee6810e8fffa6..cd11a2043f0f6f32977d4f0c15bfd7bdd69677a8 100644 (file)
@@ -62,6 +62,7 @@ add_clang_library(clangChecker
   SimpleSValuator.cpp
   StackAddrLeakChecker.cpp
   Store.cpp
+  StreamChecker.cpp
   SVals.cpp
   SValuator.cpp
   SymbolManager.cpp
index acacc8eade62081d4c9a946cc8829abbd800471e..1247a659c772111716101db40b9f9d966634d6ef 100644 (file)
@@ -23,6 +23,7 @@ void clang::RegisterExperimentalChecks(GRExprEngine &Eng) {
   // within GRExprEngine.
   RegisterPthreadLockChecker(Eng);  
   RegisterMallocChecker(Eng);
+  RegisterStreamChecker(Eng);
 }
 
 void clang::RegisterExperimentalInternalChecks(GRExprEngine &Eng) {
index 9a9da32e556e6951a14754c03f1671db7ea2db85..fb867a98f07fa674e9f7e34e4d8b9b625c21abba 100644 (file)
@@ -21,6 +21,7 @@ class GRExprEngine;
 
 void RegisterPthreadLockChecker(GRExprEngine &Eng);
 void RegisterMallocChecker(GRExprEngine &Eng);
+void RegisterStreamChecker(GRExprEngine &Eng);
 
 } // end clang namespace
 #endif
diff --git a/lib/Checker/StreamChecker.cpp b/lib/Checker/StreamChecker.cpp
new file mode 100644 (file)
index 0000000..80c29a1
--- /dev/null
@@ -0,0 +1,116 @@
+//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines checkers that model and check stream handling functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GRExprEngineExperimentalChecks.h"
+#include "clang/Checker/BugReporter/BugType.h"
+#include "clang/Checker/PathSensitive/CheckerVisitor.h"
+#include "clang/Checker/PathSensitive/GRState.h"
+#include "clang/Checker/PathSensitive/GRStateTrait.h"
+#include "clang/Checker/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/ImmutableMap.h"
+
+using namespace clang;
+
+namespace {
+
+class StreamChecker : public CheckerVisitor<StreamChecker> {
+  IdentifierInfo *II_fopen, *II_fread;
+  BuiltinBug *BT_nullfp;
+
+public:
+  StreamChecker() : II_fopen(0), II_fread(0), BT_nullfp(0) {}
+
+  static void *getTag() {
+    static int x;
+    return &x;
+  }
+
+  virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
+
+private:
+  void FOpen(CheckerContext &C, const CallExpr *CE);
+  void FRead(CheckerContext &C, const CallExpr *CE);
+};
+
+}
+
+void clang::RegisterStreamChecker(GRExprEngine &Eng) {
+  Eng.registerCheck(new StreamChecker());
+}
+
+bool StreamChecker::EvalCallExpr(CheckerContext &C, const CallExpr *CE) {
+  const GRState *state = C.getState();
+  const Expr *Callee = CE->getCallee();
+  SVal L = state->getSVal(Callee);
+  const FunctionDecl *FD = L.getAsFunctionDecl();
+  if (!FD)
+    return false;
+
+  ASTContext &Ctx = C.getASTContext();
+  if (!II_fopen)
+    II_fopen = &Ctx.Idents.get("fopen");
+
+  if (!II_fread)
+    II_fread = &Ctx.Idents.get("fread");
+
+  if (FD->getIdentifier() == II_fopen) {
+    FOpen(C, CE);
+    return true;
+  }
+
+  if (FD->getIdentifier() == II_fread) {
+    FRead(C, CE);
+    return true;
+  }
+
+  return false;
+}
+
+void StreamChecker::FOpen(CheckerContext &C, const CallExpr *CE) {
+  const GRState *state = C.getState();
+  unsigned Count = C.getNodeBuilder().getCurrentBlockCount();
+  ValueManager &ValMgr = C.getValueManager();
+  SVal RetVal = ValMgr.getConjuredSymbolVal(0, CE, Count);
+  state = state->BindExpr(CE, RetVal);
+
+  ConstraintManager &CM = C.getConstraintManager();
+  // Bifurcate the state into two: one with a valid FILE* pointer, the other
+  // with a NULL.
+  const GRState *stateNotNull, *stateNull;
+  llvm::tie(stateNotNull, stateNull) 
+    = CM.AssumeDual(state, cast<DefinedSVal>(RetVal));
+
+  C.addTransition(stateNotNull);
+  C.addTransition(stateNull);
+}
+
+void StreamChecker::FRead(CheckerContext &C, const CallExpr *CE) {
+  const GRState *state = C.getState();
+
+  // Assume CallAndMessageChecker has been run.
+  const DefinedSVal &StreamVal=cast<DefinedSVal>(state->getSVal(CE->getArg(3)));
+
+  ConstraintManager &CM = C.getConstraintManager();
+  const GRState *stateNotNull, *stateNull;
+  llvm::tie(stateNotNull, stateNull) = CM.AssumeDual(state, StreamVal);
+
+  if (!stateNotNull && stateNull) {
+    if (ExplodedNode *N = C.GenerateSink(stateNull)) {
+      if (!BT_nullfp)
+        BT_nullfp = new BuiltinBug("NULL stream pointer",
+                                   "Stream pointer might be NULL.");
+      BugReport *R = new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N);
+      C.EmitReport(R);
+    }
+  }
+}
diff --git a/test/Analysis/stream.c b/test/Analysis/stream.c
new file mode 100644 (file)
index 0000000..1787668
--- /dev/null
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-experimental-checks -analyzer-store region -verify %s
+
+typedef __typeof__(sizeof(int)) size_t;
+typedef struct _IO_FILE FILE;
+FILE *fopen(const char *path, const char *mode);
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
+
+void f1(void) {
+  FILE *p = fopen("foo", "r");
+  char buf[1024];
+  fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL.}}
+}