Skip to content

Commit 1127dd7

Browse files
authored
[WebKit Checkers] Treat a NS/CF global defined in a system header as a safe pointer origin (#161146)
1 parent 3984d19 commit 1127dd7

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
@@ -26,6 +26,7 @@ bool tryToFindPtrOrigin(
2626
const Expr *E, bool StopAtFirstRefCountedObj,
2727
std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,
2828
std::function<bool(const clang::QualType)> isSafePtrType,
29+
std::function<bool(const clang::Decl *)> isSafeGlobalDecl,
2930
std::function<bool(const clang::Expr *, bool)> callback) {
3031
while (E) {
3132
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
@@ -34,6 +35,8 @@ bool tryToFindPtrOrigin(
3435
auto IsImmortal = safeGetName(VD) == "NSApp";
3536
if (VD->hasGlobalStorage() && (IsImmortal || QT.isConstQualified()))
3637
return callback(E, true);
38+
if (VD->hasGlobalStorage() && isSafeGlobalDecl(VD))
39+
return callback(E, true);
3740
}
3841
}
3942
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
@@ -71,9 +74,11 @@ bool tryToFindPtrOrigin(
7174
}
7275
if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {
7376
return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,
74-
isSafePtr, isSafePtrType, callback) &&
77+
isSafePtr, isSafePtrType, isSafeGlobalDecl,
78+
callback) &&
7579
tryToFindPtrOrigin(Expr->getFalseExpr(), StopAtFirstRefCountedObj,
76-
isSafePtr, isSafePtrType, callback);
80+
isSafePtr, isSafePtrType, isSafeGlobalDecl,
81+
callback);
7782
}
7883
if (auto *cast = dyn_cast<CastExpr>(E)) {
7984
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
@@ -29,12 +29,12 @@ namespace {
2929
class RawPtrRefCallArgsChecker
3030
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
3131
BugType Bug;
32-
mutable BugReporter *BR;
3332

3433
TrivialFunctionAnalysis TFA;
3534
EnsureFunctionAnalysis EFA;
3635

3736
protected:
37+
mutable BugReporter *BR;
3838
mutable std::optional<RetainTypeChecker> RTC;
3939

4040
public:
@@ -46,6 +46,7 @@ class RawPtrRefCallArgsChecker
4646
virtual bool isSafePtr(const CXXRecordDecl *Record) const = 0;
4747
virtual bool isSafePtrType(const QualType type) const = 0;
4848
virtual bool isSafeExpr(const Expr *) const { return false; }
49+
virtual bool isSafeDecl(const Decl *) const { return false; }
4950
virtual const char *ptrKind() const = 0;
5051

5152
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -214,6 +215,7 @@ class RawPtrRefCallArgsChecker
214215
Arg, /*StopAtFirstRefCountedObj=*/true,
215216
[&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },
216217
[&](const clang::QualType T) { return isSafePtrType(T); },
218+
[&](const clang::Decl *D) { return isSafeDecl(D); },
217219
[&](const clang::Expr *ArgOrigin, bool IsSafe) {
218220
if (IsSafe)
219221
return true;
@@ -479,6 +481,11 @@ class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker {
479481
isa<ObjCMessageExpr>(E);
480482
}
481483

484+
bool isSafeDecl(const Decl *D) const final {
485+
// Treat NS/CF globals in system header as immortal.
486+
return BR->getSourceManager().isInSystemHeader(D->getLocation());
487+
}
488+
482489
const char *ptrKind() const final { return "unretained"; }
483490
};
484491

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,10 @@ bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
166166
class RawPtrRefLocalVarsChecker
167167
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
168168
BugType Bug;
169-
mutable BugReporter *BR;
170169
EnsureFunctionAnalysis EFA;
171170

172171
protected:
172+
mutable BugReporter *BR;
173173
mutable std::optional<RetainTypeChecker> RTC;
174174

175175
public:
@@ -180,6 +180,7 @@ class RawPtrRefLocalVarsChecker
180180
virtual bool isSafePtr(const CXXRecordDecl *) const = 0;
181181
virtual bool isSafePtrType(const QualType) const = 0;
182182
virtual bool isSafeExpr(const Expr *) const { return false; }
183+
virtual bool isSafeDecl(const Decl *) const { return false; }
183184
virtual const char *ptrKind() const = 0;
184185

185186
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -288,6 +289,7 @@ class RawPtrRefLocalVarsChecker
288289
return isSafePtr(Record);
289290
},
290291
[&](const clang::QualType Type) { return isSafePtrType(Type); },
292+
[&](const clang::Decl *D) { return isSafeDecl(D); },
291293
[&](const clang::Expr *InitArgOrigin, bool IsSafe) {
292294
if (!InitArgOrigin || IsSafe)
293295
return true;
@@ -443,6 +445,10 @@ class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
443445
return ento::cocoa::isCocoaObjectRef(E->getType()) &&
444446
isa<ObjCMessageExpr>(E);
445447
}
448+
bool isSafeDecl(const Decl *D) const final {
449+
// Treat NS/CF globals in system header as immortal.
450+
return BR->getSourceManager().isInSystemHeader(D->getLocation());
451+
}
446452
const char *ptrKind() const final { return "unretained"; }
447453
};
448454

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)