r351508 - [analyzer] [RetainCountChecker] Smart pointer support.

George Karpenkov via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 17 19:12:36 PST 2019


Author: george.karpenkov
Date: Thu Jan 17 19:12:35 2019
New Revision: 351508

URL: http://llvm.org/viewvc/llvm-project?rev=351508&view=rev
Log:
[analyzer] [RetainCountChecker] Smart pointer support.

rdar://47323216

Differential Revision: https://reviews.llvm.org/D56817

Added:
    cfe/trunk/test/Analysis/os_object_base.h
    cfe/trunk/test/Analysis/os_smart_ptr.h
Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h
    cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
    cfe/trunk/test/Analysis/osobject-retain-release.cpp
    cfe/trunk/test/Analysis/test-separate-retaincount.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h?rev=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/RetainSummaryManager.h Thu Jan 17 19:12:35 2019
@@ -685,6 +685,10 @@ public:
   Optional<BehaviorSummary> canEval(const CallExpr *CE, const FunctionDecl *FD,
                                     bool &hasTrustedImplementationAnnotation);
 
+  /// \return Whether the type corresponds to a known smart pointer
+  /// implementation (that is, everything about it is inlineable).
+  static bool isKnownSmartPointer(QualType QT);
+
   bool isTrustedReferenceCountImplementation(const FunctionDecl *FD);
 
   const RetainSummary *getSummary(const CallEvent &Call,

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp?rev=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp Thu Jan 17 19:12:35 2019
@@ -529,6 +529,13 @@ void RetainCountChecker::processSummaryO
   C.addTransition(state);
 }
 
+static bool isSmartPtrField(const MemRegion *MR) {
+  const auto *TR = dyn_cast<TypedValueRegion>(
+    cast<SubRegion>(MR)->getSuperRegion());
+  return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType());
+}
+
+
 /// A value escapes in these possible cases:
 ///
 /// - binding to something that is not a memory region.
@@ -536,10 +543,15 @@ void RetainCountChecker::processSummaryO
 /// - binding to a variable that has a destructor attached using CleanupAttr
 ///
 /// We do not currently model what happens when a symbol is
-/// assigned to a struct field, so be conservative here and let the symbol go.
+/// assigned to a struct field, unless it is a known smart pointer
+/// implementation, about which we know that it is inlined.
 /// FIXME: This could definitely be improved upon.
 static bool shouldEscapeRegion(const MemRegion *R) {
+  if (isSmartPtrField(R))
+    return false;
+
   const auto *VR = dyn_cast<VarRegion>(R);
+
   if (!R->hasStackStorage() || !VR)
     return true;
 

Modified: cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp?rev=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp Thu Jan 17 19:12:35 2019
@@ -146,7 +146,7 @@ static bool isSubclass(const Decl *D,
 }
 
 static bool isOSObjectSubclass(const Decl *D) {
-  return isSubclass(D, "OSObject");
+  return isSubclass(D, "OSMetaClassBase");
 }
 
 static bool isOSObjectDynamicCast(StringRef S) {
@@ -199,6 +199,20 @@ static bool isOSObjectRelated(const CXXM
   return false;
 }
 
+bool
+RetainSummaryManager::isKnownSmartPointer(QualType QT) {
+  QT = QT.getCanonicalType();
+  const auto *RD = QT->getAsCXXRecordDecl();
+  if (!RD)
+    return false;
+  const IdentifierInfo *II = RD->getIdentifier();
+  if (II && II->getName() == "smart_ptr")
+    if (const auto *ND = dyn_cast<NamespaceDecl>(RD->getDeclContext()))
+      if (ND->getNameAsString() == "os")
+        return true;
+  return false;
+}
+
 const RetainSummary *
 RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
                                             StringRef FName, QualType RetTy) {

Added: cfe/trunk/test/Analysis/os_object_base.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/os_object_base.h?rev=351508&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/os_object_base.h (added)
+++ cfe/trunk/test/Analysis/os_object_base.h Thu Jan 17 19:12:35 2019
@@ -0,0 +1,54 @@
+#ifndef _OS_BASE_H
+#define _OS_BASE_H
+
+#define OS_CONSUME __attribute__((os_consumed))
+#define OS_RETURNS_RETAINED __attribute__((os_returns_retained))
+#define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero))
+#define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero))
+#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained))
+#define OS_CONSUMES_THIS __attribute__((os_consumes_this))
+
+#define OSTypeID(type)   (type::metaClass)
+
+#define OSDynamicCast(type, inst)   \
+    ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
+
+#define OSTypeAlloc(type)   ((type *) ((type::metaClass)->alloc()))
+
+using size_t = decltype(sizeof(int));
+
+struct OSMetaClass;
+
+struct OSMetaClassBase {
+  static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst,
+                                       const OSMetaClass *meta);
+
+  virtual void retain() const;
+  virtual void release() const;
+  virtual void free();
+  virtual ~OSMetaClassBase(){};
+};
+
+struct OSObject : public OSMetaClassBase {
+  virtual ~OSObject(){}
+
+  unsigned int foo() { return 42; }
+
+  virtual OS_RETURNS_NOT_RETAINED OSObject *identity();
+
+  static OSObject *generateObject(int);
+
+  static OSObject *getObject();
+  static OSObject *GetObject();
+
+  static void * operator new(size_t size);
+
+  static const OSMetaClass * const metaClass;
+};
+
+struct OSMetaClass : public OSMetaClassBase {
+  virtual OSObject * alloc();
+  virtual ~OSMetaClass(){}
+};
+
+#endif /* _OS_BASE_H */

Added: cfe/trunk/test/Analysis/os_smart_ptr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/os_smart_ptr.h?rev=351508&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/os_smart_ptr.h (added)
+++ cfe/trunk/test/Analysis/os_smart_ptr.h Thu Jan 17 19:12:35 2019
@@ -0,0 +1,89 @@
+#ifndef _OS_SMART_POINTER_H
+#define _OS_SMART_POINTER_H
+
+#include "os_object_base.h"
+
+namespace os {
+
+template<class T>
+struct smart_ptr {
+  smart_ptr() : pointer(nullptr) {}
+
+  explicit smart_ptr(T *&p) : pointer(p) {
+    if (pointer) {
+      _retain(pointer);
+    }
+  }
+
+  smart_ptr(smart_ptr const &rhs) : pointer(rhs.pointer) {
+    if (pointer) {
+      _retain(pointer);
+    }
+  }
+
+  smart_ptr & operator=(T *&rhs) {
+    smart_ptr(rhs).swap(*this);
+    return *this;
+  }
+
+  smart_ptr & operator=(smart_ptr &rhs) {
+    smart_ptr(rhs).swap(*this);
+    return *this;
+  }
+
+  ~smart_ptr() {
+    if (pointer) {
+      _release(pointer);
+    }
+  }
+
+  void reset() {
+    smart_ptr().swap(*this);
+  }
+
+  T *get() const {
+    return pointer;
+  }
+
+  T ** get_for_out_param() {
+    reset();
+    return &pointer;
+  }
+
+  T * operator->() const {
+    OSPTR_LOG("Dereference smart_ptr with %p\n", pointer);
+    return pointer;
+  }
+
+  explicit
+  operator bool() const {
+    return pointer != nullptr;
+  }
+
+  inline void
+  swap(smart_ptr &p) {
+    T *temp = pointer;
+    pointer = p.pointer;
+    p.pointer = temp;
+  }
+
+  static inline void
+  _retain(T *obj) {
+    obj->retain();
+  }
+
+  static inline void
+  _release(T *obj) {
+    obj->release();
+  }
+
+  static inline T *
+  _alloc() {
+    return new T;
+  }
+
+  T *pointer;
+};
+}
+
+#endif /* _OS_SMART_POINTER_H */

Modified: cfe/trunk/test/Analysis/osobject-retain-release.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/osobject-retain-release.cpp?rev=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/osobject-retain-release.cpp (original)
+++ cfe/trunk/test/Analysis/osobject-retain-release.cpp Thu Jan 17 19:12:35 2019
@@ -1,44 +1,10 @@
 // RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\
 // RUN:                    -analyzer-checker=core,osx -verify %s
 
-struct OSMetaClass;
-
-#define OS_CONSUME __attribute__((os_consumed))
-#define OS_RETURNS_RETAINED __attribute__((os_returns_retained))
-#define OS_RETURNS_RETAINED_ON_ZERO __attribute__((os_returns_retained_on_zero))
-#define OS_RETURNS_RETAINED_ON_NONZERO __attribute__((os_returns_retained_on_non_zero))
-#define OS_RETURNS_NOT_RETAINED __attribute__((os_returns_not_retained))
-#define OS_CONSUMES_THIS __attribute__((os_consumes_this))
-
-#define OSTypeID(type)   (type::metaClass)
-
-#define OSDynamicCast(type, inst)   \
-    ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
-
-using size_t = decltype(sizeof(int));
-
-struct OSObject {
-  virtual void retain();
-  virtual void release() {};
-  virtual void free();
-  virtual ~OSObject(){}
-
-  unsigned int foo() { return 42; }
-
-  virtual OS_RETURNS_NOT_RETAINED OSObject *identity();
-
-  static OSObject *generateObject(int);
-
-  static OSObject *getObject();
-  static OSObject *GetObject();
-
-  static void * operator new(size_t size);
-
-  static const OSMetaClass * const metaClass;
-};
+#include "os_object_base.h"
+#include "os_smart_ptr.h"
 
 struct OSIterator : public OSObject {
-
   static const OSMetaClass * const metaClass;
 };
 
@@ -88,9 +54,6 @@ struct OtherStruct {
   OtherStruct(OSArray *arr);
 };
 
-struct OSMetaClassBase {
-  static OSObject *safeMetaCast(const OSObject *inst, const OSMetaClass *meta);
-};
 
 void escape(void *);
 void escape_with_source(void *p) {}
@@ -616,3 +579,51 @@ typedef bool (^Blk)(OSObject *);
 void test_escape_to_unknown_block(Blk blk) {
   blk(getObject()); // no-crash
 }
+
+using OSObjectPtr = os::smart_ptr<OSObject>;
+
+void test_smart_ptr_uaf() {
+  OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
+  {
+    OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
+   // expected-note at -1{{Returning from constructor for 'smart_ptr<OSObject>'}}
+    // expected-note at os_smart_ptr.h:13{{Taking true branch}}
+    // expected-note at os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
+    // expected-note at os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}}
+    // expected-note at os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
+  } // expected-note{{Calling '~smart_ptr'}}
+  // expected-note at os_smart_ptr.h:35{{Taking true branch}}
+  // expected-note at os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
+  // expected-note at os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}}
+  // expected-note at os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
+ // expected-note at -5{{Returning from '~smart_ptr'}}
+  obj->release(); // expected-note{{Object released}}
+  obj->release(); // expected-warning{{Reference-counted object is used after it is released}}
+// expected-note at -1{{Reference-counted object is used after it is released}}
+}
+
+void test_smart_ptr_leak() {
+  OSObject *obj = new OSObject; // expected-note{{Operator 'new' returns an OSObject of type 'OSObject' with a +1 retain count}}
+  {
+    OSObjectPtr p(obj); // expected-note{{Calling constructor for 'smart_ptr<OSObject>'}}
+   // expected-note at -1{{Returning from constructor for 'smart_ptr<OSObject>'}}
+    // expected-note at os_smart_ptr.h:13{{Taking true branch}}
+    // expected-note at os_smart_ptr.h:14{{Calling 'smart_ptr::_retain'}}
+    // expected-note at os_smart_ptr.h:72{{Reference count incremented. The object now has a +2 retain count}}
+    // expected-note at os_smart_ptr.h:14{{Returning from 'smart_ptr::_retain'}}
+  } // expected-note{{Calling '~smart_ptr'}}
+  // expected-note at os_smart_ptr.h:35{{Taking true branch}}
+  // expected-note at os_smart_ptr.h:36{{Calling 'smart_ptr::_release'}}
+  // expected-note at os_smart_ptr.h:77{{Reference count decremented. The object now has a +1 retain count}}
+  // expected-note at os_smart_ptr.h:36{{Returning from 'smart_ptr::_release'}}
+ // expected-note at -5{{Returning from '~smart_ptr'}}
+} // expected-warning{{Potential leak of an object stored into 'obj'}}
+// expected-note at -1{{Object leaked: object allocated and stored into 'obj' is not referenced later in this execution path and has a retain count of +1}}
+
+void test_smart_ptr_no_leak() {
+  OSObject *obj = new OSObject;
+  {
+    OSObjectPtr p(obj);
+  }
+  obj->release();
+}

Modified: cfe/trunk/test/Analysis/test-separate-retaincount.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/test-separate-retaincount.cpp?rev=351508&r1=351507&r2=351508&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/test-separate-retaincount.cpp (original)
+++ cfe/trunk/test/Analysis/test-separate-retaincount.cpp Thu Jan 17 19:12:35 2019
@@ -2,6 +2,8 @@
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-disable-checker osx.OSObjectRetainCount -DNO_OS_OBJECT -verify %s
 // RUN: %clang_analyze_cc1 -analyzer-checker=core,osx -analyzer-config "osx.cocoa.RetainCount:CheckOSObject=false" -DNO_OS_OBJECT -verify %s
 
+#include "os_object_base.h"
+
 typedef const void * CFTypeRef;
 extern CFTypeRef CFRetain(CFTypeRef cf);
 extern void CFRelease(CFTypeRef cf);
@@ -11,14 +13,6 @@ extern CFTypeRef CFCreate() CF_RETURNS_R
 
 using size_t = decltype(sizeof(int));
 
-struct OSObject {
-  virtual void retain();
-  virtual void release();
-
-  static void * operator new(size_t size);
-  virtual ~OSObject(){}
-};
-
 void cf_overrelease() {
   CFTypeRef cf = CFCreate();
   CFRelease(cf);




More information about the cfe-commits mailing list