Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 6ac84e9

Browse files
committed
[analyzer] RetainCount: Ignore annotations on user-made CFRetain wrappers.
It is not uncommon for the users to make their own wrappers around CoreFoundation's CFRetain and CFRelease functions that are defensive against null references. In such cases CFRetain is often incorrectly marked as CF_RETURNS_RETAINED. Ignore said annotation and treat such wrappers similarly to the regular CFRetain. rdar://problem/31699502 Differential Revision: https://reviews.llvm.org/D38877 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@315736 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 3704be1 commit 6ac84e9

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,11 @@ RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
11701170
if (cocoa::isRefType(RetTy, "CF", FName)) {
11711171
if (isRetain(FD, FName)) {
11721172
S = getUnarySummary(FT, cfretain);
1173+
// CFRetain isn't supposed to be annotated. However, this may as well
1174+
// be a user-made "safe" CFRetain function that is incorrectly
1175+
// annotated as cf_returns_retained due to lack of better options.
1176+
// We want to ignore such annotation.
1177+
AllowAnnotations = false;
11731178
} else if (isAutorelease(FD, FName)) {
11741179
S = getUnarySummary(FT, cfautorelease);
11751180
// The headers use cf_consumed, but we can fully model CFAutorelease

test/Analysis/retain-release-safe.c

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.RetainCount -verify %s
2+
3+
#pragma clang arc_cf_code_audited begin
4+
typedef const void * CFTypeRef;
5+
extern CFTypeRef CFRetain(CFTypeRef cf);
6+
extern void CFRelease(CFTypeRef cf);
7+
#pragma clang arc_cf_code_audited end
8+
9+
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
10+
#define CF_CONSUMED __attribute__((cf_consumed))
11+
12+
extern CFTypeRef CFCreate() CF_RETURNS_RETAINED;
13+
14+
// A "safe" variant of CFRetain that doesn't crash when a null pointer is
15+
// retained. This is often defined by users in a similar manner. The
16+
// CF_RETURNS_RETAINED annotation is misleading here, because the function
17+
// is not supposed to return an object with a +1 retain count. Instead, it
18+
// is supposed to return an object with +(N+1) retain count, where N is
19+
// the original retain count of 'cf'. However, there is no good annotation
20+
// to use in this case, and it is pointless to provide such annotation
21+
// because the only use cases would be CFRetain and SafeCFRetain.
22+
// So instead we teach the analyzer to be able to accept such code
23+
// and ignore the misplaced annotation.
24+
CFTypeRef SafeCFRetain(CFTypeRef cf) CF_RETURNS_RETAINED {
25+
if (cf) {
26+
return CFRetain(cf);
27+
}
28+
return cf;
29+
}
30+
31+
// A "safe" variant of CFRelease that doesn't crash when a null pointer is
32+
// released. The CF_CONSUMED annotation seems reasonable here.
33+
void SafeCFRelease(CFTypeRef CF_CONSUMED cf) {
34+
if (cf)
35+
CFRelease(cf); // no-warning (when inlined)
36+
}
37+
38+
void escape(CFTypeRef cf);
39+
40+
void makeSureTestsWork() {
41+
CFTypeRef cf = CFCreate();
42+
CFRelease(cf);
43+
CFRelease(cf); // expected-warning{{Reference-counted object is used after it is released}}
44+
}
45+
46+
// Make sure we understand that the second SafeCFRetain doesn't return an
47+
// object with +1 retain count, which we won't be able to release twice.
48+
void falseOverrelease(CFTypeRef cf) {
49+
SafeCFRetain(cf);
50+
SafeCFRetain(cf);
51+
SafeCFRelease(cf);
52+
SafeCFRelease(cf); // no-warning after inlining this.
53+
}
54+
55+
// Regular CFRelease() should behave similarly.
56+
void sameWithNormalRelease(CFTypeRef cf) {
57+
SafeCFRetain(cf);
58+
SafeCFRetain(cf);
59+
CFRelease(cf);
60+
CFRelease(cf); // no-warning
61+
}
62+
63+
// Make sure we understand that the second SafeCFRetain doesn't return an
64+
// object with +1 retain count, which would no longer be owned by us after
65+
// it escapes to escape() and released once.
66+
void falseReleaseNotOwned(CFTypeRef cf) {
67+
SafeCFRetain(cf);
68+
SafeCFRetain(cf);
69+
escape(cf);
70+
SafeCFRelease(cf);
71+
SafeCFRelease(cf); // no-warning after inlining this.
72+
}

0 commit comments

Comments
 (0)