Skip to content

Commit 21dd8e6

Browse files
committed
[WebKit checkers] Treat the return value of an instance method as an unsafe pointer origin
In bf1d278, we started treating the return value of any selector invocation as safe. This isn't quite right since not every return value is autorelease'd. So start treating these as unsafe again for messages sent to an instance but keep treating safe for messages sent to a class since those should always be autorelease'd. This PR also moves the check from isSafeExpr in local variable and call arguments checkers to tryToFindPtrOrigin so that this semantic change could be more easily implemented. This PR also fixes RawPtrRefCallArgsChecker so that it recognizes alloc-init pattern even if allocWithZone or _init instead of alloc and init was used.
1 parent 86b89a6 commit 21dd8e6

File tree

7 files changed

+41
-12
lines changed

7 files changed

+41
-12
lines changed

clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ bool tryToFindPtrOrigin(
196196
if (isSafePtrType(Method->getReturnType()))
197197
return callback(E, true);
198198
}
199+
if (ObjCMsgExpr->isClassMessage())
200+
return callback(E, true);
199201
auto Selector = ObjCMsgExpr->getSelector();
200202
auto NameForFirstSlot = Selector.getNameForSlot(0);
201203
if ((NameForFirstSlot == "class" || NameForFirstSlot == "superclass") &&

clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,21 @@ class RawPtrRefCallArgsChecker
178178
return;
179179

180180
auto Selector = E->getSelector();
181+
auto SelName = Selector.getNameForSlot(0);
182+
bool IsSafeSel = SelName.starts_with("copy") || SelName.contains("Copy") ||
183+
SelName == "isEqual" || SelName == "isEqualToString";
184+
if (Selector.getNumArgs() <= 1 && IsSafeSel)
185+
return; // These selectors are assumed to be readonly.
186+
181187
if (auto *Receiver = E->getInstanceReceiver()) {
182188
std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType());
183189
if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
184-
if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
190+
if (auto *InnerMsg =
191+
dyn_cast<ObjCMessageExpr>(Receiver->IgnoreParenCasts())) {
185192
auto InnerSelector = InnerMsg->getSelector();
186-
if (InnerSelector.getNameForSlot(0) == "alloc" &&
187-
Selector.getNameForSlot(0).starts_with("init"))
193+
auto SelName = Selector.getNameForSlot(0);
194+
if (InnerSelector.getNameForSlot(0).starts_with("alloc") &&
195+
(SelName.starts_with("init") || SelName.starts_with("_init")))
188196
return;
189197
}
190198
reportBugOnReceiver(Receiver, D);
@@ -476,11 +484,6 @@ class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
476484
return isRetainPtrOrOSPtrType(type);
477485
}
478486

479-
bool isSafeExpr(const Expr *E) const final {
480-
return ento::cocoa::isCocoaObjectRef(E->getType()) &&
481-
isa<ObjCMessageExpr>(E);
482-
}
483-
484487
bool isSafeDecl(const Decl *D) const final {
485488
// Treat NS/CF globals in system header as immortal.
486489
return BR->getSourceManager().isInSystemHeader(D->getLocation());

clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,6 @@ class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
441441
bool isSafePtrType(const QualType type) const final {
442442
return isRetainPtrOrOSPtrType(type);
443443
}
444-
bool isSafeExpr(const Expr *E) const final {
445-
return ento::cocoa::isCocoaObjectRef(E->getType()) &&
446-
isa<ObjCMessageExpr>(E);
447-
}
448444
bool isSafeDecl(const Decl *D) const final {
449445
// Treat NS/CF globals in system header as immortal.
450446
return BR->getSourceManager().isInSystemHeader(D->getLocation());

clang/test/Analysis/Checkers/WebKit/objc-mock-types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ extern NSApplication * NSApp;
195195
@end
196196

197197
@interface SomeObj : NSObject
198+
+ (SomeObj *)sharedInstance;
198199
- (instancetype)_init;
199200
- (SomeObj *)mutableCopy;
200201
- (SomeObj *)copyWithValue:(int)value;

clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ void basic_correct() {
1515
auto ns6 = retainPtr([ns3 next]);
1616
auto ns7 = retainPtr((SomeObj *)0);
1717
auto ns8 = adoptNS(nil);
18+
auto ns9 = adoptNS([[SomeObj allocWithZone:nullptr] _init]);
1819
CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10));
1920
auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault));
2021
auto cf3 = adoptCF(checked_cf_cast<CFArrayRef>(CFCopyArray(cf1)));
@@ -28,6 +29,8 @@ void basic_wrong() {
2829
// expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
2930
auto ns2 = adoptNS([ns1.get() next]);
3031
// expected-warning@-1{{Incorrect use of adoptNS. The argument is +0 and results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}}
32+
RetainPtr<SomeObj> ns3 = [[SomeObj allocWithZone:nullptr] init];
33+
// expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
3134
RetainPtr<CFMutableArrayRef> cf1 = CFArrayCreateMutable(kCFAllocatorDefault, 10);
3235
// expected-warning@-1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
3336
RetainPtr<CFMutableArrayRef> cf2 = adoptCF(provide_cf());
@@ -50,6 +53,10 @@ @implementation SomeObj {
5053
SomeObj *_other;
5154
}
5255

56+
+ (SomeObj *)sharedInstance {
57+
return nil;
58+
}
59+
5360
- (instancetype)_init {
5461
self = [super init];
5562
_number = nil;

clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,15 @@ void foo() {
445445
void foo() {
446446
auto obj = adoptNS([[SomeObj alloc] init]);
447447
[obj doWork];
448+
auto obj2 = adoptNS([[SomeObj alloc] _init]);
449+
[obj2 doWork];
450+
}
451+
452+
void bar(NSZone *zone) {
453+
auto obj = adoptNS([[SomeObj allocWithZone:zone] init]);
454+
[obj doWork];
455+
auto obj2 = adoptNS([(SomeObj *)[SomeObj allocWithZone:zone] _init]);
456+
[obj2 doWork];
448457
}
449458
}
450459

@@ -582,6 +591,7 @@ @interface TestObject : NSObject
582591
- (void)doWork:(NSString *)msg, ...;
583592
- (void)doWorkOnSelf;
584593
- (SomeObj *)getSomeObj;
594+
+ (SomeObj *)sharedObj;
585595
@end
586596

587597
@implementation TestObject
@@ -606,8 +616,15 @@ - (SomeObj *)getSomeObj {
606616
return RetainPtr<SomeObj *>(provide()).autorelease();
607617
}
608618

619+
+ (SomeObj *)sharedObj
620+
{
621+
return adoptNS([[SomeObj alloc] init]).autorelease();
622+
}
623+
609624
- (void)doWorkOnSomeObj {
610625
[[self getSomeObj] doWork];
626+
// expected-warning@-1{{Receiver is unretained and unsafe}}
627+
[[TestObject sharedObj] doWork];
611628
}
612629

613630
- (CGImageRef)createImage {

clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,9 @@ - (SomeObj*)getSomeObj {
592592

593593
- (void)storeSomeObj {
594594
auto *obj = [self getSomeObj];
595+
// expected-warning@-1{{Local variable 'obj' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
595596
[obj doWork];
597+
auto *obj2 = [SomeObj sharedInstance];
598+
[obj2 doWork];
596599
}
597600
@end

0 commit comments

Comments
 (0)