]> granicus.if.org Git - clang/commitdiff
[analyzer] Add custom path diagnostic to the Malloc Checker.
authorAnna Zaks <ganna@apple.com>
Thu, 9 Feb 2012 06:25:51 +0000 (06:25 +0000)
committerAnna Zaks <ganna@apple.com>
Thu, 9 Feb 2012 06:25:51 +0000 (06:25 +0000)
Very simple so far - we just highlight every allocation and release
site.

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

lib/StaticAnalyzer/Checkers/MallocChecker.cpp
test/Analysis/malloc.c

index 8f4e805e9575f145dee4b78855b2d1147a895cd7..4a15afd986e0da4de179d310a25f6d27476ce6c4 100644 (file)
@@ -138,6 +138,40 @@ private:
   static bool SummarizeValue(raw_ostream &os, SVal V);
   static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
   void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range) const;
+
+  /// The bug visitor which allows us to print extra diagnostics along the
+  /// BugReport path. For example, showing the allocation site of the leaked
+  /// region.
+  class MallocBugVisitor : public BugReporterVisitor {
+  protected:
+    // The allocated region symbol tracked by the main analysis.
+    SymbolRef Sym;
+
+  public:
+    MallocBugVisitor(SymbolRef S) : Sym(S) {}
+    virtual ~MallocBugVisitor() {}
+
+    void Profile(llvm::FoldingSetNodeID &ID) const {
+      static int X = 0;
+      ID.AddPointer(&X);
+      ID.AddPointer(Sym);
+    }
+
+    inline bool isAllocated(const RefState *S, const RefState *SPrev) {
+      // Did not track -> allocated. Other state (released) -> allocated.
+      return ((S && S->isAllocated()) && (!SPrev || !SPrev->isAllocated()));
+    }
+
+    inline bool isReleased(const RefState *S, const RefState *SPrev) {
+      // Did not track -> released. Other state (allocated) -> released.
+      return ((S && S->isReleased()) && (!SPrev || !SPrev->isReleased()));
+    }
+
+    PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
+                                   const ExplodedNode *PrevN,
+                                   BugReporterContext &BRC,
+                                   BugReport &BR);
+  };
 };
 } // end anonymous namespace
 
@@ -388,9 +422,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
         BT_DoubleFree.reset(
           new BuiltinBug("Double free",
                          "Try to free a memory block that has been released"));
-      // FIXME: should find where it's freed last time.
       BugReport *R = new BugReport(*BT_DoubleFree, 
                                    BT_DoubleFree->getDescription(), N);
+      R->addVisitor(new MallocBugVisitor(Sym));
       C.EmitReport(R);
     }
     return 0;
@@ -633,12 +667,14 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
 
   // FIXME: This does not handle when we have multiple leaks at a single
   // place.
+  // TODO: We don't have symbol info in the diagnostics here!
   if (N && generateReport) {
     if (!BT_Leak)
       BT_Leak.reset(new BuiltinBug("Memory leak",
               "Allocated memory never released. Potential memory leak."));
     // FIXME: where it is allocated.
     BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
+    //Report->addVisitor(new MallocBugVisitor(Sym));
     C.EmitReport(R);
   }
 }
@@ -656,6 +692,7 @@ void MallocChecker::checkEndPath(CheckerContext &Ctx) const {
           BT_Leak.reset(new BuiltinBug("Memory leak",
                     "Allocated memory never released. Potential memory leak."));
         BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N);
+        R->addVisitor(new MallocBugVisitor(I->first));
         Ctx.EmitReport(R);
       }
     }
@@ -718,6 +755,7 @@ bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
       BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(),N);
       if (S)
         R->addRange(S->getSourceRange());
+      R->addVisitor(new MallocBugVisitor(Sym));
       C.EmitReport(R);
       return true;
     }
@@ -792,6 +830,44 @@ void MallocChecker::checkBind(SVal location, SVal val,
   }
 }
 
+PathDiagnosticPiece *
+MallocChecker::MallocBugVisitor::VisitNode(const ExplodedNode *N,
+                                           const ExplodedNode *PrevN,
+                                           BugReporterContext &BRC,
+                                           BugReport &BR) {
+  const RefState *RS = N->getState()->get<RegionState>(Sym);
+  const RefState *RSPrev = PrevN->getState()->get<RegionState>(Sym);
+  if (!RS && !RSPrev)
+    return 0;
+
+  // We expect the interesting locations be StmtPoints corresponding to call
+  // expressions. We do not support indirect function calls as of now.
+  const CallExpr *CE = 0;
+  if (isa<StmtPoint>(N->getLocation()))
+    CE = dyn_cast<CallExpr>(cast<StmtPoint>(N->getLocation()).getStmt());
+  if (!CE)
+    return 0;
+  const FunctionDecl *funDecl = CE->getDirectCallee();
+  if (!funDecl)
+    return 0;
+  StringRef funName = funDecl->getName();
+
+  // Find out if this is an interesting point and what is the kind.
+  const char *Msg = 0;
+  if (isAllocated(RS, RSPrev))
+    Msg = "Memory is allocated here";
+  else if (isReleased(RS, RSPrev))
+    Msg = "Memory is released here";
+  if (!Msg)
+    return 0;
+
+  // Generate the extra diagnostic.
+  PathDiagnosticLocation Pos(CE, BRC.getSourceManager(),
+                             N->getLocationContext());
+  return new PathDiagnosticEventPiece(Pos, Msg);
+}
+
+
 #define REGISTER_CHECKER(name) \
 void ento::register##name(CheckerManager &mgr) {\
   mgr.registerChecker<MallocChecker>()->Filter.C##name = true;\
index 190a2548d77c54bbdcc58aaf012bf7a4bf8c6e98..7ffc9a1ec00166864178edcf098422fa22f8dfe3 100644 (file)
@@ -240,6 +240,14 @@ void mallocFreeUse_params() {
   myfooint(*p); //expected-warning{{Use dynamically allocated memory after it is freed}}
 }
 
+void mallocFailedOrNot() {
+  int *p = malloc(12);
+  if (!p)
+    free(p);
+  else
+    free(p);
+}
+
 int *Gl;
 struct GlStTy {
   int *x;