]> granicus.if.org Git - clang/commitdiff
[analyzer] Malloc: cleanup, disallow free on relinquished memory.
authorAnna Zaks <ganna@apple.com>
Wed, 20 Jun 2012 20:57:46 +0000 (20:57 +0000)
committerAnna Zaks <ganna@apple.com>
Wed, 20 Jun 2012 20:57:46 +0000 (20:57 +0000)
This commits sets the grounds for more aggressive use after free
checking. We will use the Relinquished sate to denote that someone
else is now responsible for releasing the memory.

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

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

index 255225467f058f1f1fda05a92a1f2f46b7aafd90..48fdec2d3f9fde3f62d5429569ca03bcf774ae42 100644 (file)
@@ -34,15 +34,24 @@ using namespace ento;
 namespace {
 
 class RefState {
-  enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped,
+  enum Kind { // Reference to allocated memory.
+              Allocated,
+              // Reference to released/freed memory.
+              Released,
+              // Reference to escaped memory - no assumptions can be made of
+              // the state after the reference escapes.
+              Escaped,
+              // The responsibility for freeing resources has transfered from
+              // this reference. A relinquished symbol should not be freed.
               Relinquished } K;
   const Stmt *S;
 
 public:
   RefState(Kind k, const Stmt *s) : K(k), S(s) {}
 
-  bool isAllocated() const { return K == AllocateUnchecked; }
+  bool isAllocated() const { return K == Allocated; }
   bool isReleased() const { return K == Released; }
+  bool isRelinquished() const { return K == Relinquished; }
 
   const Stmt *getStmt() const { return S; }
 
@@ -50,11 +59,8 @@ public:
     return K == X.K && S == X.S;
   }
 
-  static RefState getAllocateUnchecked(const Stmt *s) { 
-    return RefState(AllocateUnchecked, s); 
-  }
-  static RefState getAllocateFailed() {
-    return RefState(AllocateFailed, 0);
+  static RefState getAllocated(const Stmt *s) {
+    return RefState(Allocated, s);
   }
   static RefState getReleased(const Stmt *s) { return RefState(Released, s); }
   static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); }
@@ -528,7 +534,7 @@ ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
   assert(Sym);
 
   // Set the symbol's state to Allocated.
-  return state->set<RegionState>(Sym, RefState::getAllocateUnchecked(CE));
+  return state->set<RegionState>(Sym, RefState::getAllocated(CE));
 
 }
 
@@ -628,7 +634,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
     return 0;
 
   // Check double free.
-  if (RS->isReleased()) {
+  // TODO: Split the 2 cases for better error messages.
+  if (RS->isReleased() || RS->isRelinquished()) {
     if (ExplodedNode *N = C.generateSink()) {
       if (!BT_DoubleFree)
         BT_DoubleFree.reset(
@@ -1237,7 +1244,7 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
       if (RS) {
         if (RS->isReleased() && ! I.getData().IsFreeOnFailure)
           state = state->set<RegionState>(ReallocSym,
-                             RefState::getAllocateUnchecked(RS->getStmt()));
+                             RefState::getAllocated(RS->getStmt()));
       }
       state = state->remove<ReallocPairs>(I.getKey());
     }
index a0c145279d920216de470994d38a66a2d3dfc530..15ce62d9a5eb9eec029dfd4ca23dfc6d9202fb2b 100644 (file)
@@ -123,12 +123,12 @@ void af2e() {
   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.
+// This case inflicts a possible double-free.
+// TODO: Better error message.
 void af3() {
   int *p = my_malloc(12);
   my_hold(p);
-  free(p); // no-warning
+  free(p); // expected-warning{{Attempt to free released memory}}
 }
 
 int * af4() {