[analyzer] Allow pointers escape through calls containing callback args.

(Since we don't have a generic pointer escape callback, modify
ExprEngineCallAndReturn as well as the malloc checker.)

llvm-svn: 156134
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 55c32ec..1415184 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1285,6 +1285,11 @@
     if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
       return false;
 
+    // If the call has a callback as an argument, assume the memory
+    // can be freed.
+    if (Call->hasNonZeroCallbackArg())
+      return false;
+
     // Otherwise, assume that the function does not free memory.
     // Most system calls, do not free the memory.
     return true;
@@ -1312,6 +1317,11 @@
       return false;
     }
 
+    // If the call has a callback as an argument, assume the memory
+    // can be freed.
+    if (Call->hasNonZeroCallbackArg())
+      return false;
+
     // Otherwise, assume that the function does not free memory.
     // Most system calls, do not free the memory.
     return true;
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 4d0da06..963dc90 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -323,12 +323,14 @@
       // allocators/deallocators upon container construction.
       // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
       // be deallocated by NSMapRemove.
+      // - Any call that has a callback as one of the arguments.
       if (FName == "pthread_setspecific" ||
           FName == "funopen" ||
           FName.endswith("NoCopy") ||
           (FName.startswith("NS") &&
             (FName.find("Insert") != StringRef::npos)) ||
-          Call.isCFCGAllowingEscape(FName))
+          Call.isCFCGAllowingEscape(FName) ||
+          Call.hasNonZeroCallbackArg())
         return;
     }
 
@@ -344,6 +346,10 @@
   if (const ObjCMethodDecl *MDecl = dyn_cast<ObjCMethodDecl>(CallDecl)) {
     assert(MDecl->param_size() <= Call.getNumArgs());
     unsigned Idx = 0;
+
+    if (Call.hasNonZeroCallbackArg())
+      return;
+
     for (clang::ObjCMethodDecl::param_const_iterator
          I = MDecl->param_begin(), E = MDecl->param_end(); I != E; ++I, ++Idx) {
       if (isPointerToConst(*I))
diff --git a/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp b/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp
index 65cdcd9..dc24e81 100644
--- a/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ObjCMessage.cpp
@@ -88,3 +88,69 @@
   return 0;
 }
 
+bool CallOrObjCMessage::isCallbackArg(unsigned Idx, const Type *T) const {
+  // Should we dig into struct fields, arrays ect?
+  if (T->isBlockPointerType() || T->isFunctionPointerType())
+    if (!getArgSVal(Idx).isZeroConstant())
+      return true;
+  return false;
+}
+
+bool CallOrObjCMessage::hasNonZeroCallbackArg() const {
+  unsigned NumOfArgs = getNumArgs();
+
+  // Process ObjC message first.
+  if (!CallE) {
+    const ObjCMethodDecl *D = Msg.getMethodDecl();
+    unsigned Idx = 0;
+    for (ObjCMethodDecl::param_const_iterator I = D->param_begin(),
+                                     E = D->param_end(); I != E; ++I, ++Idx) {
+      if (NumOfArgs <= Idx)
+        break;
+
+      if (isCallbackArg(Idx, (*I)->getType().getTypePtr()))
+        return true;
+    }
+    return false;
+  }
+
+  // Else, assume we are dealing with a Function call.
+  const FunctionDecl *FD = 0;
+  if (const CXXConstructExpr *Ctor =
+        CallE.dyn_cast<const CXXConstructExpr *>())
+    FD = Ctor->getConstructor();
+
+  const CallExpr * CE = CallE.get<const CallExpr *>();
+  FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
+
+  // If calling using a function pointer, assume the function does not
+  // have a callback. TODO: We could check the types of the arguments here.
+  if (!FD)
+    return false;
+
+  unsigned Idx = 0;
+  for (FunctionDecl::param_const_iterator I = FD->param_begin(),
+                                      E = FD->param_end(); I != E; ++I, ++Idx) {
+    if (NumOfArgs <= Idx)
+      break;
+
+    if (isCallbackArg(Idx, (*I)->getType().getTypePtr()))
+      return true;
+  }
+  return false;
+}
+
+bool CallOrObjCMessage::isCFCGAllowingEscape(StringRef FName) {
+  if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G'))
+         if (StrInStrNoCase(FName, "InsertValue") != StringRef::npos||
+             StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
+             StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
+             StrInStrNoCase(FName, "WithData") != StringRef::npos ||
+             StrInStrNoCase(FName, "AppendValue") != StringRef::npos||
+             StrInStrNoCase(FName, "SetAttribute") != StringRef::npos) {
+       return true;
+     }
+  return false;
+}
+
+