[cfe-commits] r150112 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/MallocChecker.cpp test/Analysis/malloc.c

Anna Zaks ganna at apple.com
Wed Feb 8 15:16:57 PST 2012


Author: zaks
Date: Wed Feb  8 17:16:56 2012
New Revision: 150112

URL: http://llvm.org/viewvc/llvm-project?rev=150112&view=rev
Log:
[analyzer] MallocChecker: implement pessimistic version of the checker,
which allows values to escape through unknown calls.

Assumes all calls but the malloc family are unknown.

Also, catch a use-after-free when a pointer is passed to a
function after a call to free (previously, you had to explicitly
dereference the pointer value).

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
    cfe/trunk/test/Analysis/malloc.c

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp?rev=150112&r1=150111&r2=150112&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Wed Feb  8 17:16:56 2012
@@ -131,6 +131,10 @@
   void ReallocMem(CheckerContext &C, const CallExpr *CE) const;
   static void CallocMem(CheckerContext &C, const CallExpr *CE);
   
+  bool checkEscape(SymbolRef Sym, const Stmt *S, CheckerContext &C) const;
+  bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
+                         const Stmt *S = 0) const;
+
   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;
@@ -186,6 +190,7 @@
     return;
   }
 
+  if (Filter.CMallocOptimistic)
   // Check all the attributes, if there are any.
   // There can be multiple of these attributes.
   if (FD->hasAttrs()) {
@@ -206,6 +211,22 @@
       }
     }
   }
+
+  if (Filter.CMallocPessimistic) {
+    ProgramStateRef State = C.getState();
+    // The pointer might escape through a function call.
+    for (CallExpr::const_arg_iterator I = CE->arg_begin(),
+                                      E = CE->arg_end(); I != E; ++I) {
+      const Expr *A = *I;
+      if (A->getType().getTypePtr()->isAnyPointerType()) {
+        SymbolRef Sym = State->getSVal(A, C.getLocationContext()).getAsSymbol();
+        if (!Sym)
+          return;
+        checkEscape(Sym, A, C);
+        checkUseAfterFree(Sym, C, A);
+      }
+    }
+  }
 }
 
 void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) {
@@ -642,32 +663,36 @@
   }
 }
 
-void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
-  const Expr *retExpr = S->getRetValue();
-  if (!retExpr)
-    return;
-
+bool MallocChecker::checkEscape(SymbolRef Sym, const Stmt *S,
+                                CheckerContext &C) const {
   ProgramStateRef state = C.getState();
-
-  SymbolRef Sym = state->getSVal(retExpr, C.getLocationContext()).getAsSymbol();
-  if (!Sym)
-    return;
-
   const RefState *RS = state->get<RegionState>(Sym);
   if (!RS)
-    return;
+    return false;
 
-  // FIXME: check other cases.
-  if (RS->isAllocated())
+  if (RS->isAllocated()) {
     state = state->set<RegionState>(Sym, RefState::getEscaped(S));
+    C.addTransition(state);
+    return true;
+  }
+  return false;
+}
 
-  C.addTransition(state);
+void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
+  const Expr *E = S->getRetValue();
+  if (!E)
+    return;
+  SymbolRef Sym = C.getState()->getSVal(E, C.getLocationContext()).getAsSymbol();
+  if (!Sym)
+    return;
+
+  checkEscape(Sym, S, C);
 }
 
 ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
                                               SVal Cond, 
                                               bool Assumption) const {
-  // If a symblic region is assumed to NULL, set its state to AllocateFailed.
+  // If a symbolic region is assumed to NULL, set its state to AllocateFailed.
   // FIXME: should also check symbols assumed to non-null.
 
   RegionStateTy RS = state->get<RegionState>();
@@ -681,24 +706,32 @@
   return state;
 }
 
+bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
+                                      const Stmt *S) const {
+  assert(Sym);
+  const RefState *RS = C.getState()->get<RegionState>(Sym);
+  if (RS && RS->isReleased()) {
+    if (ExplodedNode *N = C.addTransition()) {
+      if (!BT_UseFree)
+        BT_UseFree.reset(new BuiltinBug("Use dynamically allocated memory "
+            "after it is freed."));
+
+      BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(),N);
+      if (S)
+        R->addRange(S->getSourceRange());
+      C.EmitReport(R);
+      return true;
+    }
+  }
+  return false;
+}
+
 // Check if the location is a freed symbolic region.
 void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
                                   CheckerContext &C) const {
   SymbolRef Sym = l.getLocSymbolInBase();
-  if (Sym) {
-    const RefState *RS = C.getState()->get<RegionState>(Sym);
-    if (RS && RS->isReleased()) {
-      if (ExplodedNode *N = C.addTransition()) {
-        if (!BT_UseFree)
-          BT_UseFree.reset(new BuiltinBug("Use dynamically allocated memory "
-                                          "after it is freed."));
-
-        BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(),
-                                     N);
-        C.EmitReport(R);
-      }
-    }
-  }
+  if (Sym)
+    checkUseAfterFree(Sym, C);
 }
 
 void MallocChecker::checkBind(SVal location, SVal val,

Modified: cfe/trunk/test/Analysis/malloc.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/malloc.c?rev=150112&r1=150111&r2=150112&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/malloc.c (original)
+++ cfe/trunk/test/Analysis/malloc.c Wed Feb  8 17:16:56 2012
@@ -4,23 +4,9 @@
 void free(void *);
 void *realloc(void *ptr, size_t size);
 void *calloc(size_t nmemb, size_t size);
-void __attribute((ownership_returns(malloc))) *my_malloc(size_t);
-void __attribute((ownership_takes(malloc, 1))) my_free(void *);
-void __attribute((ownership_returns(malloc, 1))) *my_malloc2(size_t);
-void __attribute((ownership_holds(malloc, 1))) my_hold(void *);
-
-// Duplicate attributes are silly, but not an error.
-// Duplicate attribute has no extra effect.
-// If two are of different kinds, that is an error and reported as such. 
-void __attribute((ownership_holds(malloc, 1)))
-__attribute((ownership_holds(malloc, 1)))
-__attribute((ownership_holds(malloc, 3))) my_hold2(void *, void *, void *);
-void *my_malloc3(size_t);
-void *myglobalpointer;
-struct stuff {
-  void *somefield;
-};
-struct stuff myglobalstuff;
+
+void myfoo(int *p);
+void myfooint(int p);
 
 void f1() {
   int *p = malloc(12);
@@ -44,107 +30,6 @@
   int *q = realloc(p,0); // no-warning
 }
 
-// ownership attributes tests
-void naf1() {
-  int *p = my_malloc3(12);
-  return; // no-warning
-}
-
-void n2af1() {
-  int *p = my_malloc2(12);
-  return; // expected-warning{{Allocated memory never released. Potential memory leak.}}
-}
-
-void af1() {
-  int *p = my_malloc(12);
-  return; // expected-warning{{Allocated memory never released. Potential memory leak.}}
-}
-
-void af1_b() {
-  int *p = my_malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak.}}
-}
-
-void af1_c() {
-  myglobalpointer = my_malloc(12); // no-warning
-}
-
-void af1_d() {
-  struct stuff mystuff;
-  mystuff.somefield = my_malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak.}}
-}
-
-// Test that we can pass out allocated memory via pointer-to-pointer.
-void af1_e(void **pp) {
-  *pp = my_malloc(42); // no-warning
-}
-
-void af1_f(struct stuff *somestuff) {
-  somestuff->somefield = my_malloc(12); // no-warning
-}
-
-// Allocating memory for a field via multiple indirections to our arguments is OK.
-void af1_g(struct stuff **pps) {
-  *pps = my_malloc(sizeof(struct stuff)); // no-warning
-  (*pps)->somefield = my_malloc(42); // no-warning
-}
-
-void af2() {
-  int *p = my_malloc(12);
-  my_free(p);
-  free(p); // expected-warning{{Try to free a memory block that has been released}}
-}
-
-void af2b() {
-  int *p = my_malloc(12);
-  free(p);
-  my_free(p); // expected-warning{{Try to free a memory block that has been released}}
-}
-
-void af2c() {
-  int *p = my_malloc(12);
-  free(p);
-  my_hold(p); // expected-warning{{Try to free a memory block that has been released}}
-}
-
-void af2d() {
-  int *p = my_malloc(12);
-  free(p);
-  my_hold2(0, 0, p); // expected-warning{{Try to free a memory block that has been released}}
-}
-
-// No leak if malloc returns null.
-void af2e() {
-  int *p = my_malloc(12);
-  if (!p)
-    return; // no-warning
-  free(p); // no-warning
-}
-
-// This case would inflict a double-free elsewhere.
-// However, this case is considered an analyzer bug since it causes false-positives.
-void af3() {
-  int *p = my_malloc(12);
-  my_hold(p);
-  free(p); // no-warning
-}
-
-// This case would inflict a double-free elsewhere.
-// However, this case is considered an analyzer bug since it causes false-positives.
-int * af4() {
-  int *p = my_malloc(12);
-  my_free(p);
-  return p; // no-warning
-}
-
-// This case is (possibly) ok, be conservative
-int * af5() {
-  int *p = my_malloc(12);
-  my_hold(p);
-  return p; // no-warning
-}
-
-
-
 // This case tests that storing malloc'ed memory to a static variable which is
 // then returned is not leaked.  In the absence of known contracts for functions
 // or inter-procedural analysis, this is a conservative answer.
@@ -261,3 +146,97 @@
 	}
 	return result; // expected-warning{{never released}}
 }
+
+void nullFree() {
+  int *p = 0;
+  free(p); // no warning - a nop
+}
+
+void paramFree(int *p) {
+  myfoo(p);
+  free(p); // no warning
+  myfoo(p); // TODO: This should be a warning.
+}
+
+int* mallocEscapeRet() {
+  int *p = malloc(12);
+  return p; // no warning
+}
+
+void mallocEscapeFoo() {
+  int *p = malloc(12);
+  myfoo(p);
+  return; // no warning
+}
+
+void mallocEscapeFree() {
+  int *p = malloc(12);
+  myfoo(p);
+  free(p);
+}
+
+void mallocEscapeFreeFree() {
+  int *p = malloc(12);
+  myfoo(p);
+  free(p);
+  free(p); // expected-warning{{Try to free a memory block that has been released}}
+}
+
+void mallocEscapeFreeUse() {
+  int *p = malloc(12);
+  myfoo(p);
+  free(p);
+  myfoo(p); // expected-warning{{Use dynamically allocated memory after it is freed.}}
+}
+
+int *myalloc();
+void myalloc2(int **p);
+
+void mallocEscapeFreeCustomAlloc() {
+  int *p = malloc(12);
+  myfoo(p);
+  free(p);
+  p = myalloc();
+  free(p); // no warning
+}
+
+void mallocEscapeFreeCustomAlloc2() {
+  int *p = malloc(12);
+  myfoo(p);
+  free(p);
+  myalloc2(&p);
+  free(p); // no warning
+}
+
+void mallocBindFreeUse() {
+  int *x = malloc(12);
+  int *y = x;
+  free(y);
+  myfoo(x); // expected-warning{{Use dynamically allocated memory after it is freed.}}
+}
+
+void mallocEscapeMalloc() {
+  int *p = malloc(12);
+  myfoo(p);
+  p = malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak.}}
+}
+
+void mallocMalloc() {
+  int *p = malloc(12);
+  p = malloc(12); // expected-warning{{Allocated memory never released. Potential memory leak}}
+}
+
+void mallocFreeMalloc() {
+  int *p = malloc(12);
+  free(p);
+  p = malloc(12);
+  free(p);
+}
+
+void MallocFreeUse_params() {
+  int *p = malloc(12);
+  free(p);
+  myfoo(p); //expected-warning{{Use dynamically allocated memory after it is freed}}
+  myfooint(*p); //expected-warning{{Use dynamically allocated memory after it is freed}}
+}
+





More information about the cfe-commits mailing list