Skip to content

Commit 25e2480

Browse files
authored
Merge pull request #12008 from rniwa/merge-webkit-2025-12-15
Merge webkit 2025-12-15
2 parents 729ee78 + e7887d2 commit 25e2480

27 files changed

+383
-144
lines changed

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

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,6 @@ bool tryToFindPtrOrigin(
132132
}
133133
}
134134

135-
if (call->isCallToStdMove() && call->getNumArgs() == 1) {
136-
E = call->getArg(0)->IgnoreParenCasts();
137-
continue;
138-
}
139-
140135
if (auto *callee = call->getDirectCallee()) {
141136
if (isCtorOfSafePtr(callee)) {
142137
if (StopAtFirstRefCountedObj)
@@ -146,6 +141,11 @@ bool tryToFindPtrOrigin(
146141
continue;
147142
}
148143

144+
if (isStdOrWTFMove(callee) && call->getNumArgs() == 1) {
145+
E = call->getArg(0)->IgnoreParenCasts();
146+
continue;
147+
}
148+
149149
if (isSafePtrType(callee->getReturnType()))
150150
return callback(E, true);
151151

@@ -321,6 +321,51 @@ bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E) {
321321
return result && *result;
322322
}
323323

324+
bool isAllocInit(const Expr *E, const Expr **InnerExpr) {
325+
auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(E);
326+
if (auto *POE = dyn_cast<PseudoObjectExpr>(E)) {
327+
if (unsigned ExprCount = POE->getNumSemanticExprs()) {
328+
auto *Expr = POE->getSemanticExpr(ExprCount - 1)->IgnoreParenCasts();
329+
ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Expr);
330+
if (InnerExpr)
331+
*InnerExpr = ObjCMsgExpr;
332+
}
333+
}
334+
if (!ObjCMsgExpr)
335+
return false;
336+
auto Selector = ObjCMsgExpr->getSelector();
337+
auto NameForFirstSlot = Selector.getNameForSlot(0);
338+
if (NameForFirstSlot.starts_with("alloc") ||
339+
NameForFirstSlot.starts_with("copy") ||
340+
NameForFirstSlot.starts_with("mutableCopy"))
341+
return true;
342+
if (!NameForFirstSlot.starts_with("init") &&
343+
!NameForFirstSlot.starts_with("_init"))
344+
return false;
345+
if (!ObjCMsgExpr->isInstanceMessage())
346+
return false;
347+
auto *Receiver = ObjCMsgExpr->getInstanceReceiver();
348+
if (!Receiver)
349+
return false;
350+
Receiver = Receiver->IgnoreParenCasts();
351+
if (auto *Inner = dyn_cast<ObjCMessageExpr>(Receiver)) {
352+
if (InnerExpr)
353+
*InnerExpr = Inner;
354+
auto InnerSelector = Inner->getSelector();
355+
return InnerSelector.getNameForSlot(0).starts_with("alloc");
356+
} else if (auto *CE = dyn_cast<CallExpr>(Receiver)) {
357+
if (InnerExpr)
358+
*InnerExpr = CE;
359+
if (auto *Callee = CE->getDirectCallee()) {
360+
if (Callee->getDeclName().isIdentifier()) {
361+
auto CalleeName = Callee->getName();
362+
return CalleeName.starts_with("alloc");
363+
}
364+
}
365+
}
366+
return false;
367+
}
368+
324369
class EnsureFunctionVisitor
325370
: public ConstStmtVisitor<EnsureFunctionVisitor, bool> {
326371
public:

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ bool isConstOwnerPtrMemberExpr(const clang::Expr *E);
7777
/// supports CheckedPtr.
7878
bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E);
7979

80+
/// \returns true if \p E is a [[alloc] init] pattern expression.
81+
/// Sets \p InnerExpr to the inner function call or selector invocation.
82+
bool isAllocInit(const Expr *E, const Expr **InnerExpr = nullptr);
83+
8084
/// \returns true if E is a CXXMemberCallExpr which returns a const smart
8185
/// pointer type.
8286
class EnsureFunctionAnalysis {

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,11 @@ class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
266266
while (ArgExpr) {
267267
ArgExpr = ArgExpr->IgnoreParenCasts();
268268
if (auto *InnerCE = dyn_cast<CallExpr>(ArgExpr)) {
269-
auto *InnerCallee = InnerCE->getDirectCallee();
270-
if (InnerCallee && InnerCallee->isInStdNamespace() &&
271-
safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
272-
ArgExpr = InnerCE->getArg(0);
273-
continue;
269+
if (auto *InnerCallee = InnerCE->getDirectCallee()) {
270+
if (isStdOrWTFMove(InnerCallee) && InnerCE->getNumArgs() == 1) {
271+
ArgExpr = InnerCE->getArg(0);
272+
continue;
273+
}
274274
}
275275
}
276276
if (auto *UO = dyn_cast<UnaryOperator>(ArgExpr)) {

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

Lines changed: 22 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,18 @@ bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
185185
isCtorOfRetainPtrOrOSPtr(F);
186186
}
187187

188+
bool isStdOrWTFMove(const clang::FunctionDecl *F) {
189+
auto FnName = safeGetName(F);
190+
auto *Namespace = F->getParent();
191+
if (!Namespace)
192+
return false;
193+
auto *TUDeck = Namespace->getParent();
194+
if (!isa_and_nonnull<TranslationUnitDecl>(TUDeck))
195+
return false;
196+
auto NsName = safeGetName(Namespace);
197+
return (NsName == "WTF" || NsName == "std") && FnName == "move";
198+
}
199+
188200
template <typename Predicate>
189201
static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
190202
QualType type = T;
@@ -288,37 +300,6 @@ bool RetainTypeChecker::isUnretained(const QualType QT, bool ignoreARC) {
288300
return RT && CFPointees.contains(RT);
289301
}
290302

291-
std::optional<bool> isUnretained(const QualType T, bool IsARCEnabled) {
292-
if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(T)) {
293-
if (auto *Decl = Subst->getAssociatedDecl()) {
294-
if (isRetainPtrOrOSPtr(safeGetName(Decl)))
295-
return false;
296-
}
297-
}
298-
if ((ento::cocoa::isCocoaObjectRef(T) && !IsARCEnabled) ||
299-
ento::coreFoundation::isCFObjectRef(T))
300-
return true;
301-
302-
// RetainPtr strips typedef for CF*Ref. Manually check for struct __CF* types.
303-
auto CanonicalType = T.getCanonicalType();
304-
auto *Type = CanonicalType.getTypePtrOrNull();
305-
if (!Type)
306-
return false;
307-
auto Pointee = Type->getPointeeType();
308-
auto *PointeeType = Pointee.getTypePtrOrNull();
309-
if (!PointeeType)
310-
return false;
311-
auto *Record = PointeeType->getAsStructureType();
312-
if (!Record)
313-
return false;
314-
auto *Decl = Record->getDecl();
315-
if (!Decl)
316-
return false;
317-
auto TypeName = Decl->getName();
318-
return TypeName.starts_with("__CF") || TypeName.starts_with("__CG") ||
319-
TypeName.starts_with("__CM");
320-
}
321-
322303
std::optional<bool> isUncounted(const CXXRecordDecl* Class)
323304
{
324305
// Keep isRefCounted first as it's cheaper.
@@ -354,25 +335,6 @@ std::optional<bool> isUncheckedPtr(const QualType T) {
354335
return false;
355336
}
356337

357-
std::optional<bool> isUnsafePtr(const QualType T, bool IsArcEnabled) {
358-
if (T->isPointerType() || T->isReferenceType()) {
359-
if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
360-
auto isUncountedPtr = isUncounted(CXXRD);
361-
auto isUncheckedPtr = isUnchecked(CXXRD);
362-
auto isUnretainedPtr = isUnretained(T, IsArcEnabled);
363-
std::optional<bool> result;
364-
if (isUncountedPtr)
365-
result = *isUncountedPtr;
366-
if (isUncheckedPtr)
367-
result = result ? *result || *isUncheckedPtr : *isUncheckedPtr;
368-
if (isUnretainedPtr)
369-
result = result ? *result || *isUnretainedPtr : *isUnretainedPtr;
370-
return result;
371-
}
372-
}
373-
return false;
374-
}
375-
376338
std::optional<bool> isGetterOfSafePtr(const CXXMethodDecl *M) {
377339
assert(M);
378340

@@ -574,6 +536,15 @@ class TrivialFunctionAnalysisVisitor
574536
});
575537
}
576538

539+
bool IsStatementTrivial(const Stmt *S) {
540+
auto CacheIt = Cache.find(S);
541+
if (CacheIt != Cache.end())
542+
return CacheIt->second;
543+
bool Result = Visit(S);
544+
Cache[S] = Result;
545+
return Result;
546+
}
547+
577548
bool VisitStmt(const Stmt *S) {
578549
// All statements are non-trivial unless overriden later.
579550
// Don't even recurse into children by default.
@@ -877,9 +848,7 @@ bool TrivialFunctionAnalysis::isTrivialImpl(
877848
bool TrivialFunctionAnalysis::isTrivialImpl(
878849
const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache) {
879850
TrivialFunctionAnalysisVisitor V(Cache);
880-
bool Result = V.Visit(S);
881-
assert(Cache.contains(S) && "Top-level statement not properly cached!");
882-
return Result;
851+
return V.IsStatementTrivial(S);
883852
}
884853

885854
} // namespace clang

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

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,6 @@ class RetainTypeChecker {
8787
bool defaultSynthProperties() const { return DefaultSynthProperties; }
8888
};
8989

90-
/// \returns true if \p Class is NS or CF objects AND not retained, false if
91-
/// not, std::nullopt if inconclusive.
92-
std::optional<bool> isUnretained(const clang::QualType T, bool IsARCEnabled);
93-
9490
/// \returns true if \p Class is ref-countable AND not ref-counted, false if
9591
/// not, std::nullopt if inconclusive.
9692
std::optional<bool> isUncounted(const clang::CXXRecordDecl* Class);
@@ -107,10 +103,6 @@ std::optional<bool> isUncountedPtr(const clang::QualType T);
107103
/// class, false if not, std::nullopt if inconclusive.
108104
std::optional<bool> isUncheckedPtr(const clang::QualType T);
109105

110-
/// \returns true if \p T is either a raw pointer or reference to an uncounted
111-
/// or unchecked class, false if not, std::nullopt if inconclusive.
112-
std::optional<bool> isUnsafePtr(const QualType T, bool IsArcEnabled);
113-
114106
/// \returns true if \p T is a RefPtr, Ref, CheckedPtr, CheckedRef, or its
115107
/// variant, false if not.
116108
bool isRefOrCheckedPtrType(const clang::QualType T);
@@ -134,6 +126,9 @@ bool isCtorOfCheckedPtr(const clang::FunctionDecl *F);
134126
/// uncounted parameter, false if not.
135127
bool isCtorOfSafePtr(const clang::FunctionDecl *F);
136128

129+
/// \returns true if \p F is std::move or WTF::move.
130+
bool isStdOrWTFMove(const clang::FunctionDecl *F);
131+
137132
/// \returns true if \p Name is RefPtr, Ref, or its variant, false if not.
138133
bool isRefType(const std::string &Name);
139134

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,11 @@ class RawPtrRefCallArgsChecker
177177
if (BR->getSourceManager().isInSystemHeader(E->getExprLoc()))
178178
return;
179179

180-
auto Selector = E->getSelector();
181180
if (auto *Receiver = E->getInstanceReceiver()) {
182181
std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType());
183182
if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
184-
if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
185-
auto InnerSelector = InnerMsg->getSelector();
186-
if (InnerSelector.getNameForSlot(0) == "alloc" &&
187-
Selector.getNameForSlot(0).starts_with("init"))
188-
return;
189-
}
183+
if (isAllocInit(E))
184+
return;
190185
reportBugOnReceiver(Receiver, D);
191186
}
192187
}

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,17 @@ class RawPtrRefLambdaCapturesChecker
343343
auto *Callee = CE->getCallee();
344344
if (!Callee)
345345
return;
346+
Callee = Callee->IgnoreParenCasts();
347+
if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Callee)) {
348+
Callee = MTE->getSubExpr();
349+
if (!Callee)
350+
return;
351+
Callee = Callee->IgnoreParenCasts();
352+
}
353+
if (auto *L = dyn_cast<LambdaExpr>(Callee)) {
354+
LambdasToIgnore.insert(L); // Calling a lambda upon creation is safe.
355+
return;
356+
}
346357
auto *DRE = dyn_cast<DeclRefExpr>(Callee->IgnoreParenCasts());
347358
if (!DRE)
348359
return;
@@ -416,12 +427,9 @@ class RawPtrRefLambdaCapturesChecker
416427
return false;
417428
}
418429
if (auto *CE = dyn_cast<CallExpr>(Arg)) {
419-
if (CE->isCallToStdMove() && CE->getNumArgs() == 1) {
420-
Arg = CE->getArg(0)->IgnoreParenCasts();
421-
continue;
422-
}
423430
if (auto *Callee = CE->getDirectCallee()) {
424-
if (isCtorOfSafePtr(Callee) && CE->getNumArgs() == 1) {
431+
if ((isStdOrWTFMove(Callee) || isCtorOfSafePtr(Callee)) &&
432+
CE->getNumArgs() == 1) {
425433
Arg = CE->getArg(0)->IgnoreParenCasts();
426434
continue;
427435
}
@@ -587,6 +595,8 @@ class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
587595
}
588596

589597
std::optional<bool> isUnsafePtr(QualType QT) const final {
598+
if (QT.hasStrongOrWeakObjCLifetime())
599+
return false;
590600
return RTC->isUnretained(QT);
591601
}
592602

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,14 @@ class RawPtrRefLocalVarsChecker
234234
}
235235

236236
bool TraverseIfStmt(IfStmt *IS) override {
237+
if (IS->getConditionVariable()) {
238+
// This code currently does not explicitly check the "else" statement
239+
// since getConditionVariable returns nullptr when there is a
240+
// condition defined after ";" as in "if (auto foo = ~; !foo)". If
241+
// this semantics change, we should add an explicit check for "else".
242+
if (auto *Then = IS->getThen(); !Then || TFA.isTrivial(Then))
243+
return true;
244+
}
237245
if (!TFA.isTrivial(IS))
238246
return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);
239247
return true;
@@ -433,6 +441,8 @@ class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
433441
RTC = RetainTypeChecker();
434442
}
435443
std::optional<bool> isUnsafePtr(const QualType T) const final {
444+
if (T.hasStrongOrWeakObjCLifetime())
445+
return false;
436446
return RTC->isUnretained(T);
437447
}
438448
bool isSafePtr(const CXXRecordDecl *Record) const final {

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,11 @@ class RawPtrRefMemberChecker
231231
// "assign" property doesn't retain even under ARC so treat it as unsafe.
232232
bool ignoreARC =
233233
!PD->isReadOnly() && PD->getSetterKind() == ObjCPropertyDecl::Assign;
234+
bool IsWeak =
235+
PD->getPropertyAttributes() & ObjCPropertyAttribute::kind_weak;
236+
bool HasSafeAttr = PD->isRetaining() || IsWeak;
234237
auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC);
235-
return {IsUnsafePtr && *IsUnsafePtr && !PD->isRetaining(), PropType};
238+
return {IsUnsafePtr && *IsUnsafePtr && !HasSafeAttr, PropType};
236239
}
237240

238241
bool shouldSkipDecl(const RecordDecl *RD) const {
@@ -363,6 +366,8 @@ class NoUnretainedMemberChecker final : public RawPtrRefMemberChecker {
363366
}
364367

365368
std::optional<bool> isUnsafePtr(QualType QT, bool ignoreARC) const final {
369+
if (QT.hasStrongOrWeakObjCLifetime())
370+
return false;
366371
return RTC->isUnretained(QT, ignoreARC);
367372
}
368373

0 commit comments

Comments
 (0)