]> granicus.if.org Git - clang/commitdiff
[analyzer] Model another special-case kind of cast for OSObject RetainCountChecker
authorGeorge Karpenkov <ekarpenkov@apple.com>
Tue, 22 Jan 2019 19:50:47 +0000 (19:50 +0000)
committerGeorge Karpenkov <ekarpenkov@apple.com>
Tue, 22 Jan 2019 19:50:47 +0000 (19:50 +0000)
Differential Revision: https://reviews.llvm.org/D56951

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

include/clang/StaticAnalyzer/Core/RetainSummaryManager.h
lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
test/Analysis/os_object_base.h
test/Analysis/osobject-retain-release.cpp

index 949a8cce876e95903e9e1867090d1751376da859..a7cbe6b40fe69db5c7700ac9b397c4ffcb9dce8b 100644 (file)
@@ -677,6 +677,9 @@ public:
     // Function returns the first argument.
     Identity,
 
+    // Function returns "this" argument.
+    IdentityThis,
+
     // Function either returns zero, or the input parameter.
     IdentityOrZero
   };
index 4b8d7bd262ac0ab549a81116d7cb73d253f87538..e86d63804b8ef149bfc5247cd08e6c162d6db8be 100644 (file)
@@ -849,7 +849,6 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
 //===----------------------------------------------------------------------===//
 
 bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
-  // Get the callee. We're only interested in simple C functions.
   ProgramStateRef state = C.getState();
   const FunctionDecl *FD = C.getCalleeDecl(CE);
   if (!FD)
@@ -874,18 +873,27 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
 
   // Bind the return value.
   if (BSmr == BehaviorSummary::Identity ||
-      BSmr == BehaviorSummary::IdentityOrZero) {
-    SVal RetVal = state->getSVal(CE->getArg(0), LCtx);
+      BSmr == BehaviorSummary::IdentityOrZero ||
+      BSmr == BehaviorSummary::IdentityThis) {
+
+    const Expr *BindReturnTo =
+        (BSmr == BehaviorSummary::IdentityThis)
+            ? cast<CXXMemberCallExpr>(CE)->getImplicitObjectArgument()
+            : CE->getArg(0);
+    SVal RetVal = state->getSVal(BindReturnTo, LCtx);
 
     // If the receiver is unknown or the function has
     // 'rc_ownership_trusted_implementation' annotate attribute, conjure a
     // return value.
+    // FIXME: this branch is very strange.
     if (RetVal.isUnknown() ||
         (hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
       SValBuilder &SVB = C.getSValBuilder();
       RetVal =
           SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount());
     }
+
+    // Bind the value.
     state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false);
 
     if (BSmr == BehaviorSummary::IdentityOrZero) {
index 42d87b4e27d187c9351f67b8c7e95f145c2698cd..6ebbc03c580f546ba4575218f7c3b36787ec80e2 100644 (file)
@@ -152,6 +152,10 @@ static bool isOSObjectDynamicCast(StringRef S) {
   return S == "safeMetaCast";
 }
 
+static bool isOSObjectThisCast(StringRef S) {
+  return S == "metaCast";
+}
+
 static bool isOSIteratorSubclass(const Decl *D) {
   return isSubclass(D, "OSIterator");
 }
@@ -219,13 +223,13 @@ RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
     const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
     if (PD && isOSObjectSubclass(PD)) {
       if (const IdentifierInfo *II = FD->getIdentifier()) {
-        if (isOSObjectDynamicCast(II->getName()))
+        StringRef FuncName = II->getName();
+        if (isOSObjectDynamicCast(FuncName) || isOSObjectThisCast(FuncName))
           return getDefaultSummary();
 
         // All objects returned with functions *not* starting with
         // get, or iterators, are returned at +1.
-        if ((!II->getName().startswith("get") &&
-             !II->getName().startswith("Get")) ||
+        if ((!FuncName.startswith("get") && !FuncName.startswith("Get")) ||
             isOSIteratorSubclass(PD)) {
           return getOSSummaryCreateRule(FD);
         } else {
@@ -703,8 +707,13 @@ RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD,
     // the input was non-zero),
     // or that it returns zero (when the cast failed, or the input
     // was zero).
-    if (TrackOSObjects && isOSObjectDynamicCast(FName)) {
-      return BehaviorSummary::IdentityOrZero;
+    if (TrackOSObjects) {
+      if (isOSObjectDynamicCast(FName) && FD->param_size() >= 1) {
+        return BehaviorSummary::IdentityOrZero;
+      } else if (isOSObjectThisCast(FName) && isa<CXXMethodDecl>(FD) &&
+                 !cast<CXXMethodDecl>(FD)->isStatic()) {
+        return BehaviorSummary::IdentityThis;
+      }
     }
 
     const FunctionDecl* FDD = FD->getDefinition();
index e388dddd58f170e3e3c56b243a2b89f76d41f6aa..2c69c9acdcfa1230424e3f46d071e77f2508d005 100644 (file)
@@ -23,6 +23,8 @@ struct OSMetaClassBase {
   static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst,
                                        const OSMetaClass *meta);
 
+  OSMetaClassBase *metaCast(const char *toMeta);
+
   virtual void retain() const;
   virtual void release() const;
   virtual void free();
index 9bc2e20454505823809375e01d7d03d7f6235f99..24d6cb284c0f3923714effb602f33d68db4f3d90 100644 (file)
@@ -54,6 +54,9 @@ struct OtherStruct {
   OtherStruct(OSArray *arr);
 };
 
+bool test_meta_cast_no_leak(OSMetaClassBase *arg) {
+  return arg && arg->metaCast("blah") != nullptr;
+}
 
 void escape(void *);
 void escape_with_source(void *p) {}