Skip to content

Commit 8cd08a7

Browse files
committed
[WebKit Checkers] Treat a NS/CF global defined in a system header as a safe pointer origin (llvm#161146)
1 parent 68bc5ef commit 8cd08a7

File tree

7 files changed

+73
-4
lines changed

7 files changed

+73
-4
lines changed

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ bool tryToFindPtrOrigin(
2727
const Expr *E, bool StopAtFirstRefCountedObj,
2828
std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,
2929
std::function<bool(const clang::QualType)> isSafePtrType,
30+
std::function<bool(const clang::Decl *)> isSafeGlobalDecl,
3031
std::function<bool(const clang::Expr *, bool)> callback) {
3132
while (E) {
3233
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
@@ -35,6 +36,8 @@ bool tryToFindPtrOrigin(
3536
auto IsImmortal = safeGetName(VD) == "NSApp";
3637
if (VD->hasGlobalStorage() && (IsImmortal || QT.isConstQualified()))
3738
return callback(E, true);
39+
if (VD->hasGlobalStorage() && isSafeGlobalDecl(VD))
40+
return callback(E, true);
3841
}
3942
}
4043
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
@@ -72,9 +75,11 @@ bool tryToFindPtrOrigin(
7275
}
7376
if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {
7477
return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,
75-
isSafePtr, isSafePtrType, callback) &&
78+
isSafePtr, isSafePtrType, isSafeGlobalDecl,
79+
callback) &&
7680
tryToFindPtrOrigin(Expr->getFalseExpr(), StopAtFirstRefCountedObj,
77-
isSafePtr, isSafePtrType, callback);
81+
isSafePtr, isSafePtrType, isSafeGlobalDecl,
82+
callback);
7883
}
7984
if (auto *cast = dyn_cast<CastExpr>(E)) {
8085
if (StopAtFirstRefCountedObj) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ bool tryToFindPtrOrigin(
5656
const clang::Expr *E, bool StopAtFirstRefCountedObj,
5757
std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,
5858
std::function<bool(const clang::QualType)> isSafePtrType,
59+
std::function<bool(const clang::Decl *)> isSafeGlobalDecl,
5960
std::function<bool(const clang::Expr *, bool)> callback);
6061

6162
/// For \p E referring to a ref-countable/-counted pointer/reference we return

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ namespace {
3131
class RawPtrRefCallArgsChecker
3232
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
3333
BugType Bug;
34-
mutable BugReporter *BR;
3534

3635
TrivialFunctionAnalysis TFA;
3736
EnsureFunctionAnalysis EFA;
3837

3938
protected:
39+
mutable BugReporter *BR;
4040
mutable std::optional<RetainTypeChecker> RTC;
4141

4242
public:
@@ -48,6 +48,7 @@ class RawPtrRefCallArgsChecker
4848
virtual bool isSafePtr(const CXXRecordDecl *Record) const = 0;
4949
virtual bool isSafePtrType(const QualType type) const = 0;
5050
virtual bool isSafeExpr(const Expr *) const { return false; }
51+
virtual bool isSafeDecl(const Decl *) const { return false; }
5152
virtual const char *ptrKind() const = 0;
5253

5354
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -219,6 +220,7 @@ class RawPtrRefCallArgsChecker
219220
Arg, /*StopAtFirstRefCountedObj=*/true,
220221
[&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },
221222
[&](const clang::QualType T) { return isSafePtrType(T); },
223+
[&](const clang::Decl *D) { return isSafeDecl(D); },
222224
[&](const clang::Expr *ArgOrigin, bool IsSafe) {
223225
if (IsSafe)
224226
return true;
@@ -484,6 +486,11 @@ class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
484486
isa<ObjCMessageExpr>(E);
485487
}
486488

489+
bool isSafeDecl(const Decl *D) const final {
490+
// Treat NS/CF globals in system header as immortal.
491+
return BR->getSourceManager().isInSystemHeader(D->getLocation());
492+
}
493+
487494
const char *ptrKind() const final { return "unretained"; }
488495
};
489496

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,10 @@ bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
169169
class RawPtrRefLocalVarsChecker
170170
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
171171
BugType Bug;
172-
mutable BugReporter *BR;
173172
EnsureFunctionAnalysis EFA;
174173

175174
protected:
175+
mutable BugReporter *BR;
176176
mutable std::optional<RetainTypeChecker> RTC;
177177

178178
public:
@@ -183,6 +183,7 @@ class RawPtrRefLocalVarsChecker
183183
virtual bool isSafePtr(const CXXRecordDecl *) const = 0;
184184
virtual bool isSafePtrType(const QualType) const = 0;
185185
virtual bool isSafeExpr(const Expr *) const { return false; }
186+
virtual bool isSafeDecl(const Decl *) const { return false; }
186187
virtual const char *ptrKind() const = 0;
187188

188189
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -294,6 +295,7 @@ class RawPtrRefLocalVarsChecker
294295
return isSafePtr(Record);
295296
},
296297
[&](const clang::QualType Type) { return isSafePtrType(Type); },
298+
[&](const clang::Decl *D) { return isSafeDecl(D); },
297299
[&](const clang::Expr *InitArgOrigin, bool IsSafe) {
298300
if (!InitArgOrigin || IsSafe)
299301
return true;
@@ -449,6 +451,10 @@ class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
449451
return ento::cocoa::isCocoaObjectRef(E->getType()) &&
450452
isa<ObjCMessageExpr>(E);
451453
}
454+
bool isSafeDecl(const Decl *D) const final {
455+
// Treat NS/CF globals in system header as immortal.
456+
return BR->getSourceManager().isInSystemHeader(D->getLocation());
457+
}
452458
const char *ptrKind() const final { return "unretained"; }
453459
};
454460

clang/test/Analysis/Checkers/WebKit/mock-system-header.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,17 @@ void os_log_msg(os_log_t oslog, os_log_type_t type, const char *msg, ...);
3434

3535
typedef const struct __attribute__((objc_bridge(NSString))) __CFString * CFStringRef;
3636

37+
extern CFStringRef const kCFURLTagNamesKey;
38+
3739
#ifdef __OBJC__
3840
@class NSString;
3941
@interface SystemObject {
4042
NSString *ns_string;
4143
CFStringRef cf_string;
4244
}
4345
@end
46+
47+
typedef NSString *NSNotificationName;
48+
extern "C" NSNotificationName NSApplicationDidBecomeActiveNotification;
49+
4450
#endif

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedLocalVarsChecker -verify %s
22

33
#import "objc-mock-types.h"
4+
#import "mock-system-header.h"
45

56
void someFunction();
7+
extern "C" CFStringRef LocalGlobalCFString;
8+
extern "C" NSString *LocalGlobalNSString;
69

710
namespace raw_ptr {
811
void foo() {
@@ -547,6 +550,29 @@ void foo() {
547550

548551
} // autoreleased
549552

553+
namespace ns_global {
554+
555+
void consumeCFString(CFStringRef);
556+
void consumeNSString(NSString *);
557+
558+
void cf() {
559+
auto *str = kCFURLTagNamesKey;
560+
consumeCFString(str);
561+
auto *localStr = LocalGlobalCFString;
562+
// expected-warning@-1{{Local variable 'localStr' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
563+
consumeCFString(localStr);
564+
}
565+
566+
void ns() {
567+
auto *str = NSApplicationDidBecomeActiveNotification;
568+
consumeNSString(str);
569+
auto *localStr = LocalGlobalNSString;
570+
// expected-warning@-1{{Local variable 'localStr' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}}
571+
consumeNSString(localStr);
572+
}
573+
574+
}
575+
550576
bool doMoreWorkOpaque(OtherObj*);
551577
SomeObj* provide();
552578

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UnretainedCallArgsChecker -verify %s
2+
3+
#import "mock-types.h"
4+
#import "mock-system-header.h"
5+
6+
void consumeCFString(CFStringRef);
7+
extern "C" CFStringRef LocalGlobalCFString;
8+
void consumeNSString(NSString *);
9+
extern "C" NSString *LocalGlobalNSString;
10+
11+
void foo() {
12+
consumeCFString(kCFURLTagNamesKey);
13+
consumeCFString(LocalGlobalCFString);
14+
// expected-warning@-1{{Call argument is unretained and unsafe}}
15+
consumeNSString(NSApplicationDidBecomeActiveNotification);
16+
consumeNSString(LocalGlobalNSString);
17+
// expected-warning@-1{{Call argument is unretained and unsafe}}
18+
}

0 commit comments

Comments
 (0)