danielmarjamaki created this revision.

I saw a false positive where the analyzer made wrong conclusions about a static 
variable.

Static variables that are not written have known values (initialized values).

This is the (simplified) code that motivated me to create this patch:

  static char *allv[] = {
        "rpcgen", "-s", "udp", "-s", "tcp",
  
  };
  static int allc = sizeof(allv) / sizeof(allv[0]);
  
  static void f(void) {
        int i;
  
        for (i = 1; i < allc; i++) {
                const char *p = allv[i];  // <- line 28
                i++;
        }
  }

Clang output:

  array-fp3.c:28:19: warning: Access out-of-bound array element (buffer 
overflow)
                  const char *p = allv[i];
                                  ^~~~~~~

I added testcases that shows this patch solves both false positives and false 
negatives


Repository:
  rL LLVM

https://reviews.llvm.org/D37897

Files:
  include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/global-vars.c

Index: test/Analysis/global-vars.c
===================================================================
--- test/Analysis/global-vars.c
+++ test/Analysis/global-vars.c
@@ -0,0 +1,18 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha,core -verify %s
+
+// Avoid false positive.
+static char *allv[] = {"rpcgen", "-s", "udp", "-s", "tcp"};
+static int allc = sizeof(allv) / sizeof(allv[0]);
+static void falsePositive1(void) {
+  int i;
+  for (i = 1; i < allc; i++) {
+    const char *p = allv[i]; // no-warning
+    i++;
+  }
+}
+
+// Detect division by zero.
+static int zero = 0;
+void truePositive1() {
+  int x = 1000 / zero; // expected-warning{{Division by zero}}
+}
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -103,8 +103,106 @@
 // Utility methods.
 //===----------------------------------------------------------------------===//
 
+static bool isChanged(const Stmt *S, const VarDecl *VD, bool W) {
+  if (!S)
+    return false;
+  if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S)) {
+    if (B->isAssignmentOp())
+      return isChanged(B->getLHS(), VD, true) || isChanged(B->getRHS(), VD, W);
+  } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
+    if (U->getOpcode() == UO_AddrOf || U->getOpcode() == UO_PreInc ||
+        U->getOpcode() == UO_PreDec || U->getOpcode() == UO_PostInc ||
+        U->getOpcode() == UO_PostDec)
+      return isChanged(U->getSubExpr(), VD, true);
+  } else if (const DeclRefExpr *D = dyn_cast<DeclRefExpr>(S)) {
+    return W && D->getDecl() == VD;
+  }
+
+  for (const Stmt *Child : S->children()) {
+    if (isChanged(Child, VD, W))
+      return true;
+  }
+  return false;
+}
+
+static bool isChanged(const Decl *D, const VarDecl *VD) {
+  if (isa<FunctionDecl>(D))
+    return isChanged(D->getBody(), VD, false);
+  if (const auto *Rec = dyn_cast<TagDecl>(D)) {
+    for (const auto *RecChild : Rec->decls()) {
+      if (isChanged(RecChild, VD))
+        return true;
+    }
+  }
+  return false;
+}
+
+ProgramStateRef ExprEngine::getInitialState(const LocationContext *LCtx,
+                                            ProgramStateRef State,
+                                            const VarDecl *VD) {
+  // Is variable used in location context?
+  const FunctionDecl *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
+  if (!FD)
+    return State;
+
+  // Is variable changed anywhere in TU?
+  for (const Decl *D : AMgr.getASTContext().getTranslationUnitDecl()->decls()) {
+    if (isChanged(D, VD))
+      return State;
+  }
+
+  // What is the initialized value?
+  llvm::APSInt InitVal;
+  if (const Expr *I = VD->getInit()) {
+    if (!I->EvaluateAsInt(InitVal, getContext()))
+      return State;
+  } else {
+    InitVal = 0;
+  }
+
+  const MemRegion *R = State->getRegion(VD, LCtx);
+  if (!R)
+    return State;
+  SVal V = State->getSVal(loc::MemRegionVal(R));
+  SVal Constraint_untested =
+      evalBinOp(State, BO_EQ, V, svalBuilder.makeIntVal(InitVal),
+                svalBuilder.getConditionType());
+  Optional<DefinedOrUnknownSVal> Constraint =
+      Constraint_untested.getAs<DefinedOrUnknownSVal>();
+  if (!Constraint)
+    return State;
+  return State->assume(*Constraint, true);
+}
+
+static void getStaticVars(const Stmt *S,
+                          llvm::SmallSet<const VarDecl *, 4> *Vars) {
+  if (!S)
+    return;
+  if (const DeclRefExpr *D = dyn_cast<DeclRefExpr>(S)) {
+    const VarDecl *VD = dyn_cast<VarDecl>(D->getDecl());
+    if (VD && VD->isDefinedOutsideFunctionOrMethod() &&
+        VD->getType()->isIntegerType() && VD->getStorageClass() == SC_Static &&
+        !VD->getType()->isPointerType()) {
+      Vars->insert(VD);
+    }
+  }
+  for (const Stmt *Child : S->children()) {
+    getStaticVars(Child, Vars);
+  }
+}
+
 ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
   ProgramStateRef state = StateMgr.getInitialState(InitLoc);
+
+  // Get initial states for static global variables.
+  if (const auto *FD = dyn_cast<FunctionDecl>(InitLoc->getDecl())) {
+    llvm::SmallSet<const VarDecl *, 4> Vars;
+    getStaticVars(FD->getBody(), &Vars);
+    for (const VarDecl *VD : Vars) {
+      state = getInitialState(InitLoc, state, VD);
+    }
+  }
+
   const Decl *D = InitLoc->getDecl();
 
   // Preconditions.
Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
===================================================================
--- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -575,15 +575,17 @@
   /// \brief Default implementation of call evaluation.
   void defaultEvalCall(NodeBuilder &B, ExplodedNode *Pred,
                        const CallEvent &Call);
+
 private:
+  ProgramStateRef getInitialState(const LocationContext *LCtx,
+                                  const ProgramStateRef State,
+                                  const VarDecl *VD);
+
   void evalLoadCommon(ExplodedNodeSet &Dst,
-                      const Expr *NodeEx,  /* Eventually will be a CFGStmt */
-                      const Expr *BoundEx,
-                      ExplodedNode *Pred,
-                      ProgramStateRef St,
-                      SVal location,
-                      const ProgramPointTag *tag,
-                      QualType LoadTy);
+                      const Expr *NodeEx, /* Eventually will be a CFGStmt */
+                      const Expr *BoundEx, ExplodedNode *Pred,
+                      ProgramStateRef St, SVal location,
+                      const ProgramPointTag *tag, QualType LoadTy);
 
   // FIXME: 'tag' should be removed, and a LocationContext should be used
   // instead.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to