r178250 - [analyzer] These implements unix.MismatchedDeallocatorChecker checker.

Anton Yartsev anton.yartsev at gmail.com
Thu Mar 28 10:05:19 PDT 2013


Author: ayartsev
Date: Thu Mar 28 12:05:19 2013
New Revision: 178250

URL: http://llvm.org/viewvc/llvm-project?rev=178250&view=rev
Log:
[analyzer] These implements unix.MismatchedDeallocatorChecker checker.
+ Improved display names for allocators and deallocators

The checker checks if a deallocation function matches allocation one. ('free' for 'malloc', 'delete' for 'new' etc.)

Added:
    cfe/trunk/test/Analysis/alloc-match-dealloc.mm
Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td
    cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
    cfe/trunk/test/Analysis/NewDelete-checker-test.mm
    cfe/trunk/test/Analysis/free.c
    cfe/trunk/test/Analysis/malloc.mm

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td?rev=178250&r1=178249&r2=178250&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/Checkers.td Thu Mar 28 12:05:19 2013
@@ -290,6 +290,10 @@ def MallocPessimistic : Checker<"Malloc"
 def MallocSizeofChecker : Checker<"MallocSizeof">,
   HelpText<"Check for dubious malloc arguments involving sizeof">,
   DescFile<"MallocSizeofChecker.cpp">;
+
+def MismatchedDeallocatorChecker : Checker<"MismatchedDeallocator">,
+  HelpText<"Check for mismatched deallocators.">,
+  DescFile<"MallocChecker.cpp">;
   
 } // end "unix"
 
@@ -531,4 +535,3 @@ def ExprInspectionChecker : Checker<"Exp
   DescFile<"ExprInspectionChecker.cpp">;
 
 } // end "debug"
-

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp?rev=178250&r1=178249&r2=178250&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Thu Mar 28 12:05:19 2013
@@ -35,6 +35,14 @@ using namespace ento;
 
 namespace {
 
+// Used to check correspondence between allocators and deallocators.
+enum AllocationFamily {
+  AF_None,
+  AF_Malloc,
+  AF_CXXNew,
+  AF_CXXNewArray
+};
+
 class RefState {
   enum Kind { // Reference to allocated memory.
               Allocated,
@@ -42,33 +50,42 @@ class RefState {
               Released,
               // The responsibility for freeing resources has transfered from
               // this reference. A relinquished symbol should not be freed.
-              Relinquished } K;
+              Relinquished };
+
   const Stmt *S;
+  unsigned K : 2; // Kind enum, but stored as a bitfield.
+  unsigned Family : 30; // Rest of 32-bit word, currently just an allocation 
+                        // family.
 
+  RefState(Kind k, const Stmt *s, unsigned family) 
+    : K(k), S(s), Family(family) {}
 public:
-  RefState(Kind k, const Stmt *s) : K(k), S(s) {}
-
   bool isAllocated() const { return K == Allocated; }
   bool isReleased() const { return K == Released; }
   bool isRelinquished() const { return K == Relinquished; }
-
+  AllocationFamily getAllocationFamily() const { 
+    return (AllocationFamily)Family;
+  }
   const Stmt *getStmt() const { return S; }
 
   bool operator==(const RefState &X) const {
-    return K == X.K && S == X.S;
+    return K == X.K && S == X.S && Family == X.Family;
   }
 
-  static RefState getAllocated(const Stmt *s) {
-    return RefState(Allocated, s);
+  static RefState getAllocated(unsigned family, const Stmt *s) {
+    return RefState(Allocated, s, family);
   }
-  static RefState getReleased(const Stmt *s) { return RefState(Released, s); }
-  static RefState getRelinquished(const Stmt *s) {
-    return RefState(Relinquished, s);
+  static RefState getReleased(unsigned family, const Stmt *s) { 
+    return RefState(Released, s, family);
+  }
+  static RefState getRelinquished(unsigned family, const Stmt *s) {
+    return RefState(Relinquished, s, family);
   }
 
   void Profile(llvm::FoldingSetNodeID &ID) const {
     ID.AddInteger(K);
     ID.AddPointer(S);
+    ID.AddInteger(Family);
   }
 
   void dump(raw_ostream &OS) const {
@@ -131,6 +148,7 @@ class MallocChecker : public Checker<che
   mutable OwningPtr<BugType> BT_Leak;
   mutable OwningPtr<BugType> BT_UseFree;
   mutable OwningPtr<BugType> BT_BadFree;
+  mutable OwningPtr<BugType> BT_BadDealloc;
   mutable OwningPtr<BugType> BT_OffsetFree;
   mutable IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc,
                          *II_valloc, *II_reallocf, *II_strndup, *II_strdup;
@@ -145,6 +163,7 @@ public:
     DefaultBool CMallocPessimistic;
     DefaultBool CMallocOptimistic;
     DefaultBool CNewDeleteChecker;
+    DefaultBool CMismatchedDeallocatorChecker;
   };
 
   ChecksFilter Filter;
@@ -173,6 +192,23 @@ public:
 private:
   void initIdentifierInfo(ASTContext &C) const;
 
+  /// \brief Determine family of a deallocation expression.
+  AllocationFamily getAllocationFamily(CheckerContext &C, const Expr *E) const;
+
+  /// \brief Print names of allocators and deallocators.
+  ///
+  /// \returns true on success.
+  bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, 
+                             const Expr *E) const;
+
+  /// \brief Print expected name of an allocator based on the deallocator's
+  /// family derived from the DeallocExpr.
+  void printExpectedAllocName(raw_ostream &os, CheckerContext &C, 
+                              const Expr *DeallocExpr) const;
+  /// \brief Print expected name of a deallocator based on the allocator's 
+  /// family.
+  void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const;
+
   ///@{
   /// Check if this is one of the functions which can allocate/reallocate memory 
   /// pointed to by one of its arguments.
@@ -186,20 +222,22 @@ private:
                                               const OwnershipAttr* Att);
   static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
                                      const Expr *SizeEx, SVal Init,
-                                     ProgramStateRef state) {
+                                     ProgramStateRef State,
+                                     AllocationFamily Family = AF_Malloc) {
     return MallocMemAux(C, CE,
-                        state->getSVal(SizeEx, C.getLocationContext()),
-                        Init, state);
+                        State->getSVal(SizeEx, C.getLocationContext()),
+                        Init, State, Family);
   }
 
   static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
                                      SVal SizeEx, SVal Init,
-                                     ProgramStateRef state);
+                                     ProgramStateRef State,
+                                     AllocationFamily Family = AF_Malloc);
 
   /// Update the RefState to reflect the new memory allocation.
-  static ProgramStateRef MallocUpdateRefState(CheckerContext &C,
-                                              const Expr *E,
-                                              ProgramStateRef state);
+  static ProgramStateRef 
+  MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
+                       AllocationFamily Family = AF_Malloc);
 
   ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
                               const OwnershipAttr* Att) const;
@@ -234,10 +272,13 @@ 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;
+  void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, 
+                     const Expr *DeallocExpr) const;
   void ReportBadDealloc(CheckerContext &C, SourceRange Range,
                         const Expr *DeallocExpr, const RefState *RS) const;
-  void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range)const;
+  void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, 
+                        const Expr *DeallocExpr, 
+                        const Expr *AllocExpr = 0) const;
   void ReportUseAfterFree(CheckerContext &C, SourceRange Range,
                           SymbolRef Sym) const;
   void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released,
@@ -505,7 +546,8 @@ void MallocChecker::checkPostStmt(const
     initIdentifierInfo(C.getASTContext());
     IdentifierInfo *FunI = FD->getIdentifier();
 
-    if (Filter.CMallocOptimistic || Filter.CMallocPessimistic) {
+    if (Filter.CMallocOptimistic || Filter.CMallocPessimistic ||
+        Filter.CMismatchedDeallocatorChecker) {
       if (FunI == II_malloc || FunI == II_valloc) {
         if (CE->getNumArgs() < 1)
           return;
@@ -525,7 +567,7 @@ void MallocChecker::checkPostStmt(const
       }
     }
 
-    if (Filter.CNewDeleteChecker) {
+    if (Filter.CNewDeleteChecker || Filter.CMismatchedDeallocatorChecker) {
       if (isStandardNewDelete(FD, C.getASTContext())) {
         // Process direct calls to operator new/new[]/delete/delete[] functions
         // as distinct from new/new[]/delete/delete[] expressions that are 
@@ -533,9 +575,11 @@ void MallocChecker::checkPostStmt(const
         // CXXDeleteExpr.
         OverloadedOperatorKind K = FD->getOverloadedOperator();
         if (K == OO_New)
-          State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+          State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+                               AF_CXXNew);
         else if (K == OO_Array_New)
-          State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+          State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+                               AF_CXXNewArray);
         else if (K == OO_Delete || K == OO_Array_Delete)
           State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
         else
@@ -544,7 +588,7 @@ void MallocChecker::checkPostStmt(const
     }
   }
 
-  if (Filter.CMallocOptimistic) {
+  if (Filter.CMallocOptimistic || Filter.CMismatchedDeallocatorChecker) {
     // Check all the attributes, if there are any.
     // There can be multiple of these attributes.
     if (FD->hasAttrs())
@@ -575,7 +619,7 @@ void MallocChecker::checkPostStmt(const
       if (SymbolRef Sym = C.getSVal(*I).getAsSymbol())
         checkUseAfterFree(Sym, C, *I);
 
-  if (!Filter.CNewDeleteChecker)
+  if (!Filter.CNewDeleteChecker && !Filter.CMismatchedDeallocatorChecker)
     return;
 
   if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext()))
@@ -586,19 +630,20 @@ void MallocChecker::checkPostStmt(const
   // value (if any) and we don't want to loose this value. So we call 
   // MallocUpdateRefState() instead of MallocMemAux() which breakes the 
   // existing binding.
-  State = MallocUpdateRefState(C, NE, State);
+  State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray 
+                                                           : AF_CXXNew);
   C.addTransition(State);
 }
 
 void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, 
                                  CheckerContext &C) const {
 
-  if (!Filter.CNewDeleteChecker) {
+  if (!Filter.CNewDeleteChecker)
     if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
       checkUseAfterFree(Sym, C, DE->getArgument());
 
+  if (!Filter.CNewDeleteChecker && !Filter.CMismatchedDeallocatorChecker)
     return;
-  }
 
   if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext()))
     return;
@@ -674,7 +719,8 @@ ProgramStateRef MallocChecker::MallocMem
 ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
                                            const CallExpr *CE,
                                            SVal Size, SVal Init,
-                                           ProgramStateRef state) {
+                                           ProgramStateRef State,
+                                           AllocationFamily Family) {
 
   // Bind the return value to the symbolic value from the heap region.
   // TODO: We could rewrite post visit to eval call; 'malloc' does not have
@@ -684,14 +730,14 @@ ProgramStateRef MallocChecker::MallocMem
   const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
   DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count)
       .castAs<DefinedSVal>();
-  state = state->BindExpr(CE, C.getLocationContext(), RetVal);
+  State = State->BindExpr(CE, C.getLocationContext(), RetVal);
 
   // We expect the malloc functions to return a pointer.
   if (!RetVal.getAs<Loc>())
     return 0;
 
   // Fill the region with the initialization value.
-  state = state->bindDefault(RetVal, Init);
+  State = State->bindDefault(RetVal, Init);
 
   // Set the region's extent equal to the Size parameter.
   const SymbolicRegion *R =
@@ -703,20 +749,21 @@ ProgramStateRef MallocChecker::MallocMem
     SValBuilder &svalBuilder = C.getSValBuilder();
     DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
     DefinedOrUnknownSVal extentMatchesSize =
-        svalBuilder.evalEQ(state, Extent, *DefinedSize);
+        svalBuilder.evalEQ(State, Extent, *DefinedSize);
 
-    state = state->assume(extentMatchesSize, true);
-    assert(state);
+    State = State->assume(extentMatchesSize, true);
+    assert(State);
   }
   
-  return MallocUpdateRefState(C, CE, state);
+  return MallocUpdateRefState(C, CE, State, Family);
 }
 
 ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
                                                     const Expr *E,
-                                                    ProgramStateRef state) {
+                                                    ProgramStateRef State,
+                                                    AllocationFamily Family) {
   // Get the return value.
-  SVal retVal = state->getSVal(E, C.getLocationContext());
+  SVal retVal = State->getSVal(E, C.getLocationContext());
 
   // We expect the malloc functions to return a pointer.
   if (!retVal.getAs<Loc>())
@@ -726,8 +773,7 @@ ProgramStateRef MallocChecker::MallocUpd
   assert(Sym);
 
   // Set the symbol's state to Allocated.
-  return state->set<RegionState>(Sym, RefState::getAllocated(E));
-
+  return State->set<RegionState>(Sym, RefState::getAllocated(Family, E));
 }
 
 ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
@@ -779,6 +825,100 @@ static bool didPreviousFreeFail(ProgramS
   return false;
 }
 
+AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, 
+                                                    const Expr *E) const {
+  if (!E)
+    return AF_None;
+
+  if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+    const FunctionDecl *FD = C.getCalleeDecl(CE);
+    ASTContext &Ctx = C.getASTContext();
+
+    if (isFreeFunction(FD, Ctx))
+      return AF_Malloc;
+
+    if (isStandardNewDelete(FD, Ctx)) {
+      OverloadedOperatorKind Kind = FD->getOverloadedOperator();
+      if (Kind == OO_Delete)
+        return AF_CXXNew;
+      else if (Kind == OO_Array_Delete)
+        return AF_CXXNewArray;
+    }
+
+    return AF_None;
+  }
+
+  if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E))
+    return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew;
+
+  if (isa<ObjCMessageExpr>(E))
+    return AF_Malloc;
+
+  return AF_None;
+}
+
+bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, 
+                                          const Expr *E) const {
+  if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+    // FIXME: This doesn't handle indirect calls.
+    const FunctionDecl *FD = CE->getDirectCallee();
+    if (!FD)
+      return false;
+    
+    os << *FD;
+    if (!FD->isOverloadedOperator())
+      os << "()";
+    return true;
+  }
+
+  if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
+    if (Msg->isInstanceMessage())
+      os << "-";
+    else
+      os << "+";
+    os << Msg->getSelector().getAsString();
+    return true;
+  }
+
+  if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
+    os << "'" 
+       << getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator())
+       << "'";
+    return true;
+  }
+
+  if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) {
+    os << "'" 
+       << getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator())
+       << "'";
+    return true;
+  }
+
+  return false;
+}
+
+void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
+                                           const Expr *E) const {
+  AllocationFamily Family = getAllocationFamily(C, E);
+
+  switch(Family) {
+    case AF_Malloc: os << "malloc()"; return;
+    case AF_CXXNew: os << "'new'"; return;
+    case AF_CXXNewArray: os << "'new[]'"; return;
+    case AF_None: llvm_unreachable("not a deallocation expression");
+  }
+}
+
+void MallocChecker::printExpectedDeallocName(raw_ostream &os, 
+                                             AllocationFamily Family) const {
+  switch(Family) {
+    case AF_Malloc: os << "free()"; return;
+    case AF_CXXNew: os << "'delete'"; return;
+    case AF_CXXNewArray: os << "'delete[]'"; return;
+    case AF_None: llvm_unreachable("suspicious AF_None argument");
+  }
+}
+
 ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
                                           const Expr *ArgExpr,
                                           const Expr *ParentExpr,
@@ -812,7 +952,7 @@ ProgramStateRef MallocChecker::FreeMemAu
   // Nonlocs can't be freed, of course.
   // Non-region locations (labels and fixed addresses) also shouldn't be freed.
   if (!R) {
-    ReportBadFree(C, ArgVal, ArgExpr->getSourceRange());
+    ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
     return 0;
   }
   
@@ -820,7 +960,7 @@ ProgramStateRef MallocChecker::FreeMemAu
   
   // Blocks might show up as heap data, but should not be free()d
   if (isa<BlockDataRegion>(R)) {
-    ReportBadFree(C, ArgVal, ArgExpr->getSourceRange());
+    ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
     return 0;
   }
   
@@ -837,7 +977,7 @@ ProgramStateRef MallocChecker::FreeMemAu
     // function, so UnknownSpaceRegion is always a possibility.
     // False negatives are better than false positives.
     
-    ReportBadFree(C, ArgVal, ArgExpr->getSourceRange());
+    ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
     return 0;
   }
 
@@ -860,6 +1000,14 @@ ProgramStateRef MallocChecker::FreeMemAu
     return 0;
   }
 
+  // Check if an expected deallocation function matches the real one.
+  if (RsBase && 
+      RsBase->getAllocationFamily() != AF_None &&
+      RsBase->getAllocationFamily() != getAllocationFamily(C, ParentExpr) ) {
+    ReportBadDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase);
+    return 0;
+  }
+
   // Check if the memory location being freed is the actual location
   // allocated, or an offset.
   RegionOffset Offset = R->getAsOffset();
@@ -867,7 +1015,9 @@ ProgramStateRef MallocChecker::FreeMemAu
       Offset.isValid() &&
       !Offset.hasSymbolicOffset() &&
       Offset.getOffset() != 0) {
-    ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange());
+    const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
+    ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, 
+                     AllocExpr);
     return 0;
   }
 
@@ -887,12 +1037,15 @@ ProgramStateRef MallocChecker::FreeMemAu
     }
   }
 
+  AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() : AF_None;
   // Normal free.
-  if (Hold) {
+  if (Hold)
     return State->set<RegionState>(SymBase,
-                                   RefState::getRelinquished(ParentExpr));
-  }
-  return State->set<RegionState>(SymBase, RefState::getReleased(ParentExpr));
+                                   RefState::getRelinquished(Family,
+                                                             ParentExpr));
+
+  return State->set<RegionState>(SymBase,
+                                 RefState::getReleased(Family, ParentExpr));
 }
 
 bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
@@ -982,38 +1135,43 @@ bool MallocChecker::SummarizeRegion(raw_
   }
 }
 
-void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
-                                  SourceRange Range) const {
+void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, 
+                                  SourceRange Range, 
+                                  const Expr *DeallocExpr) const {
+
+  if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && 
+      !Filter.CNewDeleteChecker)
+    return;
+
   if (ExplodedNode *N = C.generateSink()) {
     if (!BT_BadFree)
       BT_BadFree.reset(new BugType("Bad free", "Memory Error"));
     
     SmallString<100> buf;
     llvm::raw_svector_ostream os(buf);
-    
+
     const MemRegion *MR = ArgVal.getAsRegion();
-    if (MR) {
-      while (const ElementRegion *ER = dyn_cast<ElementRegion>(MR))
-        MR = ER->getSuperRegion();
-      
-      // Special case for alloca()
-      if (isa<AllocaRegion>(MR))
-        os << "Argument to free() was allocated by alloca(), not malloc()";
-      else {
-        os << "Argument to free() is ";
-        if (SummarizeRegion(os, MR))
-          os << ", which is not memory allocated by malloc()";
-        else
-          os << "not memory allocated by malloc()";
-      }
-    } else {
-      os << "Argument to free() is ";
-      if (SummarizeValue(os, ArgVal))
-        os << ", which is not memory allocated by malloc()";
+    while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))
+      MR = ER->getSuperRegion();
+
+    if (MR && isa<AllocaRegion>(MR))
+      os << "Memory allocated by alloca() should not be deallocated";
+    else {
+      os << "Argument to ";
+      if (!printAllocDeallocName(os, C, DeallocExpr))
+        os << "deallocator";
+
+      os << " is ";
+      bool Summarized = MR ? SummarizeRegion(os, MR) 
+                           : SummarizeValue(os, ArgVal);
+      if (Summarized)
+        os << ", which is not memory allocated by ";
       else
-        os << "not memory allocated by malloc()";
+        os << "not memory allocated by ";
+
+      printExpectedAllocName(os, C, DeallocExpr);
     }
-    
+
     BugReport *R = new BugReport(*BT_BadFree, os.str(), N);
     R->markInteresting(MR);
     R->addRange(Range);
@@ -1021,8 +1179,50 @@ void MallocChecker::ReportBadFree(Checke
   }
 }
 
+void MallocChecker::ReportBadDealloc(CheckerContext &C, SourceRange Range,
+                                     const Expr *DeallocExpr, 
+                                     const RefState *RS) const {
+
+  if (!Filter.CMismatchedDeallocatorChecker)
+    return;
+
+  if (ExplodedNode *N = C.generateSink()) {
+    if (!BT_BadDealloc)
+      BT_BadDealloc.reset(new BugType("Bad deallocator", "Memory Error"));
+    
+    SmallString<100> buf;
+    llvm::raw_svector_ostream os(buf);
+
+    const Expr *AllocExpr = cast<Expr>(RS->getStmt());
+    SmallString<20> AllocBuf;
+    llvm::raw_svector_ostream AllocOs(AllocBuf);
+    SmallString<20> DeallocBuf;
+    llvm::raw_svector_ostream DeallocOs(DeallocBuf);
+
+    os << "Memory";
+    if (printAllocDeallocName(AllocOs, C, AllocExpr))
+      os << " allocated by " << AllocOs.str();
+
+    os << " should be deallocated by ";
+      printExpectedDeallocName(os, RS->getAllocationFamily());
+
+    if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
+      os << ", not " << DeallocOs.str();
+
+    BugReport *R = new BugReport(*BT_BadDealloc, os.str(), N);
+    R->addRange(Range);
+    C.emitReport(R);
+  }
+}
+
 void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
-                                     SourceRange Range) const {
+                                     SourceRange Range, const Expr *DeallocExpr,
+                                     const Expr *AllocExpr) const {
+
+  if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && 
+      !Filter.CNewDeleteChecker)
+    return;
+
   ExplodedNode *N = C.generateSink();
   if (N == NULL)
     return;
@@ -1032,6 +1232,8 @@ void MallocChecker::ReportOffsetFree(Che
 
   SmallString<100> buf;
   llvm::raw_svector_ostream os(buf);
+  SmallString<20> AllocNameBuf;
+  llvm::raw_svector_ostream AllocNameOs(AllocNameBuf);
 
   const MemRegion *MR = ArgVal.getAsRegion();
   assert(MR && "Only MemRegion based symbols can have offset free errors");
@@ -1044,11 +1246,18 @@ void MallocChecker::ReportOffsetFree(Che
 
   int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth();
 
-  os << "Argument to free() is offset by "
+  os << "Argument to ";
+  if (!printAllocDeallocName(os, C, DeallocExpr))
+    os << "deallocator";
+  os << " is offset by "
      << offsetBytes
      << " "
      << ((abs(offsetBytes) > 1) ? "bytes" : "byte")
-     << " from the start of memory allocated by malloc()";
+     << " from the start of ";
+  if (AllocExpr && printAllocDeallocName(AllocNameOs, C, AllocExpr))
+    os << "memory allocated by " << AllocNameOs.str();
+  else
+    os << "allocated memory";
 
   BugReport *R = new BugReport(*BT_OffsetFree, os.str(), N);
   R->markInteresting(MR->getBaseRegion());
@@ -1059,6 +1268,10 @@ void MallocChecker::ReportOffsetFree(Che
 void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
                                        SymbolRef Sym) const {
 
+  if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && 
+      !Filter.CNewDeleteChecker)
+    return;
+
   if (ExplodedNode *N = C.generateSink()) {
     if (!BT_UseFree)
       BT_UseFree.reset(new BugType("Use-after-free", "Memory Error"));
@@ -1077,6 +1290,10 @@ void MallocChecker::ReportDoubleFree(Che
                                      bool Released, SymbolRef Sym, 
                                      SymbolRef PrevSym) const {
 
+  if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && 
+      !Filter.CNewDeleteChecker)
+    return;
+
   if (ExplodedNode *N = C.generateSink()) {
     if (!BT_DoubleFree)
       BT_DoubleFree.reset(new BugType("Double free", "Memory Error"));
@@ -1248,6 +1465,11 @@ MallocChecker::getAllocationSite(const E
 
 void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
                                CheckerContext &C) const {
+
+  if (!Filter.CMallocOptimistic && !Filter.CMallocPessimistic && 
+      !Filter.CNewDeleteChecker)
+    return;
+
   assert(N);
   if (!BT_Leak) {
     BT_Leak.reset(new BugType("Memory leak", "Memory Error"));
@@ -1491,7 +1713,7 @@ ProgramStateRef MallocChecker::evalAssum
       if (RS->isReleased()) {
         if (I.getData().Kind == RPToBeFreedAfterFailure)
           state = state->set<RegionState>(ReallocSym,
-              RefState::getAllocated(RS->getStmt()));
+              RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));
         else if (I.getData().Kind == RPDoNotTrackAfterFailure)
           state = state->remove<RegionState>(ReallocSym);
         else
@@ -1796,3 +2018,4 @@ void ento::register##name(CheckerManager
 REGISTER_CHECKER(MallocPessimistic)
 REGISTER_CHECKER(MallocOptimistic)
 REGISTER_CHECKER(NewDeleteChecker)
+REGISTER_CHECKER(MismatchedDeallocatorChecker)

Modified: cfe/trunk/test/Analysis/NewDelete-checker-test.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/NewDelete-checker-test.mm?rev=178250&r1=178249&r2=178250&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/NewDelete-checker-test.mm (original)
+++ cfe/trunk/test/Analysis/NewDelete-checker-test.mm Thu Mar 28 12:05:19 2013
@@ -84,7 +84,7 @@ void testUseAfterDelete() {
 
 void testDeleteAlloca() {
   int *p = (int *)__builtin_alloca(sizeof(int));
-  delete p; // expected-warning{{Argument to free() was allocated by alloca(), not malloc()}}
+  delete p; // expected-warning{{Memory allocated by alloca() should not be deallocated}}
 }
 
 void testDoubleDelete() {
@@ -95,18 +95,18 @@ void testDoubleDelete() {
 
 void testExprDeleteArg() {
   int i;
-  delete &i; // expected-warning{{Argument to free() is the address of the local variable 'i', which is not memory allocated by malloc()}}
-} // FIXME: 'free()' -> 'delete'; 'malloc()' -> 'new'
+  delete &i; // expected-warning{{Argument to 'delete' is the address of the local variable 'i', which is not memory allocated by 'new'}}
+}
 
 void testExprDeleteArrArg() {
   int i;
-  delete[] &i; // expected-warning{{Argument to free() is the address of the local variable 'i', which is not memory allocated by malloc()}}
-} // FIXME: 'free()' -> 'delete[]'; 'malloc()' -> 'new[]'
+  delete[] &i; // expected-warning{{Argument to 'delete[]' is the address of the local variable 'i', which is not memory allocated by 'new[]'}}
+}
 
 void testAllocDeallocNames() {
   int *p = new(std::nothrow) int[1];
-  delete[] (++p); // expected-warning{{Argument to free() is offset by 4 bytes from the start of memory allocated by malloc()}}
-} // FIXME: 'free()' -> 'delete[]'; 'malloc()' -> 'new[]'
+  delete[] (++p); // expected-warning{{Argument to 'delete[]' is offset by 4 bytes from the start of memory allocated by 'new[]'}}
+}
 
 //----------------------------------------------------------------------------
 // Check for intersections with unix.Malloc and unix.MallocWithAnnotations 
@@ -143,8 +143,9 @@ void testFreeNewExpr() {
 
 void testObjcFreeNewed() {
   int *p = new int;
-  NSData *nsdata = [NSData dataWithBytesNoCopy:p length:sizeof(int) freeWhenDone:1]; // pointer escaped, no-warning
+  NSData *nsdata = [NSData dataWithBytesNoCopy:p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Memory is never released; potential leak}}
 }
+// FIXME: Pointer should escape
 
 void testFreeAfterDelete() {
   int *p = new int;  

Added: cfe/trunk/test/Analysis/alloc-match-dealloc.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/alloc-match-dealloc.mm?rev=178250&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/alloc-match-dealloc.mm (added)
+++ cfe/trunk/test/Analysis/alloc-match-dealloc.mm Thu Mar 28 12:05:19 2013
@@ -0,0 +1,187 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.MismatchedDeallocator -fblocks -verify %s
+
+#include "Inputs/system-header-simulator-objc.h"
+#include "Inputs/system-header-simulator-cxx.h"
+
+typedef __typeof__(sizeof(int)) size_t;
+void *malloc(size_t);
+void *realloc(void *ptr, size_t size);
+void *calloc(size_t nmemb, size_t size);
+char *strdup(const char *s);
+void __attribute((ownership_returns(malloc))) *my_malloc(size_t);
+
+void free(void *);
+void __attribute((ownership_takes(malloc, 1))) my_free(void *);
+
+//---------------------------------------------------------------
+// Test if an allocation function matches deallocation function
+//---------------------------------------------------------------
+
+//--------------- test malloc family
+void testMalloc1() {
+  int *p = (int *)malloc(sizeof(int));
+  delete p; // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not 'delete'}}
+}
+
+void testMalloc2() {
+  int *p = (int *)malloc(8);
+  int *q = (int *)realloc(p, 16);
+  delete q; // expected-warning{{Memory allocated by realloc() should be deallocated by free(), not 'delete'}}
+}
+
+void testMalloc3() {
+  int *p = (int *)calloc(1, sizeof(int));
+  delete p; // expected-warning{{Memory allocated by calloc() should be deallocated by free(), not 'delete'}}
+}
+
+void testMalloc4(const char *s) {
+  char *p = strdup(s);
+  delete p; // expected-warning{{Memory allocated by strdup() should be deallocated by free(), not 'delete'}}
+}
+
+void testMalloc5() {
+  int *p = (int *)my_malloc(sizeof(int));
+  delete p; // expected-warning{{Memory allocated by my_malloc() should be deallocated by free(), not 'delete'}}
+}
+
+void testMalloc6() {
+  int *p = (int *)malloc(sizeof(int));
+  operator delete(p); // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not operator delete}}
+}
+
+void testMalloc7() {
+  int *p = (int *)malloc(sizeof(int));
+  delete[] p; // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not 'delete[]'}}
+}
+
+void testMalloc8() {
+  int *p = (int *)malloc(sizeof(int));
+  operator delete[](p); // expected-warning{{Memory allocated by malloc() should be deallocated by free(), not operator delete[]}}
+}
+
+//--------------- test new family
+void testNew1() {
+  int *p = new int;
+  free(p); // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not free()}}
+}
+
+void testNew2() {
+  int *p = (int *)operator new(0);
+  free(p); // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not free()}}
+}
+
+void testNew3() {
+  int *p = new int[1];
+  free(p); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not free()}}
+}
+
+void testNew4() {
+  int *p = new int;
+  realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not realloc()}}
+}
+
+void testNew5() {
+  int *p = (int *)operator new(0);
+  realloc(p, sizeof(long)); // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not realloc()}}
+}
+
+void testNew6() {
+  int *p = new int[1];
+  realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not realloc()}}
+}
+
+void testNew7() {
+  int *p = new int;
+  delete[] p; // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not 'delete[]'}}
+}
+
+void testNew8() {
+  int *p = (int *)operator new(0);
+  delete[] p; // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not 'delete[]'}}
+}
+
+void testNew9() {
+  int *p = new int[1];
+  delete p; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}
+}
+
+void testNew10() {
+  int *p = (int *)operator new[](0);
+  delete p; // expected-warning{{Memory allocated by operator new[] should be deallocated by 'delete[]', not 'delete'}}
+}
+
+void testNew11(NSUInteger dataLength) {
+  int *p = new int;
+  NSData *d = [NSData dataWithBytesNoCopy:p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not +dataWithBytesNoCopy:length:freeWhenDone:}}
+  // FIXME: should be "+dataWithBytesNoCopy:length:freeWhenDone: cannot take ownership of memory allocated by 'new'."
+}
+
+//-------------------------------------------------------
+// Check for intersection with unix.Malloc bounded with 
+// unix.MismatchedDeallocator
+//-------------------------------------------------------
+
+// new/delete oparators are subjects of cplusplus.NewDelete.
+void testNewDeleteNoWarn() {
+  int i;
+  delete &i; // no-warning
+
+  int *p1 = new int;
+  delete ++p1; // no-warning
+
+  int *p2 = new int;
+  delete p2;
+  delete p2; // no-warning
+
+  int *p3 = new int; // no-warning
+}
+
+void testDeleteOpAfterFree() {
+  int *p = (int *)malloc(sizeof(int));
+  free(p);
+  operator delete(p); // no-warning
+}
+
+void testDeleteAfterFree() {
+  int *p = (int *)malloc(sizeof(int));
+  free(p);
+  delete p; // no-warning
+}
+
+void testStandardPlacementNewAfterFree() {
+  int *p = (int *)malloc(sizeof(int));
+  free(p);
+  p = new(p) int; // no-warning
+}
+
+//---------------------------------------------------------------
+// Check for intersection with cplusplus.NewDelete bounded with 
+// unix.MismatchedDeallocator
+//---------------------------------------------------------------
+
+// malloc()/free() are subjects of unix.Malloc and unix.MallocWithAnnotations
+void testMallocFreeNoWarn() {
+  int i;
+  free(&i); // no-warning
+
+  int *p1 = (int *)malloc(sizeof(int));
+  free(++p1); // no-warning
+
+  int *p2 = (int *)malloc(sizeof(int));
+  free(p2);
+  free(p2); // no-warning
+
+  int *p3 = (int *)malloc(sizeof(int)); // no-warning
+}
+
+void testFreeAfterDelete() {
+  int *p = new int;  
+  delete p;
+  free(p); // no-warning
+}
+
+void testStandardPlacementNewAfterDelete() {
+  int *p = new int;  
+  delete p;
+  p = new(p) int; // no-warning
+}

Modified: cfe/trunk/test/Analysis/free.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/free.c?rev=178250&r1=178249&r2=178250&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/free.c (original)
+++ cfe/trunk/test/Analysis/free.c Thu Mar 28 12:05:19 2013
@@ -50,7 +50,7 @@ void t10 () {
 
 void t11 () {
   char *p = (char*)__builtin_alloca(2);
-  free(p); // expected-warning {{Argument to free() was allocated by alloca(), not malloc()}}
+  free(p); // expected-warning {{Memory allocated by alloca() should not be deallocated}}
 }
 
 void t12 () {

Modified: cfe/trunk/test/Analysis/malloc.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/malloc.mm?rev=178250&r1=178249&r2=178250&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/malloc.mm (original)
+++ cfe/trunk/test/Analysis/malloc.mm Thu Mar 28 12:05:19 2013
@@ -68,7 +68,7 @@ void testNSStringFreeWhenDoneNO2(NSUInte
 
 void testOffsetFree() {
   int *p = (int *)malloc(sizeof(int));
-  NSData *nsdata = [NSData dataWithBytesNoCopy:++p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Argument to free() is offset by 4 bytes from the start of memory allocated by malloc()}}
+  NSData *nsdata = [NSData dataWithBytesNoCopy:++p length:sizeof(int) freeWhenDone:1]; // expected-warning{{Argument to +dataWithBytesNoCopy:length:freeWhenDone: is offset by 4 bytes from the start of memory allocated by malloc()}}
 }
 
 void testRelinquished1() {





More information about the cfe-commits mailing list