diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1730,6 +1730,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]>; diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -216,6 +216,9 @@ /// Check if the memory associated with this symbol was released. static bool isReleased(SymbolRef Sym, CheckerContext &C); +/// Check if the memory associated with this symbol is using reference counting +static bool isReferenceCountedAttributed(SymbolRef Sym); + /// 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. @@ -1960,7 +1963,8 @@ // Check for double free first. if ((RsBase->isReleased() || RsBase->isRelinquished()) && - !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) { + !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol) && + !isReferenceCountedAttributed(SymBase)) { HandleDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(), SymBase, PreviousRetStatusSymbol); return nullptr; @@ -2956,6 +2960,25 @@ return (RS && RS->isReleased()); } +static bool isReferenceCountedAttributed(SymbolRef Sym) { + QualType QT = Sym->getType(); + + if (QT.isNull()) + return false; + + QualType PT = QT->getPointeeType(); + if (PT.isNull()) + return false; + + Decl *RD = PT->getAsRecordDecl(); + + if (!RD) + return false; + + bool result = RD->hasAttr(); + return result; +} + bool MallocChecker::suppressDeallocationsInSuspiciousContexts( const CallEvent &Call, CheckerContext &C) const { if (Call.getNumArgs() == 0) @@ -2988,6 +3011,10 @@ bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const { + if (isReferenceCountedAttributed(Sym)) { + return false; + } + if (isReleased(Sym, C)) { HandleUseAfterFree(C, S->getSourceRange(), Sym); return true; diff --git a/clang/test/Analysis/malloc-annotations.c b/clang/test/Analysis/malloc-annotations.c --- a/clang/test/Analysis/malloc-annotations.c +++ b/clang/test/Analysis/malloc-annotations.c @@ -30,6 +30,16 @@ }; struct stuff myglobalstuff; +struct __attribute((reference_counted)) AnnotatedRefCountedStruct { + int mockRefCount; +}; + +typedef struct AnnotatedRefCountedStruct TypeDefAnnotatedRefCountedStruct; + +struct AnnotatedRefCountedStruct *CreateAnnotatedRefCountedStruct(void); + +TypeDefAnnotatedRefCountedStruct *CreateTypeDefAnnotatedRefCountedStruct(void); + void f1() { int *p = malloc(12); return; // expected-warning{{Potential leak of memory pointed to by}} @@ -273,3 +283,23 @@ my_freeBoth(p, q); } +struct AnnotatedRefCountedStruct *test_AnnotatedRefCountedStruct_ignores_use_after_free() { + struct AnnotatedRefCountedStruct *p = CreateAnnotatedRefCountedStruct(); + my_free((int *)p); + + return p; +} + +TypeDefAnnotatedRefCountedStruct *test_TypeDefAnnotatedRefCountedStruct_ignores_use_after_free() { + TypeDefAnnotatedRefCountedStruct *p = CreateTypeDefAnnotatedRefCountedStruct(); + my_free((int *)p); + + return p; +} + +void test_TypeDefAnnotatedRefCountedStruct_ignores_double_free() { + TypeDefAnnotatedRefCountedStruct *p = CreateTypeDefAnnotatedRefCountedStruct(); + + my_free((int *)p); + my_free((int *)p); +}