malhar1995 updated this revision to Diff 106685. malhar1995 added a comment.
Suppresses false positives involving functions which perform reference counting. Added relevant test-cases to test/Analysis/retain-release-inline.m https://reviews.llvm.org/D34937 Files: lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp test/Analysis/retain-release-inline.m
Index: test/Analysis/retain-release-inline.m =================================================================== --- test/Analysis/retain-release-inline.m +++ test/Analysis/retain-release-inline.m @@ -12,7 +12,7 @@ // // It includes the basic definitions for the test cases below. //===----------------------------------------------------------------------===// - +#define NULL 0 typedef unsigned int __darwin_natural_t; typedef unsigned long uintptr_t; typedef unsigned int uint32_t; @@ -267,6 +267,10 @@ extern CFStringRef CFStringCreateWithCStringNoCopy(CFAllocatorRef alloc, const char *cStr, CFStringEncoding encoding, CFAllocatorRef contentsDeallocator); +typedef struct { + int ref; +} isl_basic_map; + //===----------------------------------------------------------------------===// // Test cases. //===----------------------------------------------------------------------===// @@ -285,6 +289,7 @@ foo(s); bar(s); } + void test_neg() { NSString *s = [[NSString alloc] init]; // no-warning foo(s); @@ -294,6 +299,56 @@ bar(s); } +__attribute__((cf_returns_retained)) isl_basic_map *isl_basic_map_cow(__attribute__((cf_consumed)) isl_basic_map *bmap); +void free(void *); + +// As 'isl_basic_map_free' is annotated with 'rc_ownership_trusted_implementation', RetainCountChecker trusts its +// implementation and doesn't analyze its body. If the annotation 'rc_ownership_trusted_implementation' is removed, +// a leak warning is raised by RetainCountChecker as the analyzer is unable to detect a decrement in the reference +// count of 'bmap' along the path in 'isl_basic_map_free' assuming the predicate of the second 'if' branch to be +// true or assuming both the predicates in the function to be false. +__attribute__((annotate("rc_ownership_trusted_implementation"))) isl_basic_map *isl_basic_map_free(__attribute__((cf_consumed)) isl_basic_map *bmap) { + if (!bmap) + return NULL; + + if (--bmap->ref > 0) + return NULL; + + free(bmap); + return NULL; +} + +// As 'isl_basic_map_copy' is annotated with 'rc_ownership_trusted_implementation', RetainCountChecker trusts its +// implementation and doesn't analyze its body. If that annotation is removed, a 'use-after-release' warning might +// be raised by RetainCountChecker as the pointer which is passed as an argument to this function and the pointer +// which is returned from the function point to the same memory location. +__attribute__((annotate("rc_ownership_trusted_implementation"))) __attribute__((cf_returns_retained)) isl_basic_map *isl_basic_map_copy(isl_basic_map *bmap) +{ + if (!bmap) + return NULL; + + bmap->ref++; + return bmap; +} + +void test_use_after_release_with_trusted_implementation_annotate_attribute(__attribute__((cf_consumed)) isl_basic_map *bmap) { + // After this call, 'bmap' has a +1 reference count. + bmap = isl_basic_map_cow(bmap); + // After the call to 'isl_basic_map_copy', 'bmap' has a +1 reference count. + isl_basic_map *temp = isl_basic_map_cow(isl_basic_map_copy(bmap)); + // After this call, 'bmap' has a +0 reference count. + isl_basic_map *temp2 = isl_basic_map_cow(bmap); // no-warning + isl_basic_map_free(temp2); + isl_basic_map_free(temp); +} + +void test_leak_with_trusted_implementation_annotate_attribute(__attribute__((cf_consumed)) isl_basic_map *bmap) { + // After this call, 'bmap' has a +1 reference count. + bmap = isl_basic_map_cow(bmap); // no-warning + // After this call, 'bmap' has a +0 reference count. + isl_basic_map_free(bmap); +} + //===----------------------------------------------------------------------===// // Test returning retained and not-retained values. //===----------------------------------------------------------------------===// Index: lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -1894,6 +1894,16 @@ return SFC->getAnalysisDeclContext()->isBodyAutosynthesized(); } +/// Returns true if the function declaration 'FD' contains +/// 'rc_ownership_trusted_implementation' annotate attribute. +bool isTrustedReferenceCountImplementation(const FunctionDecl *FD) { + for (const auto *Ann : FD->specific_attrs<AnnotateAttr>()) { + if (Ann->getAnnotation() == "rc_ownership_trusted_implementation") + return true; + } + return false; +} + std::shared_ptr<PathDiagnosticPiece> CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { @@ -3380,6 +3390,9 @@ // See if it's one of the specific functions we know how to eval. bool canEval = false; + // See if the function has 'rc_ownership_trusted_implementation' + // annotate attribute. + bool hasTrustedImplementationAnnotation = false; QualType ResultTy = CE->getCallReturnType(C.getASTContext()); if (ResultTy->isObjCIdType()) { @@ -3395,17 +3408,21 @@ cocoa::isRefType(ResultTy, "CV", FName)) { canEval = isRetain(FD, FName) || isAutorelease(FD, FName) || isMakeCollectable(FD, FName); - } + } else { + canEval = isTrustedReferenceCountImplementation(FD); + hasTrustedImplementationAnnotation = canEval; + } } if (!canEval) return false; // Bind the return value. const LocationContext *LCtx = C.getLocationContext(); SVal RetVal = state->getSVal(CE->getArg(0), LCtx); - if (RetVal.isUnknown()) { - // If the receiver is unknown, conjure a return value. + if (RetVal.isUnknown() || (hasTrustedImplementationAnnotation && !ResultTy.isNull())) { + // If the receiver is unknown or the function has 'rc_ownership_trusted_implementation' + // annotate attribute, conjure a return value. SValBuilder &SVB = C.getSValBuilder(); RetVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount()); } @@ -3422,7 +3439,7 @@ // Invalidate the argument region. state = state->invalidateRegions(ArgRegion, CE, C.blockCount(), LCtx, - /*CausesPointerEscape*/ false); + /*CausesPointerEscape*/ hasTrustedImplementationAnnotation); // Restore the refcount status of the argument. if (Binding)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits