chrisdangelo updated this revision to Diff 394374.
chrisdangelo added a comment.
Herald added a subscriber: jdoerfert.

This change adds support for reference_counted attribute in 
pragma-attribute-supported-attributes-list.

This change edits comments describing function signatures for 
isReferenceCountedAttributed[...].


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D113622/new/

https://reviews.llvm.org/D113622

Files:
  clang/include/clang/Basic/Attr.td
  clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
  clang/test/Analysis/malloc-annotations.c
  clang/test/Misc/pragma-attribute-supported-attributes-list.test

Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -147,6 +147,7 @@
 // CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: PatchableFunctionEntry (SubjectMatchRule_function, SubjectMatchRule_objc_method)
 // CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union)
+// CHECK-NEXT: ReferenceCounted (SubjectMatchRule_record)
 // CHECK-NEXT: ReleaseHandle (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function)
 // CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function)
Index: clang/test/Analysis/malloc-annotations.c
===================================================================
--- clang/test/Analysis/malloc-annotations.c
+++ clang/test/Analysis/malloc-annotations.c
@@ -30,6 +30,44 @@
 };
 struct stuff myglobalstuff;
 
+struct BMockStruct {
+  int number;
+};
+
+struct AMockStruct {
+  struct BMockStruct bMockStruct;
+};
+
+struct __attribute__((reference_counted)) AnnotatedRefCountedStruct {
+  int mockRefCount;
+  struct AnnotatedRefCountedStruct *mockNext;
+  struct StructContainingAnnotatedRefCountedStruct *unannotatedStructPtr;
+  struct AMockStruct aMockStruct;
+};
+
+struct StructContainingAnnotatedRefCountedStruct {
+  struct AnnotatedRefCountedStruct *refCountedStructPtr;
+  void *opaquePtr;
+  struct AMockStruct aMockStruct;
+};
+
+void my_use_after_free_internal(void *p);
+
+void my_use_after_free_external(void *p) {
+  my_use_after_free_internal(p);
+}
+
+typedef struct AnnotatedRefCountedStruct TypeDefAnnotatedRefCountedStruct;
+struct AnnotatedRefCountedStruct *CreateAnnotatedRefCountedStruct(void);
+TypeDefAnnotatedRefCountedStruct *CreateTypeDefAnnotatedRefCountedStruct(void);
+struct StructContainingAnnotatedRefCountedStruct *CreateStructContainingAnnotatedRefCountedStruct(void);
+typedef struct __attribute__((reference_counted)) UnknownStruct TypeDefUnknownStruct;
+TypeDefUnknownStruct *CreateTypeDefUnknownStruct(void);
+typedef struct __attribute__((reference_counted)) UnknownStruct *TypeDefUnknownStructRef;
+TypeDefUnknownStructRef CreateTypeDefUnknownStructRef(void);
+void *CreateUntypedPointer(void);
+void __attribute__((ownership_takes(malloc, 1))) my_typed_free(struct AnnotatedRefCountedStruct *);
+
 void f1() {
   int *p = malloc(12);
   return; // expected-warning{{Potential leak of memory pointed to by}}
@@ -273,3 +311,179 @@
   my_freeBoth(p, q);
 }
 
+struct AnnotatedRefCountedStruct *testAnnotatedRefCountedStructIgnoresUseAfterFree() {
+  struct AnnotatedRefCountedStruct *p = CreateAnnotatedRefCountedStruct();
+  my_free(p);
+
+  return p;
+}
+
+TypeDefAnnotatedRefCountedStruct *testTypeDefAnnotatedRefCountedStructIgnoresUseAfterFree() {
+  TypeDefAnnotatedRefCountedStruct *p = CreateTypeDefAnnotatedRefCountedStruct();
+  my_free(p);
+
+  return p;
+}
+
+void testTypeDefAnnotatedRefCountedStructIgnoresDoubleFree() {
+  TypeDefAnnotatedRefCountedStruct *p = CreateTypeDefAnnotatedRefCountedStruct();
+
+  my_free(p);
+  my_free(p);
+}
+
+void testCompileTimeAnnotatedTypeIsSufficientToIgnoreDoubleFree() {
+  TypeDefAnnotatedRefCountedStruct *p = CreateUntypedPointer();
+
+  my_free(p);
+  my_free(p);
+}
+
+void testCompileTimeAnnotatedTypeAtUseSiteIsSufficientToIgnoreDoubleFree() {
+  void *p = CreateUntypedPointer();
+
+  my_free(p);
+  my_typed_free(p);
+}
+
+void testCompileTimeDoubleCastTypeCheckingIgnoresUseAfterFree() {
+  TypeDefAnnotatedRefCountedStruct *p = CreateUntypedPointer();
+
+  my_free(p);
+  my_use_after_free_external(p);
+}
+
+TypeDefUnknownStruct *testTypeDefUnkownStructIgnoresUseAfterFree() {
+  TypeDefUnknownStruct *p = CreateTypeDefUnknownStruct();
+
+  my_free(p);
+  return p;
+}
+
+TypeDefUnknownStructRef testTypeDefUnkownStructRefIgnoresUseAfterFree() {
+  TypeDefUnknownStructRef ref = CreateTypeDefUnknownStructRef();
+
+  my_free(ref);
+  return ref;
+}
+
+TypeDefUnknownStruct *testTypeDefUnkownStructFromArrayIgnoresUseAfterFree() {
+  TypeDefUnknownStruct *p[1] = {};
+  p[0] = CreateTypeDefUnknownStruct();
+
+  my_free(p[0]);
+  return p[0];
+}
+
+struct AnnotatedRefCountedStruct *testCreateUntypedPointerWhereCallsiteKnowsIgnoreUseAfterFree() {
+  struct AnnotatedRefCountedStruct *p = CreateUntypedPointer();
+  my_free(p);
+
+  return p;
+}
+
+struct AnnotatedRefCountedStruct *testCreateUntypedPointerWhereReturnTypeKnowsIgnoreUseAfterFree() {
+  void *p = CreateUntypedPointer();
+  my_typed_free(p);
+
+  return p;
+}
+
+TypeDefUnknownStructRef testCreateUntypedPointerWhereReturnTypeIsRefAndKnowsIgnoreUseAfterFree() {
+  void *p = CreateUntypedPointer();
+  my_free(p);
+
+  return p;
+}
+
+int testMemberExpressionTypedBaseIsUsedAsTypeToIgnoreUseAfterFree() {
+  struct AnnotatedRefCountedStruct *p = CreateAnnotatedRefCountedStruct();
+  my_free(p);
+
+  return p->mockRefCount;
+}
+
+struct AnnotatedRefCountedStruct *testUseAfterFreeIsIgnoredWhenEmbedded() {
+  struct StructContainingAnnotatedRefCountedStruct *p = CreateUntypedPointer();
+  p->refCountedStructPtr = CreateUntypedPointer();
+  my_free(p->refCountedStructPtr);
+  my_free(p);
+
+  return p->refCountedStructPtr; // expected-warning{{Use of memory after it is freed}}
+}
+
+int testMemberExpressionUntypedBaseIsUsedAsTypeToIgnoreUseAfterFree() {
+  struct AnnotatedRefCountedStruct *p = CreateUntypedPointer();
+  my_free(p);
+
+  return p->mockRefCount;
+}
+
+int testMemberExpressionTypedInitiatedMemberIsUsedAsTypeToIgnoreUseAfterFree() {
+  struct AnnotatedRefCountedStruct *p = CreateAnnotatedRefCountedStruct();
+  p->mockNext = CreateAnnotatedRefCountedStruct();
+  my_free(p->mockNext);
+
+  return p->mockNext->mockRefCount;
+}
+
+int testMemberExpressionUnTypedInitiedMemberIsUsedAsTypeToIgnoreUseAfterFree() {
+  struct AnnotatedRefCountedStruct *p = CreateUntypedPointer();
+  p->mockNext = CreateUntypedPointer();
+  my_free(p->mockNext);
+
+  return p->mockNext->mockRefCount;
+}
+
+struct StructContainingAnnotatedRefCountedStruct *testMemberExpressionIsUsedAsTypeToShowUseAfterFree() {
+  struct StructContainingAnnotatedRefCountedStruct *s = CreateStructContainingAnnotatedRefCountedStruct();
+  my_free(s);
+
+  return s; // expected-warning{{Use of memory after it is freed}}
+}
+
+void *testMemberExpressionIndicatesUseAfterFree() {
+  struct StructContainingAnnotatedRefCountedStruct *s = CreateStructContainingAnnotatedRefCountedStruct();
+  my_free(s);
+
+  return s->opaquePtr; // expected-warning{{Use of memory after it is freed}}
+}
+
+void *testUseAfterFreeIsIgnoredFromRootMemberExprBase() {
+  struct AnnotatedRefCountedStruct *p = CreateAnnotatedRefCountedStruct();
+  my_free(p);
+
+  return p->unannotatedStructPtr->opaquePtr;
+}
+
+int testUseAfterFreeIsDiscoveredFromRootMemberExprBaseWhenChainContainsRefCountedDereference() {
+  struct StructContainingAnnotatedRefCountedStruct *p = CreateUntypedPointer();
+  p->refCountedStructPtr = CreateUntypedPointer();
+  my_free(p->refCountedStructPtr);
+  my_free(p);
+
+  return p->refCountedStructPtr->mockRefCount; // expected-warning{{Use of memory after it is freed}}
+}
+
+void *testEmbeddedUseAfterFreeIsDiscovered() {
+  struct AnnotatedRefCountedStruct *p = CreateUntypedPointer();
+  p->mockNext = CreateUntypedPointer();
+  p->unannotatedStructPtr = CreateStructContainingAnnotatedRefCountedStruct();
+  my_free(p->mockNext->unannotatedStructPtr);
+
+  return p->mockNext->unannotatedStructPtr->opaquePtr; // expected-warning{{Use of memory after it is freed}}
+}
+
+int testChainedMemberExpressionsWithDotsDiscoversUseAfterFree() {
+  struct StructContainingAnnotatedRefCountedStruct *p = CreateStructContainingAnnotatedRefCountedStruct();
+  my_free(p);
+
+  return p->aMockStruct.bMockStruct.number; // expected-warning{{Use of memory after it is freed}}
+}
+
+int testChainedMemberExpressionsToReferenceCountedUntypedWithDotsIgnoresUseAfterFree() {
+  struct AnnotatedRefCountedStruct *p = CreateUntypedPointer();
+  my_free(p);
+
+  return p->aMockStruct.bMockStruct.number;
+}
\ No newline at end of file
Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -216,6 +216,18 @@
 /// Check if the memory associated with this symbol was released.
 static bool isReleased(SymbolRef Sym, CheckerContext &C);
 
+/// Check if the if the qualified type is declared as reference counted
+static bool isReferenceCountedAttributedQualType(QualType QT);
+
+/// Check if the if the value or expression is declared as reference counted
+static bool isReferenceCountedAttributed(SymbolRef Sym, const Stmt *S);
+
+/// Check if the if the valueis declared as reference counted
+static bool isReferenceCountedAttributedSymRef(SymbolRef Sym);
+
+/// Check if the if the value or expression is declared as reference counted
+static bool isReferenceCountedAttributedExpr(const Stmt *S);
+
 /// Update the RefState to reflect the new memory allocation.
 /// The optional \p RetVal parameter specifies the newly allocated pointer
 /// value; if unspecified, the value of expression \p E is used.
@@ -2956,6 +2968,45 @@
   return (RS && RS->isReleased());
 }
 
+static bool isReferenceCountedAttributedQualType(QualType QT) {
+  if (QT.isNull())
+    return false;
+
+  QualType PT = QT->getPointeeType();
+  if (PT.isNull())
+    return false;
+
+  Decl *RD = PT->getAsRecordDecl();
+  if (!RD)
+    return false;
+
+  return RD->hasAttr<ReferenceCountedAttr>();
+}
+
+static bool isReferenceCountedAttributedSymRef(SymbolRef Sym) {
+  QualType QT = Sym->getType();
+  if (QT.isNull())
+    return false;
+
+  return isReferenceCountedAttributedQualType(QT);
+}
+
+static bool isReferenceCountedAttributedExpr(const Stmt *S) {
+  const Expr *E = dyn_cast_or_null<Expr>(S);
+  if (!E)
+    return false;
+
+  QualType QT = E->getType();
+  return isReferenceCountedAttributedQualType(QT);
+}
+
+static bool isReferenceCountedAttributed(SymbolRef Sym, const Stmt *S) {
+  bool result = isReferenceCountedAttributedSymRef(Sym) ||
+                isReferenceCountedAttributedExpr(S);
+
+  return result;
+}
+
 bool MallocChecker::suppressDeallocationsInSuspiciousContexts(
     const CallEvent &Call, CheckerContext &C) const {
   if (Call.getNumArgs() == 0)
@@ -3335,6 +3386,16 @@
   const RefState *RSPrev = statePrev->get<RegionState>(Sym);
 
   const Stmt *S = N->getStmtForDiagnostics();
+
+  SymbolRef SR = nullptr;
+  const Expr *E = dyn_cast_or_null<Expr>(S);
+  if (E && E->isPRValue())
+    SR = N->getSVal(E).getAsSymbol();
+
+  if (SR == Sym)
+    if (isReferenceCountedAttributed(SR, S))
+      BR.markInvalid(getTag(), S);
+
   // When dealing with containers, we sometimes want to give a note
   // even if the statement is missing.
   if (!S && (!RSCurr || RSCurr->getAllocationFamily() != AF_InnerBuffer))
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -1694,6 +1694,13 @@
   let SimpleHandler = 1;
 }
 
+def ReferenceCounted : InheritableAttr {
+  let Spellings = [Clang<"reference_counted">];
+  let Subjects = SubjectList<[Record]>;
+  let Documentation = [Undocumented];
+  let SimpleHandler = 1;
+}
+
 def ReturnsTwice : InheritableAttr {
   let Spellings = [GCC<"returns_twice">];
   let Subjects = SubjectList<[Function]>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to