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]>; 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(); +} + +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(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(Sym); const Stmt *S = N->getStmtForDiagnostics(); + + SymbolRef SR = nullptr; + const Expr *E = dyn_cast_or_null(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/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/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)