Hi, Jordan. Thanx for the review!

Attached is the new version of the patch with all the comments addressed. Also added support for directly called operator new()/new[]() and operator delete()

There is currently one problem with handling of operator delete(). The following test

void testDeleteOp1() {
  int *p = (int *)malloc(sizeof(int));
operator delete(p); // FIXME: should complain "Argument to operator delete() was allocated by malloc(), not operator new"
}

produce no warnings as attached RefState seem to be missing at the point when checkPostStmt(const CallExpr *CE, CheckerContext &C) callback is called for operator delete(p). I haven't investigated the problem deeply yet, intend to address it parallel with the review.

+  if (NE->getNumPlacementArgs())
+    return;
+  // skip placement new operators as they may not allocate memory

Two comments here:
- Please make sure all comments are complete, capitalized, and punctuated sentences. (This has the important one, "complete"....just missing capitalization and punctuation.)
I'll try. Unfortunately I am not as good in English as I want to be, so sorry for my grammar, syntax, and punctuation.

--
Anton

Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/MallocChecker.cpp	(revision 174894)
+++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp	(working copy)
@@ -35,40 +35,70 @@
 
 namespace {
 
+// Possible deallocator kinds.
+// Numeration is added for convenient mapping to RefState::Kind
+enum DeallocatorKind {
+  D_unknown = 0,
+  D_free = 0x4,
+  D_delete = 0x8,
+  D_deleteArray = 0xC
+};
+
 class RefState {
+  // First two bits of Kind represent memory kind
+  static const int K_MASK = 0x3;
+  // Next two bits represent deallocator kind (mapped from DeallocatorKind)
+  static const int D_MASK = 0xC;
   enum Kind { // Reference to allocated memory.
               Allocated,
               // Reference to released/freed memory.
               Released,
               // The responsibility for freeing resources has transfered from
               // this reference. A relinquished symbol should not be freed.
-              Relinquished } K;
+              Relinquished,
+
+              // Mapped DeallocatorKind. 
+              // Expected kind of a deallocator; used to check if a real 
+              // kind of a deallocator matches expected one
+              Free = 0x4,
+              Delete = 0x8,
+              DeleteArray = 0xC
+  } K;
   const Stmt *S;
+  const FunctionDecl *CalleeDecl;
 
+  RefState(Kind k, const Stmt *s, const FunctionDecl *calleeDecl) 
+    : K(k), S(s), CalleeDecl(calleeDecl) {}
 public:
-  RefState(Kind k, const Stmt *s) : K(k), S(s) {}
+  bool isAllocated() const { return (K & K_MASK) == Allocated; }
+  bool isReleased() const { return (K & K_MASK) == Released; }
+  bool isRelinquished() const { return (K & K_MASK) == Relinquished; }
+  DeallocatorKind getDeallocKind() const { 
+    return (DeallocatorKind)(K & D_MASK);
+  }
 
-  bool isAllocated() const { return K == Allocated; }
-  bool isReleased() const { return K == Released; }
-  bool isRelinquished() const { return K == Relinquished; }
-
   const Stmt *getStmt() const { return S; }
+  const FunctionDecl *getCalleeDecl() const { return CalleeDecl; }
 
   bool operator==(const RefState &X) const {
-    return K == X.K && S == X.S;
+    return K == X.K && S == X.S && CalleeDecl == X.CalleeDecl;
   }
 
-  static RefState getAllocated(const Stmt *s) {
-    return RefState(Allocated, s);
+  static RefState getAllocated(DeallocatorKind dKind, const Stmt *s, 
+                               const FunctionDecl *calleeDecl) {
+    return RefState((Kind)(Allocated | dKind), s, calleeDecl);
   }
-  static RefState getReleased(const Stmt *s) { return RefState(Released, s); }
+  static RefState getReleased(const Stmt *s) { 
+    return RefState(Released, s, 0); 
+  }
   static RefState getRelinquished(const Stmt *s) {
-    return RefState(Relinquished, s);
+    return RefState(Relinquished, s, 0);
   }
 
   void Profile(llvm::FoldingSetNodeID &ID) const {
     ID.AddInteger(K);
     ID.AddPointer(S);
+    ID.AddPointer(CalleeDecl);
   }
 
   void dump(raw_ostream &OS) const {
@@ -77,7 +107,7 @@
       "Released",
       "Relinquished"
     };
-    OS << Table[(unsigned) K];
+    OS << Table[(unsigned) K & K_MASK];
   }
 
   LLVM_ATTRIBUTE_USED void dump() const {
@@ -120,6 +150,8 @@
                                      check::PreStmt<ReturnStmt>,
                                      check::PreStmt<CallExpr>,
                                      check::PostStmt<CallExpr>,
+                                     check::PostStmt<CXXNewExpr>,
+                                     check::PreStmt<CXXDeleteExpr>,
                                      check::PostStmt<BlockExpr>,
                                      check::PostObjCMessage,
                                      check::Location,
@@ -148,6 +180,8 @@
 
   void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
   void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+  void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+  void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
   void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
   void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
@@ -168,31 +202,46 @@
 private:
   void initIdentifierInfo(ASTContext &C) const;
 
+  /// Auxiliary functions that return kind and print names of 
+  /// allocators/deallocators
+  DeallocatorKind GetDeallocKind(CheckerContext &C, const Expr *E) const;
+  void PrintAllocDeallocName(raw_ostream &os, CheckerContext &C, 
+                             const Expr *E) const;
+  void PrintAllocDeallocName(raw_ostream &os, CheckerContext &C, 
+                             const RefState *RS) const;
+  void PrintExpectedAllocName(raw_ostream &os, CheckerContext &C, 
+                              const Expr *DeallocExpr) const;
+
   /// Check if this is one of the functions which can allocate/reallocate memory 
   /// pointed to by one of its arguments.
   bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
   bool isFreeFunction(const FunctionDecl *FD, ASTContext &C) const;
   bool isAllocationFunction(const FunctionDecl *FD, ASTContext &C) const;
+  bool isDefaultNonptrplacementNewDelete(const FunctionDecl *FD,
+                                         CheckerContext &C) const;
 
   static ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
                                               const CallExpr *CE,
                                               const OwnershipAttr* Att);
   static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
                                      const Expr *SizeEx, SVal Init,
-                                     ProgramStateRef state) {
+                                     ProgramStateRef state,
+                                     DeallocatorKind dKind = D_free) {
     return MallocMemAux(C, CE,
                         state->getSVal(SizeEx, C.getLocationContext()),
-                        Init, state);
+                        Init, state, dKind);
   }
 
   static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
                                      SVal SizeEx, SVal Init,
-                                     ProgramStateRef state);
+                                     ProgramStateRef state,
+                                     DeallocatorKind dKind = D_free);
 
   /// Update the RefState to reflect the new memory allocation.
   static ProgramStateRef MallocUpdateRefState(CheckerContext &C,
-                                              const CallExpr *CE,
-                                              ProgramStateRef state);
+                                              const Expr *E,
+                                              ProgramStateRef state,
+                                              DeallocatorKind dKind = D_free);
 
   ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
                               const OwnershipAttr* Att) const;
@@ -225,8 +274,11 @@
 
   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 ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range)const;
+  void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range, 
+                     const Expr *DeallocExpr, const RefState *RS = 0) const;
+  void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, 
+                        const Expr *DeallocExpr, 
+                        const RefState *RS = 0) const;
 
   /// Find the location of the allocation for Sym on the path leading to the
   /// exploded node N.
@@ -439,6 +491,32 @@
   return false;
 }
 
+bool MallocChecker::isDefaultNonptrplacementNewDelete(const FunctionDecl *FD,
+                                                      CheckerContext &C) const {
+  if (!FD)
+    return false;
+
+  if (FD->getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
+    return false;
+
+  OverloadedOperatorKind kind = FD->getDeclName().getCXXOverloadedOperator();
+  if (kind != OO_New && kind != OO_Array_New && 
+      kind != OO_Delete && kind != OO_Array_Delete)
+    return false;
+
+  // Skip custom new operators
+  if (!FD->isImplicit() &&
+      !C.getSourceManager().isInSystemHeader(FD->getLocStart()))
+    return false;
+
+  if (FD->isReservedGlobalPlacementOperator())
+    return false;
+
+  // One of the standard new/new[]/delete/delete[] operators including placement
+  // nothrow versions
+  return true;
+}
+
 void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
   if (C.wasInlined)
     return;
@@ -470,6 +548,16 @@
       State = MallocUpdateRefState(C, CE, State);
     } else if (FunI == II_strndup) {
       State = MallocUpdateRefState(C, CE, State);
+    } else if (isDefaultNonptrplacementNewDelete(FD, C)) {
+      OverloadedOperatorKind K = FD->getDeclName().getCXXOverloadedOperator();
+      if (K == OO_New)
+        State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+                             D_delete);
+      else if (K == OO_Array_New)
+        State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+                             D_deleteArray);
+      else
+        State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
     }
   }
 
@@ -495,6 +583,52 @@
   C.addTransition(State);
 }
 
+void MallocChecker::checkPostStmt(const CXXNewExpr *NE, 
+                                  CheckerContext &C) const {
+
+  FunctionDecl *OperatorNew = NE->getOperatorNew();
+  if (!OperatorNew)
+    return;
+
+  // Skip custom new operators
+  if (!OperatorNew->isImplicit() &&
+      !C.getSourceManager().isInSystemHeader(OperatorNew->getLocStart()) &&
+      !NE->isGlobalNew())
+    return;
+
+  // Skip standard global placement operator new/new[](std::size_t, void * p);
+  // process all other standard new/new[] operators including placement
+  // operators new/new[](std::size_t, const std::nothrow_t&)
+  if (OperatorNew->isReservedGlobalPlacementOperator())
+    return;
+
+  ProgramStateRef State = C.getState();
+  // the return value from operator new is already bound and we don't want to 
+  // break this binding so we call MallocUpdateRefState instead of MallocMemAux
+  State = MallocUpdateRefState(C, NE, State, NE->isArray() ? D_deleteArray 
+                                                           : D_delete);
+  C.addTransition(State);
+}
+
+void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, 
+                                 CheckerContext &C) const {
+  FunctionDecl *OperatorDelete = DE->getOperatorDelete();
+  if (!OperatorDelete)
+    return;
+
+  // Skip custom delete operators
+  if (!OperatorDelete->isImplicit() &&
+      !C.getSourceManager().isInSystemHeader(OperatorDelete->getLocStart()))
+    return;
+
+  ProgramStateRef State = C.getState();
+  bool ReleasedAllocated = false;
+  State = FreeMemAux(C, DE->getArgument(), DE, State,
+                     /*Hold*/false, ReleasedAllocated);
+
+  C.addTransition(State);
+}
+
 static bool isFreeWhenDoneSetToZero(const ObjCMethodCall &Call) {
   Selector S = Call.getSelector();
   for (unsigned i = 1; i < S.getNumArgs(); ++i)
@@ -514,7 +648,6 @@
   // be released with 'free' by the new object.
   // Ex:  [NSData dataWithBytesNoCopy:bytes length:10];
   // Unless 'freeWhenDone' param set to 0.
-  // TODO: Check that the memory was allocated with malloc.
   bool ReleasedAllocatedMemory = false;
   Selector S = Call.getSelector();
   if ((S.getNameForSlot(0) == "dataWithBytesNoCopy" ||
@@ -547,7 +680,8 @@
 ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
                                            const CallExpr *CE,
                                            SVal Size, SVal Init,
-                                           ProgramStateRef state) {
+                                           ProgramStateRef state,
+                                           DeallocatorKind dKind) {
 
   // 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
@@ -582,14 +716,15 @@
     assert(state);
   }
   
-  return MallocUpdateRefState(C, CE, state);
+  return MallocUpdateRefState(C, CE, state, dKind);
 }
 
 ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
-                                                    const CallExpr *CE,
-                                                    ProgramStateRef state) {
+                                                    const Expr *E,
+                                                    ProgramStateRef state,
+                                                    DeallocatorKind dKind) {
   // Get the return value.
-  SVal retVal = state->getSVal(CE, C.getLocationContext());
+  SVal retVal = state->getSVal(E, C.getLocationContext());
 
   // We expect the malloc functions to return a pointer.
   if (!isa<Loc>(retVal))
@@ -598,8 +733,12 @@
   SymbolRef Sym = retVal.getAsLocSymbol();
   assert(Sym);
 
+  const FunctionDecl *FD = 0;
+  if (const CallExpr *CE = dyn_cast<CallExpr>(E))
+    FD = C.getCalleeDecl(CE);
+
   // Set the symbol's state to Allocated.
-  return state->set<RegionState>(Sym, RefState::getAllocated(CE));
+  return state->set<RegionState>(Sym, RefState::getAllocated(dKind, E, FD));
 
 }
 
@@ -652,6 +791,99 @@
   return false;
 }
 
+DeallocatorKind MallocChecker::GetDeallocKind(CheckerContext &C, 
+                                              const Expr *E) const {
+  if (!E)
+    return D_unknown;
+
+  if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+    const FunctionDecl *FD = C.getCalleeDecl(CE);
+    ASTContext &Ctx = C.getASTContext();
+
+    if (isFreeFunction(FD, Ctx))
+      return D_free;
+
+    if (isDefaultNonptrplacementNewDelete(FD, C)) {
+      OverloadedOperatorKind kind = 
+          FD->getDeclName().getCXXOverloadedOperator();
+      if (kind == OO_New)
+        return D_delete;
+      else if (kind == OO_Array_New)
+        return D_deleteArray;
+    }
+
+    return D_unknown;
+  }
+
+  if (const CXXDeleteExpr *DE = dyn_cast_or_null<CXXDeleteExpr>(E))
+    return DE->isArrayForm() ? D_deleteArray : D_delete;
+
+  return D_unknown;
+}
+
+void MallocChecker::PrintAllocDeallocName(raw_ostream &os, CheckerContext &C, 
+                                          const Expr *E) const {
+
+  if (!E)
+    return;
+
+  // get the exact name of an allocation function
+  if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+    if (const FunctionDecl *FD = CE->getDirectCallee()) {
+      if (FD->getKind() == Decl::Function) {
+        os << *FD;
+        if (FD->getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
+          os << "()";
+        return;
+      }
+    }
+  }
+
+  if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
+    os << "operator new" << (NE->isArray() ? "[]" : "");
+    return;
+  }
+
+  if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) {
+    os << "operator delete" << (DE->isArrayForm() ? "[]" : "");
+    return;
+  }
+
+  if (isa<ObjCMessageExpr>(E)) {
+    os << "Objective-C method";
+    return;
+  }
+
+  os << "unknown means";
+}
+
+void MallocChecker::PrintAllocDeallocName(raw_ostream &os, CheckerContext &C, 
+                                          const RefState *RS) const {
+  if (const FunctionDecl *FD = RS->getCalleeDecl()) {
+    if (FD->getKind() == Decl::Function) {
+      os << *FD;
+      if (FD->getDeclName().getNameKind() != DeclarationName::CXXOperatorName)
+        os << "()";
+      return;
+    }
+  }
+
+  PrintAllocDeallocName(os, C, cast<Expr>(RS->getStmt()));
+}
+
+void MallocChecker::PrintExpectedAllocName(raw_ostream &os, CheckerContext &C, 
+                                           const Expr *E) const {
+  DeallocatorKind dKind = GetDeallocKind(C, E);
+
+  switch(dKind) {
+    case D_free: os << "malloc()"; return;
+    case D_delete: os << "operator new"; return;
+    case D_deleteArray: os << "operator new[]"; return;
+    case D_unknown: os << "unknown means"; return;
+    default: assert(0 && "unhandled DeallocatorKind");
+  }
+}
+
 ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
                                           const Expr *ArgExpr,
                                           const Expr *ParentExpr,
@@ -685,7 +917,7 @@
   // 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;
   }
   
@@ -693,13 +925,14 @@
   
   // 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;
   }
   
   const MemSpaceRegion *MS = R->getMemorySpace();
   
-  // Parameters, locals, statics, and globals shouldn't be freed.
+  // Parameters, locals, statics, globals, and memory returned by alloca() 
+  // shouldn't be freed.
   if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
     // FIXME: at the time this code was written, malloc() regions were
     // represented by conjured symbols, which are all in UnknownSpaceRegion.
@@ -709,7 +942,7 @@
     // 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;
   }
 
@@ -746,6 +979,14 @@
     return 0;
   }
 
+  // Check if an expected deallocation function matches real one
+  if (RsBase && 
+      RsBase->getDeallocKind() != D_unknown &&
+      RsBase->getDeallocKind() != GetDeallocKind(C, ParentExpr) ) {
+    ReportBadFree(C, ArgVal, 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();
@@ -753,7 +994,7 @@
       Offset.isValid() &&
       !Offset.hasSymbolicOffset() &&
       Offset.getOffset() != 0) {
-    ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange());
+    ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, RsBase);
     return 0;
   }
 
@@ -868,38 +1109,49 @@
   }
 }
 
-void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
-                                  SourceRange range) const {
+void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, 
+                                  SourceRange range, const Expr *DeallocExpr,
+                                  const RefState *RS) const {
   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()";
+
+    os << "Argument to ";
+    PrintAllocDeallocName(os, C, DeallocExpr);
+    if (RS) {
+       os << " was allocated by ";
+       PrintAllocDeallocName(os, C, RS);
+       os << ", not ";
+    } else {
+      if (MR) {
+        while (const ElementRegion *ER = dyn_cast<ElementRegion>(MR))
+          MR = ER->getSuperRegion();
+
+        // Special case for alloca()
+        if (isa<AllocaRegion>(MR))
+           os << " was allocated by alloca(), not ";
+        else {
+          os << " is ";
+          if (SummarizeRegion(os, MR))
+            os << ", which is not memory allocated by ";
+          else
+            os << "not memory allocated by ";
+        }
+      } else {
+        os << " is ";
+        if (SummarizeValue(os, ArgVal))
+          os << ", which is not memory allocated by ";
         else
-          os << "not memory allocated by malloc()";
+          os << "not memory allocated by ";
       }
-    } else {
-      os << "Argument to free() is ";
-      if (SummarizeValue(os, ArgVal))
-        os << ", which is not memory allocated by malloc()";
-      else
-        os << "not memory allocated by malloc()";
     }
-    
+    PrintExpectedAllocName(os, C, DeallocExpr);
+
     BugReport *R = new BugReport(*BT_BadFree, os.str(), N);
     R->markInteresting(MR);
     R->addRange(range);
@@ -908,7 +1160,8 @@
 }
 
 void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
-                                     SourceRange Range) const {
+                                     SourceRange Range, const Expr *DeallocExpr,
+                                     const RefState *RS) const {
   ExplodedNode *N = C.generateSink();
   if (N == NULL)
     return;
@@ -930,11 +1183,17 @@
 
   int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth();
 
-  os << "Argument to free() is offset by "
+  os << "Argument to ";
+  PrintAllocDeallocName(os, C, DeallocExpr);
+  os << " is offset by "
      << offsetBytes
      << " "
      << ((abs(offsetBytes) > 1) ? "bytes" : "byte")
-     << " from the start of memory allocated by malloc()";
+     << " from the start of memory allocated by ";
+  if (RS)
+    PrintAllocDeallocName(os, C, RS);
+  else
+    PrintExpectedAllocName(os, C, DeallocExpr);
 
   BugReport *R = new BugReport(*BT_OffsetFree, os.str(), N);
   R->markInteresting(MR->getBaseRegion());
@@ -1343,7 +1602,8 @@
       if (RS->isReleased()) {
         if (I.getData().Kind == RPToBeFreedAfterFailure)
           state = state->set<RegionState>(ReallocSym,
-              RefState::getAllocated(RS->getStmt()));
+              RefState::getAllocated(RS->getDeallocKind(), RS->getStmt(),
+                                     RS->getCalleeDecl()));
         else if (I.getData().Kind == RPDoNotTrackAfterFailure)
           state = state->remove<RegionState>(ReallocSym);
         else
Index: test/Analysis/alloc-match-dealloc.cpp
===================================================================
--- test/Analysis/alloc-match-dealloc.cpp	(revision 0)
+++ test/Analysis/alloc-match-dealloc.cpp	(working copy)
@@ -0,0 +1,119 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.unix.MallocWithAnnotations -verify %s
+
+#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 delete expression
+void testDeleteExpr1() {
+  int *p = (int *)malloc(sizeof(int));
+  delete p; // expected-warning{{Argument to operator delete was allocated by malloc(), not operator new}}
+}
+
+void testDeleteExpr2() {
+  int *p = (int *)malloc(8);
+  int *q = (int *)realloc(p, 16);
+  delete q; // expected-warning{{Argument to operator delete was allocated by realloc(), not operator new}}
+}
+
+void testDeleteExpr3() {
+  int *p = (int *)calloc(1, sizeof(int));
+  delete p; // expected-warning{{Argument to operator delete was allocated by calloc(), not operator new}}
+}
+
+void testDeleteExpr4(const char *s) {
+  char *p = strdup(s);
+  delete p; // expected-warning{{Argument to operator delete was allocated by strdup(), not operator new}}
+}
+
+void testDeleteExpr5() {
+  int *p = (int *)my_malloc(sizeof(int));
+  delete p; // expected-warning{{Argument to operator delete was allocated by my_malloc(), not operator new}}
+}
+
+void testDeleteExpr6() {
+  int *p = (int *)__builtin_alloca(sizeof(int));
+  delete p; // expected-warning{{Argument to operator delete was allocated by alloca(), not operator new}}
+}
+
+void testDeleteExpr7() {
+  int *p = new int[1];
+  delete p; // expected-warning{{Argument to operator delete was allocated by operator new[], not operator new}}
+}
+
+void testDeleteExpr8() {
+  int *p = (int *)operator new[](0);
+  delete p; // expected-warning{{Argument to operator delete was allocated by operator new[], not operator new}}
+}
+
+//--------------- test operator delete
+void testDeleteOp1() {
+  int *p = (int *)malloc(sizeof(int));
+  operator delete(p); // FIXME: should complain "Argument to operator delete() was allocated by malloc(), not operator new"
+}
+
+//--------------- test delete[] expression
+void testDeleteArrayExpr1() {
+  int *p = (int *)malloc(sizeof(int));
+  delete[] p; // expected-warning{{Argument to operator delete[] was allocated by malloc(), not operator new[]}}
+}
+
+void testDeleteArrayExpr2() {
+  int *p = new int;
+  delete[] p; // expected-warning{{Argument to operator delete[] was allocated by operator new, not operator new[]}}
+}
+
+void testDeleteArrayExpr3() {
+  int *p = (int *)operator new(0);
+  delete[] p; // expected-warning{{Argument to operator delete[] was allocated by operator new, not operator new[]}}
+}
+
+//--------------- test operator delete[]
+void testDeleteArrayOp1() {
+  int *p = (int *)malloc(sizeof(int));
+  operator delete[](p); // FIXME: should complain "Argument to operator delete[]() was allocated by malloc(), not operator new"
+}
+
+//--------------- test free()
+void testFree1() {
+  int *p = new int;
+  free(p); // expected-warning{{Argument to free() was allocated by operator new, not malloc()}}
+}
+
+void testFree2() {
+  int *p = (int *)operator new(0);
+  free(p); // expected-warning{{Argument to free() was allocated by operator new, not malloc()}}
+}
+
+void testFree3() {
+  int *p = new int[1];
+  free(p); // expected-warning{{Argument to free() was allocated by operator new[], not malloc()}}
+}
+
+//--------------- test realloc()
+void testRealloc1() {
+  int *p = new int;
+  realloc(p, sizeof(long)); // expected-warning{{Argument to realloc() was allocated by operator new, not malloc()}}
+}
+
+void testRealloc2() {
+  int *p = (int *)operator new(0);
+  realloc(p, sizeof(long)); // expected-warning{{Argument to realloc() was allocated by operator new, not malloc()}}
+}
+
+void testRealloc3() {
+  int *p = new int[1];
+  realloc(p, sizeof(long)); // expected-warning{{Argument to realloc() was allocated by operator new[], not malloc()}}
+}
Index: test/Analysis/inline.cpp
===================================================================
--- test/Analysis/inline.cpp	(revision 174894)
+++ test/Analysis/inline.cpp	(working copy)
@@ -255,6 +255,7 @@
     IntWrapper *obj = new IntWrapper(42);
     // should be TRUE
     clang_analyzer_eval(obj->value == 42); // expected-warning{{UNKNOWN}}
+    delete obj;
   }
 
   void testPlacement() {
Index: test/Analysis/Inputs/system-header-simulator-cxx.h
===================================================================
--- test/Analysis/Inputs/system-header-simulator-cxx.h	(revision 174894)
+++ test/Analysis/Inputs/system-header-simulator-cxx.h	(working copy)
@@ -59,4 +59,28 @@
       return 0;
     }
   };
+
+  class bad_alloc : public exception {
+    public:
+    bad_alloc() throw();
+    bad_alloc(const bad_alloc&) throw();
+    bad_alloc& operator=(const bad_alloc&) throw();
+    virtual const char* what() const throw() {
+      return 0;
+    }
+  };
+
+  struct nothrow_t {};
+
+  extern const nothrow_t nothrow;
 }
+
+void* operator new(std::size_t, const std::nothrow_t&) throw();
+void* operator new[](std::size_t, const std::nothrow_t&) throw();
+void operator delete(void*, const std::nothrow_t&) throw();
+void operator delete[](void*, const std::nothrow_t&) throw();
+
+void* operator new (std::size_t size, void* ptr) throw() { return ptr; };
+void* operator new[] (std::size_t size, void* ptr) throw() { return ptr; };
+void operator delete (void* ptr, void*) throw() {};
+void operator delete[] (void* ptr, void*) throw() {};
Index: test/Analysis/new.cpp
===================================================================
--- test/Analysis/new.cpp	(revision 174894)
+++ test/Analysis/new.cpp	(working copy)
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-store region -std=c++11 -verify %s
+#include "Inputs/system-header-simulator-cxx.h"
 
 void clang_analyzer_eval(bool);
 
@@ -19,13 +20,6 @@
   clang_analyzer_eval(someGlobal == 0); // expected-warning{{TRUE}}
 }
 
-
-// This is the standard placement new.
-inline void* operator new(size_t, void* __p) throw()
-{
-  return __p;
-}
-
 void *testPlacementNew() {
   int *x = (int *)malloc(sizeof(int));
   *x = 1;
@@ -73,7 +67,6 @@
   clang_analyzer_eval(*n == 0); // expected-warning{{TRUE}}
 }
 
-
 struct PtrWrapper {
   int *x;
 
@@ -85,7 +78,80 @@
   return new PtrWrapper(static_cast<int *>(malloc(4)));
 }
 
+//--------------------------------
+// unix.Malloc checks
+//--------------------------------
 
+void testGlobalExprNewBeforeOverload() {
+  int *p = new int;
+} // expected-warning{{Memory is never released; potential leak}}
+
+void testGlobalOpNewBeforeOverload() {
+  void *p = operator new(0);
+} // expected-warning{{Memory is never released; potential leak}}
+
+void *operator new(size_t);
+void *operator new(size_t, double d);
+void *operator new[](size_t);
+void *operator new[](size_t, double d);
+
+void testExprPlacementNew() {
+  int i;
+  int *p1 = new(&i) int; // no warn - standard placement new
+
+  int *p2 = new(1.0) int; // no warn - overloaded placement new
+
+  int *p3 = new (std::nothrow) int;
+} // expected-warning{{Memory is never released; potential leak}}
+
+void testExprPlacementNewArray() {
+  int i;
+  int *p1 = new(&i) int[1]; // no warn - standard placement new[]
+
+  int *p2 = new(1.0) int[1]; // no warn - overloaded placement new[]
+
+  int *p3 = new (std::nothrow) int[1];
+} // expected-warning{{Memory is never released; potential leak}}
+
+void testCustomOpNew() {
+  void *p = operator new(0);
+} // no warn - custom new
+
+void testGlobalExprNew() {
+  void *p = ::new int;
+} // expected-warning{{Memory is never released; potential leak}}
+
+void testCustomExprNew() {
+  int *p = new int;
+} // no warn - custom new
+
+void testGlobalExprNewArray() {
+  void *p = ::new int[1];
+} // expected-warning{{Memory is never released; potential leak}}
+
+void testOverloadedExprNewArray() {
+  int *p = new int[1];
+} // no warn - custom new[]
+
+// test if unix.Malloc processes operator delete
+void testExprDeleteArg() {
+  int i;
+  delete &i; // expected-warning{{Argument to operator delete is the address of the local variable 'i', which is not memory allocated by operator new}}
+}
+
+// test if unix.Malloc handles operator delete[]
+void testExprDeleteArrArg() {
+  int i;
+  delete[] &i; // expected-warning{{Argument to operator delete[] is the address of the local variable 'i', which is not memory allocated by operator new[]}}
+}
+
+// test for proper allocator/deallocator names in the warning
+void testAllocDeallocNames() {
+  int *p = ::new int[1];
+  p += 1;
+  delete[] (p); // expected-warning{{Argument to operator delete[] is offset by 4 bytes from the start of memory allocated by operator new[]}}
+}
+
 //--------------------------------
 // Incorrectly-modelled behavior
 //--------------------------------
@@ -95,8 +161,10 @@
 
   // Should warn that *n is uninitialized.
   if (*n) { // no-warning
+    delete n;
     return 0;
   }
+  delete n;
   return 1;
 }
 
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to