xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //=======- PtrTypesSemantics.cpp ---------------------------------*- C++ -*-==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "PtrTypesSemantics.h"
10 #include "ASTUtils.h"
11 #include "clang/AST/CXXInheritance.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/AST/DeclCXX.h"
14 #include "clang/AST/ExprCXX.h"
15 #include "clang/AST/StmtVisitor.h"
16 #include <optional>
17 
18 using namespace clang;
19 
20 namespace {
21 
22 bool hasPublicMethodInBaseClass(const CXXRecordDecl *R,
23                                 const char *NameToMatch) {
24   assert(R);
25   assert(R->hasDefinition());
26 
27   for (const CXXMethodDecl *MD : R->methods()) {
28     const auto MethodName = safeGetName(MD);
29     if (MethodName == NameToMatch && MD->getAccess() == AS_public)
30       return true;
31   }
32   return false;
33 }
34 
35 } // namespace
36 
37 namespace clang {
38 
39 std::optional<const clang::CXXRecordDecl *>
40 hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch) {
41   assert(Base);
42 
43   const Type *T = Base->getType().getTypePtrOrNull();
44   if (!T)
45     return std::nullopt;
46 
47   const CXXRecordDecl *R = T->getAsCXXRecordDecl();
48   if (!R)
49     return std::nullopt;
50   if (!R->hasDefinition())
51     return std::nullopt;
52 
53   return hasPublicMethodInBaseClass(R, NameToMatch) ? R : nullptr;
54 }
55 
56 std::optional<bool> isRefCountable(const CXXRecordDecl* R)
57 {
58   assert(R);
59 
60   R = R->getDefinition();
61   if (!R)
62     return std::nullopt;
63 
64   bool hasRef = hasPublicMethodInBaseClass(R, "ref");
65   bool hasDeref = hasPublicMethodInBaseClass(R, "deref");
66   if (hasRef && hasDeref)
67     return true;
68 
69   CXXBasePaths Paths;
70   Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
71 
72   bool AnyInconclusiveBase = false;
73   const auto hasPublicRefInBase =
74       [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
75         auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
76         if (!hasRefInBase) {
77           AnyInconclusiveBase = true;
78           return false;
79         }
80         return (*hasRefInBase) != nullptr;
81       };
82 
83   hasRef = hasRef || R->lookupInBases(hasPublicRefInBase, Paths,
84                                       /*LookupInDependent =*/true);
85   if (AnyInconclusiveBase)
86     return std::nullopt;
87 
88   Paths.clear();
89   const auto hasPublicDerefInBase =
90       [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
91         auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
92         if (!hasDerefInBase) {
93           AnyInconclusiveBase = true;
94           return false;
95         }
96         return (*hasDerefInBase) != nullptr;
97       };
98   hasDeref = hasDeref || R->lookupInBases(hasPublicDerefInBase, Paths,
99                                           /*LookupInDependent =*/true);
100   if (AnyInconclusiveBase)
101     return std::nullopt;
102 
103   return hasRef && hasDeref;
104 }
105 
106 bool isRefType(const std::string &Name) {
107   return Name == "Ref" || Name == "RefAllowingPartiallyDestroyed" ||
108          Name == "RefPtr" || Name == "RefPtrAllowingPartiallyDestroyed";
109 }
110 
111 bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
112   assert(F);
113   const std::string &FunctionName = safeGetName(F);
114 
115   return isRefType(FunctionName) || FunctionName == "makeRef" ||
116          FunctionName == "makeRefPtr" || FunctionName == "UniqueRef" ||
117          FunctionName == "makeUniqueRef" ||
118          FunctionName == "makeUniqueRefWithoutFastMallocCheck"
119 
120          || FunctionName == "String" || FunctionName == "AtomString" ||
121          FunctionName == "UniqueString"
122          // FIXME: Implement as attribute.
123          || FunctionName == "Identifier";
124 }
125 
126 bool isReturnValueRefCounted(const clang::FunctionDecl *F) {
127   assert(F);
128   QualType type = F->getReturnType();
129   while (!type.isNull()) {
130     if (auto *elaboratedT = type->getAs<ElaboratedType>()) {
131       type = elaboratedT->desugar();
132       continue;
133     }
134     if (auto *specialT = type->getAs<TemplateSpecializationType>()) {
135       if (auto *decl = specialT->getTemplateName().getAsTemplateDecl()) {
136         auto name = decl->getNameAsString();
137         return isRefType(name);
138       }
139       return false;
140     }
141     return false;
142   }
143   return false;
144 }
145 
146 std::optional<bool> isUncounted(const CXXRecordDecl* Class)
147 {
148   // Keep isRefCounted first as it's cheaper.
149   if (isRefCounted(Class))
150     return false;
151 
152   std::optional<bool> IsRefCountable = isRefCountable(Class);
153   if (!IsRefCountable)
154     return std::nullopt;
155 
156   return (*IsRefCountable);
157 }
158 
159 std::optional<bool> isUncountedPtr(const Type* T)
160 {
161   assert(T);
162 
163   if (T->isPointerType() || T->isReferenceType()) {
164     if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
165       return isUncounted(CXXRD);
166     }
167   }
168   return false;
169 }
170 
171 std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M)
172 {
173   assert(M);
174 
175   if (isa<CXXMethodDecl>(M)) {
176     const CXXRecordDecl *calleeMethodsClass = M->getParent();
177     auto className = safeGetName(calleeMethodsClass);
178     auto method = safeGetName(M);
179 
180     if ((isRefType(className) && (method == "get" || method == "ptr")) ||
181         ((className == "String" || className == "AtomString" ||
182           className == "AtomStringImpl" || className == "UniqueString" ||
183           className == "UniqueStringImpl" || className == "Identifier") &&
184          method == "impl"))
185       return true;
186 
187     // Ref<T> -> T conversion
188     // FIXME: Currently allowing any Ref<T> -> whatever cast.
189     if (isRefType(className)) {
190       if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
191         if (auto *targetConversionType =
192                 maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) {
193           return isUncountedPtr(targetConversionType);
194         }
195       }
196     }
197   }
198   return false;
199 }
200 
201 bool isRefCounted(const CXXRecordDecl *R) {
202   assert(R);
203   if (auto *TmplR = R->getTemplateInstantiationPattern()) {
204     // FIXME: String/AtomString/UniqueString
205     const auto &ClassName = safeGetName(TmplR);
206     return isRefType(ClassName);
207   }
208   return false;
209 }
210 
211 bool isPtrConversion(const FunctionDecl *F) {
212   assert(F);
213   if (isCtorOfRefCounted(F))
214     return true;
215 
216   // FIXME: check # of params == 1
217   const auto FunctionName = safeGetName(F);
218   if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
219       FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
220       FunctionName == "checkedDowncast" ||
221       FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast")
222     return true;
223 
224   return false;
225 }
226 
227 bool isSingleton(const FunctionDecl *F) {
228   assert(F);
229   // FIXME: check # of params == 1
230   if (auto *MethodDecl = dyn_cast<CXXMethodDecl>(F)) {
231     if (!MethodDecl->isStatic())
232       return false;
233   }
234   const auto &Name = safeGetName(F);
235   std::string SingletonStr = "singleton";
236   auto index = Name.find(SingletonStr);
237   return index != std::string::npos &&
238          index == Name.size() - SingletonStr.size();
239 }
240 
241 // We only care about statements so let's use the simple
242 // (non-recursive) visitor.
243 class TrivialFunctionAnalysisVisitor
244     : public ConstStmtVisitor<TrivialFunctionAnalysisVisitor, bool> {
245 
246   // Returns false if at least one child is non-trivial.
247   bool VisitChildren(const Stmt *S) {
248     for (const Stmt *Child : S->children()) {
249       if (Child && !Visit(Child))
250         return false;
251     }
252 
253     return true;
254   }
255 
256   template <typename CheckFunction>
257   bool WithCachedResult(const Stmt *S, CheckFunction Function) {
258     // If the statement isn't in the cache, conservatively assume that
259     // it's not trivial until analysis completes. Insert false to the cache
260     // first to avoid infinite recursion.
261     auto [It, IsNew] = Cache.insert(std::make_pair(S, false));
262     if (!IsNew)
263       return It->second;
264     bool Result = Function();
265     Cache[S] = Result;
266     return Result;
267   }
268 
269 public:
270   using CacheTy = TrivialFunctionAnalysis::CacheTy;
271 
272   TrivialFunctionAnalysisVisitor(CacheTy &Cache) : Cache(Cache) {}
273 
274   bool IsFunctionTrivial(const Decl *D) {
275     auto CacheIt = Cache.find(D);
276     if (CacheIt != Cache.end())
277       return CacheIt->second;
278 
279     // Treat a recursive function call to be trivial until proven otherwise.
280     auto [RecursiveIt, IsNew] = RecursiveFn.insert(std::make_pair(D, true));
281     if (!IsNew)
282       return RecursiveIt->second;
283 
284     bool Result = [&]() {
285       if (auto *CtorDecl = dyn_cast<CXXConstructorDecl>(D)) {
286         for (auto *CtorInit : CtorDecl->inits()) {
287           if (!Visit(CtorInit->getInit()))
288             return false;
289         }
290       }
291       const Stmt *Body = D->getBody();
292       if (!Body)
293         return false;
294       return Visit(Body);
295     }();
296 
297     if (!Result) {
298       // D and its mutually recursive callers are all non-trivial.
299       for (auto &It : RecursiveFn)
300         It.second = false;
301     }
302     RecursiveIt = RecursiveFn.find(D);
303     assert(RecursiveIt != RecursiveFn.end());
304     Result = RecursiveIt->second;
305     RecursiveFn.erase(RecursiveIt);
306     Cache[D] = Result;
307 
308     return Result;
309   }
310 
311   bool VisitStmt(const Stmt *S) {
312     // All statements are non-trivial unless overriden later.
313     // Don't even recurse into children by default.
314     return false;
315   }
316 
317   bool VisitCompoundStmt(const CompoundStmt *CS) {
318     // A compound statement is allowed as long each individual sub-statement
319     // is trivial.
320     return WithCachedResult(CS, [&]() { return VisitChildren(CS); });
321   }
322 
323   bool VisitReturnStmt(const ReturnStmt *RS) {
324     // A return statement is allowed as long as the return value is trivial.
325     if (auto *RV = RS->getRetValue())
326       return Visit(RV);
327     return true;
328   }
329 
330   bool VisitDeclStmt(const DeclStmt *DS) { return VisitChildren(DS); }
331   bool VisitDoStmt(const DoStmt *DS) { return VisitChildren(DS); }
332   bool VisitIfStmt(const IfStmt *IS) {
333     return WithCachedResult(IS, [&]() { return VisitChildren(IS); });
334   }
335   bool VisitForStmt(const ForStmt *FS) {
336     return WithCachedResult(FS, [&]() { return VisitChildren(FS); });
337   }
338   bool VisitCXXForRangeStmt(const CXXForRangeStmt *FS) {
339     return WithCachedResult(FS, [&]() { return VisitChildren(FS); });
340   }
341   bool VisitWhileStmt(const WhileStmt *WS) {
342     return WithCachedResult(WS, [&]() { return VisitChildren(WS); });
343   }
344   bool VisitSwitchStmt(const SwitchStmt *SS) { return VisitChildren(SS); }
345   bool VisitCaseStmt(const CaseStmt *CS) { return VisitChildren(CS); }
346   bool VisitDefaultStmt(const DefaultStmt *DS) { return VisitChildren(DS); }
347 
348   // break, continue, goto, and label statements are always trivial.
349   bool VisitBreakStmt(const BreakStmt *) { return true; }
350   bool VisitContinueStmt(const ContinueStmt *) { return true; }
351   bool VisitGotoStmt(const GotoStmt *) { return true; }
352   bool VisitLabelStmt(const LabelStmt *) { return true; }
353 
354   bool VisitUnaryOperator(const UnaryOperator *UO) {
355     // Unary operators are trivial if its operand is trivial except co_await.
356     return UO->getOpcode() != UO_Coawait && Visit(UO->getSubExpr());
357   }
358 
359   bool VisitBinaryOperator(const BinaryOperator *BO) {
360     // Binary operators are trivial if their operands are trivial.
361     return Visit(BO->getLHS()) && Visit(BO->getRHS());
362   }
363 
364   bool VisitCompoundAssignOperator(const CompoundAssignOperator *CAO) {
365     // Compound assignment operator such as |= is trivial if its
366     // subexpresssions are trivial.
367     return VisitChildren(CAO);
368   }
369 
370   bool VisitArraySubscriptExpr(const ArraySubscriptExpr *ASE) {
371     return VisitChildren(ASE);
372   }
373 
374   bool VisitConditionalOperator(const ConditionalOperator *CO) {
375     // Ternary operators are trivial if their conditions & values are trivial.
376     return VisitChildren(CO);
377   }
378 
379   bool VisitAtomicExpr(const AtomicExpr *E) { return VisitChildren(E); }
380 
381   bool VisitStaticAssertDecl(const StaticAssertDecl *SAD) {
382     // Any static_assert is considered trivial.
383     return true;
384   }
385 
386   bool VisitCallExpr(const CallExpr *CE) {
387     if (!checkArguments(CE))
388       return false;
389 
390     auto *Callee = CE->getDirectCallee();
391     if (!Callee)
392       return false;
393     const auto &Name = safeGetName(Callee);
394 
395     if (Callee->isInStdNamespace() &&
396         (Name == "addressof" || Name == "forward" || Name == "move"))
397       return true;
398 
399     if (Name == "WTFCrashWithInfo" || Name == "WTFBreakpointTrap" ||
400         Name == "WTFCrashWithSecurityImplication" || Name == "WTFCrash" ||
401         Name == "WTFReportAssertionFailure" || Name == "isMainThread" ||
402         Name == "isMainThreadOrGCThread" || Name == "isMainRunLoop" ||
403         Name == "isWebThread" || Name == "isUIThread" ||
404         Name == "mayBeGCThread" || Name == "compilerFenceForCrash" ||
405         Name == "bitwise_cast" || Name.find("__builtin") == 0)
406       return true;
407 
408     return IsFunctionTrivial(Callee);
409   }
410 
411   bool
412   VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) {
413     // Non-type template paramter is compile time constant and trivial.
414     return true;
415   }
416 
417   bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) {
418     return VisitChildren(E);
419   }
420 
421   bool VisitPredefinedExpr(const PredefinedExpr *E) {
422     // A predefined identifier such as "func" is considered trivial.
423     return true;
424   }
425 
426   bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
427     if (!checkArguments(MCE))
428       return false;
429 
430     bool TrivialThis = Visit(MCE->getImplicitObjectArgument());
431     if (!TrivialThis)
432       return false;
433 
434     auto *Callee = MCE->getMethodDecl();
435     if (!Callee)
436       return false;
437 
438     std::optional<bool> IsGetterOfRefCounted = isGetterOfRefCounted(Callee);
439     if (IsGetterOfRefCounted && *IsGetterOfRefCounted)
440       return true;
441 
442     // Recursively descend into the callee to confirm that it's trivial as well.
443     return IsFunctionTrivial(Callee);
444   }
445 
446   bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
447     if (!checkArguments(OCE))
448       return false;
449     auto *Callee = OCE->getCalleeDecl();
450     if (!Callee)
451       return false;
452     // Recursively descend into the callee to confirm that it's trivial as well.
453     return IsFunctionTrivial(Callee);
454   }
455 
456   bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) {
457     if (auto *Expr = E->getExpr()) {
458       if (!Visit(Expr))
459         return false;
460     }
461     return true;
462   }
463 
464   bool checkArguments(const CallExpr *CE) {
465     for (const Expr *Arg : CE->arguments()) {
466       if (Arg && !Visit(Arg))
467         return false;
468     }
469     return true;
470   }
471 
472   bool VisitCXXConstructExpr(const CXXConstructExpr *CE) {
473     for (const Expr *Arg : CE->arguments()) {
474       if (Arg && !Visit(Arg))
475         return false;
476     }
477 
478     // Recursively descend into the callee to confirm that it's trivial.
479     return IsFunctionTrivial(CE->getConstructor());
480   }
481 
482   bool VisitCXXNewExpr(const CXXNewExpr *NE) { return VisitChildren(NE); }
483 
484   bool VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
485     return Visit(ICE->getSubExpr());
486   }
487 
488   bool VisitExplicitCastExpr(const ExplicitCastExpr *ECE) {
489     return Visit(ECE->getSubExpr());
490   }
491 
492   bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *VMT) {
493     return Visit(VMT->getSubExpr());
494   }
495 
496   bool VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE) {
497     if (auto *Temp = BTE->getTemporary()) {
498       if (!TrivialFunctionAnalysis::isTrivialImpl(Temp->getDestructor(), Cache))
499         return false;
500     }
501     return Visit(BTE->getSubExpr());
502   }
503 
504   bool VisitExprWithCleanups(const ExprWithCleanups *EWC) {
505     return Visit(EWC->getSubExpr());
506   }
507 
508   bool VisitParenExpr(const ParenExpr *PE) { return Visit(PE->getSubExpr()); }
509 
510   bool VisitInitListExpr(const InitListExpr *ILE) {
511     for (const Expr *Child : ILE->inits()) {
512       if (Child && !Visit(Child))
513         return false;
514     }
515     return true;
516   }
517 
518   bool VisitMemberExpr(const MemberExpr *ME) {
519     // Field access is allowed but the base pointer may itself be non-trivial.
520     return Visit(ME->getBase());
521   }
522 
523   bool VisitCXXThisExpr(const CXXThisExpr *CTE) {
524     // The expression 'this' is always trivial, be it explicit or implicit.
525     return true;
526   }
527 
528   bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
529     // nullptr is trivial.
530     return true;
531   }
532 
533   bool VisitDeclRefExpr(const DeclRefExpr *DRE) {
534     // The use of a variable is trivial.
535     return true;
536   }
537 
538   // Constant literal expressions are always trivial
539   bool VisitIntegerLiteral(const IntegerLiteral *E) { return true; }
540   bool VisitFloatingLiteral(const FloatingLiteral *E) { return true; }
541   bool VisitFixedPointLiteral(const FixedPointLiteral *E) { return true; }
542   bool VisitCharacterLiteral(const CharacterLiteral *E) { return true; }
543   bool VisitStringLiteral(const StringLiteral *E) { return true; }
544   bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E) { return true; }
545 
546   bool VisitConstantExpr(const ConstantExpr *CE) {
547     // Constant expressions are trivial.
548     return true;
549   }
550 
551 private:
552   CacheTy &Cache;
553   CacheTy RecursiveFn;
554 };
555 
556 bool TrivialFunctionAnalysis::isTrivialImpl(
557     const Decl *D, TrivialFunctionAnalysis::CacheTy &Cache) {
558   TrivialFunctionAnalysisVisitor V(Cache);
559   return V.IsFunctionTrivial(D);
560 }
561 
562 bool TrivialFunctionAnalysis::isTrivialImpl(
563     const Stmt *S, TrivialFunctionAnalysis::CacheTy &Cache) {
564   // If the statement isn't in the cache, conservatively assume that
565   // it's not trivial until analysis completes. Unlike a function case,
566   // we don't insert an entry into the cache until Visit returns
567   // since Visit* functions themselves make use of the cache.
568 
569   TrivialFunctionAnalysisVisitor V(Cache);
570   bool Result = V.Visit(S);
571   assert(Cache.contains(S) && "Top-level statement not properly cached!");
572   return Result;
573 }
574 
575 } // namespace clang
576