tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CustomMatchers.h (18983B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #ifndef CustomMatchers_h__
      6 #define CustomMatchers_h__
      7 
      8 #include "MemMoveAnnotation.h"
      9 #include "Utils.h"
     10 
     11 #if CLANG_VERSION_FULL >= 1300
     12 // Starting with clang-13 Expr::isRValue has been renamed to Expr::isPRValue
     13 #define isRValue isPRValue
     14 #endif
     15 
     16 namespace clang {
     17 namespace ast_matchers {
     18 
     19 /// This matcher will match any function declaration that is declared as a heap
     20 /// allocator.
     21 AST_MATCHER(VarDecl, hasMozGlobalType) {
     22  if(auto * TD = Node.getType().getTypePtr()->getAsTagDecl()) {
     23    if(hasCustomAttribute<moz_global_class>(TD))
     24      return true;
     25  }
     26  return false;
     27 }
     28 
     29 /// Match any variable declared with the MOZ_RUNINIT qualifier.
     30 AST_MATCHER(VarDecl, isMozGlobal) {
     31  return hasCustomAttribute<moz_global_var>(&Node);
     32 }
     33 
     34 /// Match any variable declared with the MOZ_GLOBINIT qualifier.
     35 AST_MATCHER(VarDecl, isMozGenerated) {
     36  return hasCustomAttribute<moz_generated>(&Node);
     37 }
     38 
     39 /// Match any variable declared with the constinit qualifier.
     40 AST_MATCHER(VarDecl, hasConstInitAttr) {
     41  return Node.hasAttr<ConstInitAttr>();
     42 }
     43 
     44 /// This matcher will match any function declaration that is declared as a heap
     45 /// allocator.
     46 AST_MATCHER(FunctionDecl, heapAllocator) {
     47  return hasCustomAttribute<moz_heap_allocator>(&Node);
     48 }
     49 
     50 /// This matcher will match any declaration that is marked as not accepting
     51 /// arithmetic expressions in its arguments.
     52 AST_MATCHER(Decl, noArithmeticExprInArgs) {
     53  return hasCustomAttribute<moz_no_arith_expr_in_arg>(&Node);
     54 }
     55 
     56 /// This matcher will match any C++ class that is marked as having a trivial
     57 /// constructor and destructor.
     58 AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) {
     59  return hasCustomAttribute<moz_trivial_ctor_dtor>(&Node);
     60 }
     61 
     62 /// This matcher will match any C++ class that is marked as having a trivial
     63 /// destructor.
     64 AST_MATCHER(CXXRecordDecl, hasTrivialDtor) {
     65  return hasCustomAttribute<moz_trivial_dtor>(&Node);
     66 }
     67 
     68 AST_MATCHER(CXXConstructExpr, allowsTemporary) {
     69  return hasCustomAttribute<moz_allow_temporary>(Node.getConstructor());
     70 }
     71 
     72 /// This matcher will match lvalue-ref-qualified methods.
     73 AST_MATCHER(CXXMethodDecl, isLValueRefQualified) {
     74  return Node.getRefQualifier() == RQ_LValue;
     75 }
     76 
     77 /// This matcher will match rvalue-ref-qualified methods.
     78 AST_MATCHER(CXXMethodDecl, isRValueRefQualified) {
     79  return Node.getRefQualifier() == RQ_RValue;
     80 }
     81 
     82 AST_POLYMORPHIC_MATCHER(isFirstParty,
     83                        AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt)) {
     84  return !inThirdPartyPath(&Node, &Finder->getASTContext()) &&
     85         !ASTIsInSystemHeader(Finder->getASTContext(), Node);
     86 }
     87 
     88 AST_MATCHER(DeclaratorDecl, isNotSpiderMonkey) {
     89  // Detect SpiderMonkey path. Not as strict as isFirstParty, but this is
     90  // expected to disappear soon by getting a common style guide between DOM and
     91  // SpiderMonkey.
     92  std::string Path = Node.getBeginLoc().printToString(
     93      Finder->getASTContext().getSourceManager());
     94  return Path.find("js") == std::string::npos &&
     95         Path.find("xpc") == std::string::npos &&
     96         Path.find("XPC") == std::string::npos;
     97 }
     98 
     99 /// This matcher will match any declaration with an initializer that's
    100 /// considered as constant by the compiler.
    101 AST_MATCHER(VarDecl, hasConstantInitializer) {
    102  if(Node.hasInit())
    103    return Node.getInit()->isConstantInitializer(
    104                  Finder->getASTContext(),
    105                  Node.getType()->isReferenceType());
    106  else
    107    return Node.getType().isTrivialType(Finder->getASTContext());
    108 }
    109 
    110 /// This matcher will match temporary expressions.
    111 /// We need this matcher for compatibility with clang 3.* (clang 4 and above
    112 /// insert a MaterializeTemporaryExpr everywhere).
    113 AST_MATCHER(Expr, isTemporary) {
    114  return Node.isRValue() || Node.isXValue() ||
    115         isa<MaterializeTemporaryExpr>(&Node);
    116 }
    117 
    118 /// This matcher will match any method declaration that is marked as returning
    119 /// a pointer deleted by the destructor of the class.
    120 AST_MATCHER(CXXMethodDecl, noDanglingOnTemporaries) {
    121  return hasCustomAttribute<moz_no_dangling_on_temporaries>(&Node);
    122 }
    123 
    124 /// This matcher will match any function declaration that is marked to prohibit
    125 /// calling AddRef or Release on its return value.
    126 AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) {
    127  return hasCustomAttribute<moz_no_addref_release_on_return>(&Node);
    128 }
    129 
    130 /// This matcher will match any function declaration that is marked as being
    131 /// allowed to run script.
    132 AST_MATCHER(FunctionDecl, hasCanRunScriptAnnotation) {
    133  return hasCustomAttribute<moz_can_run_script>(&Node);
    134 }
    135 
    136 /// This matcher will match all arithmetic binary operators.
    137 AST_MATCHER(BinaryOperator, binaryArithmeticOperator) {
    138  BinaryOperatorKind OpCode = Node.getOpcode();
    139  return OpCode == BO_Mul || OpCode == BO_Div || OpCode == BO_Rem ||
    140         OpCode == BO_Add || OpCode == BO_Sub || OpCode == BO_Shl ||
    141         OpCode == BO_Shr || OpCode == BO_And || OpCode == BO_Xor ||
    142         OpCode == BO_Or || OpCode == BO_MulAssign || OpCode == BO_DivAssign ||
    143         OpCode == BO_RemAssign || OpCode == BO_AddAssign ||
    144         OpCode == BO_SubAssign || OpCode == BO_ShlAssign ||
    145         OpCode == BO_ShrAssign || OpCode == BO_AndAssign ||
    146         OpCode == BO_XorAssign || OpCode == BO_OrAssign;
    147 }
    148 
    149 /// This matcher will match all arithmetic unary operators.
    150 AST_MATCHER(UnaryOperator, unaryArithmeticOperator) {
    151  UnaryOperatorKind OpCode = Node.getOpcode();
    152  return OpCode == UO_PostInc || OpCode == UO_PostDec || OpCode == UO_PreInc ||
    153         OpCode == UO_PreDec || OpCode == UO_Plus || OpCode == UO_Minus ||
    154         OpCode == UO_Not;
    155 }
    156 
    157 /// This matcher will match the unary dereference operator
    158 AST_MATCHER(UnaryOperator, unaryDereferenceOperator) {
    159  UnaryOperatorKind OpCode = Node.getOpcode();
    160  return OpCode == UO_Deref;
    161 }
    162 
    163 /// This matcher will match == and != binary operators.
    164 AST_MATCHER(BinaryOperator, binaryEqualityOperator) {
    165  BinaryOperatorKind OpCode = Node.getOpcode();
    166  return OpCode == BO_EQ || OpCode == BO_NE;
    167 }
    168 
    169 /// This matcher will match comma operator.
    170 AST_MATCHER(BinaryOperator, binaryCommaOperator) {
    171  BinaryOperatorKind OpCode = Node.getOpcode();
    172  return OpCode == BO_Comma;
    173 }
    174 
    175 /// This matcher will match floating point types.
    176 AST_MATCHER(QualType, isFloat) { return Node->isRealFloatingType(); }
    177 
    178 /// This matcher will match locations in system headers.  This is adopted from
    179 /// isExpansionInSystemHeader in newer clangs, but modified in order to work
    180 /// with old clangs that we use on infra.
    181 AST_POLYMORPHIC_MATCHER(isInSystemHeader,
    182                        AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt)) {
    183  return ASTIsInSystemHeader(Finder->getASTContext(), Node);
    184 }
    185 
    186 /// This matcher will match a file "gtest-port.h". The file contains
    187 /// known fopen usages that are OK.
    188 AST_MATCHER(CallExpr, isInWhitelistForFopenUsage) {
    189  static const char Whitelist[] = "gtest-port.h";
    190  SourceLocation Loc = Node.getBeginLoc();
    191  StringRef FileName =
    192      getFilename(Finder->getASTContext().getSourceManager(), Loc);
    193 
    194  return *llvm::sys::path::rbegin(FileName) == Whitelist;
    195 }
    196 
    197 /// This matcher will match a list of files.  These files contain
    198 /// known NaN-testing expressions which we would like to whitelist.
    199 AST_MATCHER(BinaryOperator, isInWhitelistForNaNExpr) {
    200  const char *whitelist[] = {"SkScalar.h", "json_writer.cpp", "State.cpp"};
    201 
    202  SourceLocation Loc = Node.getOperatorLoc();
    203  StringRef FileName =
    204      getFilename(Finder->getASTContext().getSourceManager(), Loc);
    205  for (auto itr = std::begin(whitelist); itr != std::end(whitelist); itr++) {
    206    if (*llvm::sys::path::rbegin(FileName) == *itr) {
    207      return true;
    208    }
    209  }
    210 
    211  return false;
    212 }
    213 
    214 AST_MATCHER(CallExpr, isInWhiteListForPrincipalGetUri) {
    215  const auto Whitelist = {"nsIPrincipal.h", "BasePrincipal.cpp",
    216                          "ContentPrincipal.cpp"};
    217  SourceLocation Loc = Node.getBeginLoc();
    218  StringRef Filename =
    219      getFilename(Finder->getASTContext().getSourceManager(), Loc);
    220 
    221  for (auto Exclusion : Whitelist) {
    222    if (Filename.find(Exclusion) != std::string::npos) {
    223      return true;
    224    }
    225  }
    226  return false;
    227 }
    228 
    229 /// This matcher will match a list of files which contain NS_NewNamedThread
    230 /// code or names of existing threads that we would like to ignore.
    231 AST_MATCHER(CallExpr, isInAllowlistForThreads) {
    232 
    233  // Get the source location of the call.
    234  SourceLocation Loc = Node.getRParenLoc();
    235  StringRef FileName =
    236      getFilename(Finder->getASTContext().getSourceManager(), Loc);
    237 
    238  const auto rbegin = [](StringRef s) { return llvm::sys::path::rbegin(s); };
    239  const auto rend = [](StringRef s) { return llvm::sys::path::rend(s); };
    240 
    241  // Files in the allowlist are (definitionally) explicitly permitted to create
    242  // new threads.
    243  for (auto thread_file : allow_thread_files) {
    244    // All the provided path-elements must match.
    245    const bool match = [&] {
    246      auto it1 = rbegin(FileName), it2 = rbegin(thread_file),
    247           end1 = rend(FileName), end2 = rend(thread_file);
    248      for (; it2 != end2; ++it1, ++it2) {
    249        if (it1 == end1 || *it1 != *it2) {
    250          return false;
    251        }
    252      }
    253      return true;
    254    }();
    255    if (match) {
    256      return true;
    257    }
    258  }
    259 
    260  // Check the first arg (the name of the thread).
    261  const StringLiteral *nameArg =
    262      dyn_cast<StringLiteral>(Node.getArg(0)->IgnoreImplicit());
    263  if (nameArg) {
    264    const StringRef name = nameArg->getString();
    265    for (auto thread_name : allow_thread_names) {
    266      if (name == thread_name) {
    267        return true;
    268      }
    269    }
    270  }
    271 
    272  return false;
    273 }
    274 
    275 /// This matcher will match all accesses to AddRef or Release methods.
    276 AST_MATCHER(MemberExpr, isAddRefOrRelease) {
    277  ValueDecl *Member = Node.getMemberDecl();
    278  CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member);
    279  if (Method) {
    280    const auto &Name = getNameChecked(Method);
    281    return Name == "AddRef" || Name == "Release";
    282  }
    283  return false;
    284 }
    285 
    286 /// This matcher will select classes which are refcounted AND have an mRefCnt
    287 /// member.
    288 AST_MATCHER(CXXRecordDecl, hasRefCntMember) {
    289  return isClassRefCounted(&Node) && getClassRefCntMember(&Node);
    290 }
    291 
    292 /// This matcher will select classes which are refcounted.
    293 AST_MATCHER(CXXRecordDecl, isRefCounted) { return isClassRefCounted(&Node); }
    294 
    295 AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); }
    296 
    297 AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) {
    298  return hasCustomAttribute<moz_needs_no_vtable_type>(&Node);
    299 }
    300 
    301 /// This matcher will select classes which are non-memmovable
    302 AST_MATCHER(QualType, isNonMemMovable) {
    303  return NonMemMovable.hasEffectiveAnnotation(Node);
    304 }
    305 
    306 /// This matcher will select classes which require a memmovable template arg
    307 AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) {
    308  return hasCustomAttribute<moz_needs_memmovable_type>(&Node);
    309 }
    310 
    311 /// This matcher will select classes which require all members to be memmovable
    312 AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) {
    313  return hasCustomAttribute<moz_needs_memmovable_members>(&Node);
    314 }
    315 
    316 AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) {
    317  const CXXConstructorDecl *Declaration = Node.getCanonicalDecl();
    318  return
    319      // Skip constructors in system headers
    320      !ASTIsInSystemHeader(Declaration->getASTContext(), *Declaration) &&
    321      // Skip ignored namespaces and paths
    322      !isInIgnoredNamespaceForImplicitCtor(Declaration) &&
    323      !inThirdPartyPath(Declaration) &&
    324      // We only want Converting constructors
    325      Declaration->isConvertingConstructor(false) &&
    326      // We don't want copy of move constructors, as those are allowed to be
    327      // implicit
    328      !Declaration->isCopyOrMoveConstructor() &&
    329      // We don't want inheriting constructors, since using declarations can't
    330      // have attributes
    331      !Declaration->isInheritingConstructor() &&
    332      // We don't want deleted constructors.
    333      !Declaration->isDeleted();
    334 }
    335 
    336 AST_MATCHER_P(Expr, ignoreTrivials, internal::Matcher<Expr>, InnerMatcher) {
    337  return InnerMatcher.matches(*IgnoreTrivials(&Node), Finder, Builder);
    338 }
    339 
    340 // Takes two matchers: the first one is a condition; the second is a matcher to
    341 // be applied once we are done unwrapping trivials.  While the condition does
    342 // not match and we're looking at a trivial, will keep unwrapping the trivial
    343 // and trying again. Once the condition matches, we will go ahead and unwrap all
    344 // trivials and apply the inner matcher to the result.
    345 //
    346 // The expected use here is if we want to condition a match on some typecheck
    347 // but apply the match to only non-trivials, because there are trivials (e.g.
    348 // casts) that can change types.
    349 AST_MATCHER_P2(Expr, ignoreTrivialsConditional, internal::Matcher<Expr>,
    350               Condition, internal::Matcher<Expr>, InnerMatcher) {
    351  const Expr *node = &Node;
    352  while (true) {
    353    if (Condition.matches(*node, Finder, Builder)) {
    354      return InnerMatcher.matches(*IgnoreTrivials(node), Finder, Builder);
    355    }
    356    const Expr *newNode = MaybeSkipOneTrivial(node);
    357    if (newNode == node) {
    358      return false;
    359    }
    360    node = newNode;
    361  }
    362 }
    363 
    364 // We can't call this "isImplicit" since it clashes with an existing matcher in
    365 // clang.
    366 AST_MATCHER(CXXConstructorDecl, isMarkedImplicit) {
    367  return hasCustomAttribute<moz_implicit>(&Node);
    368 }
    369 
    370 AST_MATCHER(CXXRecordDecl, isConcreteClass) { return !Node.isAbstract(); }
    371 
    372 AST_MATCHER(QualType, autoNonAutoableType) {
    373  if (const AutoType *T = Node->getContainedAutoType()) {
    374    if (const CXXRecordDecl *Rec = T->getAsCXXRecordDecl()) {
    375      return hasCustomAttribute<moz_non_autoable>(Rec);
    376    }
    377  }
    378  return false;
    379 }
    380 
    381 AST_MATCHER(CXXConstructorDecl, isExplicitMoveConstructor) {
    382  return Node.isExplicit() && Node.isMoveConstructor();
    383 }
    384 
    385 AST_MATCHER(CXXConstructorDecl, isCompilerProvidedCopyConstructor) {
    386  return !Node.isUserProvided() && Node.isCopyConstructor();
    387 }
    388 
    389 AST_MATCHER(CallExpr, isAssertAssignmentTestFunc) {
    390  static const std::string AssertName = "MOZ_AssertAssignmentTest";
    391  const FunctionDecl *Method = Node.getDirectCallee();
    392 
    393  return Method && Method->getDeclName().isIdentifier() &&
    394         Method->getName() == AssertName;
    395 }
    396 
    397 AST_MATCHER(CallExpr, isSnprintfLikeFunc) {
    398  static const std::string Snprintf = "snprintf";
    399  static const std::string Vsnprintf = "vsnprintf";
    400  const FunctionDecl *Func = Node.getDirectCallee();
    401 
    402  if (!Func || isa<CXXMethodDecl>(Func)) {
    403    return false;
    404  }
    405 
    406  StringRef Name = getNameChecked(Func);
    407  if (Name != Snprintf && Name != Vsnprintf) {
    408    return false;
    409  }
    410 
    411  return !inThirdPartyPath(Node.getBeginLoc(),
    412                           Finder->getASTContext().getSourceManager()) &&
    413         !isIgnoredPathForSprintfLiteral(
    414             &Node, Finder->getASTContext().getSourceManager());
    415 }
    416 
    417 AST_MATCHER(CXXRecordDecl, isLambdaDecl) { return Node.isLambda(); }
    418 
    419 AST_MATCHER(QualType, isRefPtr) { return typeIsRefPtr(Node); }
    420 
    421 AST_MATCHER(QualType, isSmartPtrToRefCounted) {
    422  auto *D = getNonTemplateSpecializedCXXRecordDecl(Node);
    423  if (!D) {
    424    return false;
    425  }
    426 
    427  D = D->getCanonicalDecl();
    428 
    429  return D && hasCustomAttribute<moz_is_smartptr_to_refcounted>(D);
    430 }
    431 
    432 AST_MATCHER(ClassTemplateSpecializationDecl, isSmartPtrToRefCountedDecl) {
    433  auto *D = dyn_cast_or_null<CXXRecordDecl>(
    434      Node.getSpecializedTemplate()->getTemplatedDecl());
    435  if (!D) {
    436    return false;
    437  }
    438 
    439  D = D->getCanonicalDecl();
    440 
    441  return D && hasCustomAttribute<moz_is_smartptr_to_refcounted>(D);
    442 }
    443 
    444 AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
    445  const CXXRecordDecl *Decl = Node.getCanonicalDecl();
    446 
    447  // Must have definition and should inherit other classes
    448  return Decl && Decl->hasDefinition() && Decl->getNumBases();
    449 }
    450 
    451 AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) {
    452  const CXXMethodDecl *Decl = Node.getCanonicalDecl();
    453  return Decl && hasCustomAttribute<moz_required_base_method>(Decl);
    454 }
    455 
    456 AST_MATCHER(CXXMethodDecl, isNonVirtual) {
    457  const CXXMethodDecl *Decl = Node.getCanonicalDecl();
    458  return Decl && !Decl->isVirtual();
    459 }
    460 
    461 AST_MATCHER(FunctionDecl, isMozMustReturnFromCaller) {
    462  const FunctionDecl *Decl = Node.getCanonicalDecl();
    463  return Decl &&
    464         hasCustomAttribute<moz_must_return_from_caller_if_this_is_arg>(Decl);
    465 }
    466 
    467 /// This matcher will select default args which have nullptr as the value.
    468 AST_MATCHER(CXXDefaultArgExpr, isNullDefaultArg) {
    469  const Expr *Expr = Node.getExpr();
    470  return Expr && Expr->isNullPointerConstant(Finder->getASTContext(),
    471                                             Expr::NPC_NeverValueDependent);
    472 }
    473 
    474 AST_MATCHER(UsingDirectiveDecl, isUsingNamespaceMozillaJava) {
    475  const NamespaceDecl *Namespace = Node.getNominatedNamespace();
    476  const std::string &FQName = Namespace->getQualifiedNameAsString();
    477 
    478  static const char NAMESPACE[] = "mozilla::java";
    479  static const char PREFIX[] = "mozilla::java::";
    480 
    481  // We match both the `mozilla::java` namespace itself as well as any other
    482  // namespaces contained within the `mozilla::java` namespace.
    483  return !FQName.compare(NAMESPACE) ||
    484         !FQName.compare(0, sizeof(PREFIX) - 1, PREFIX);
    485 }
    486 
    487 AST_MATCHER(MemberExpr, hasKnownLiveAnnotation) {
    488  ValueDecl *Member = Node.getMemberDecl();
    489  FieldDecl *Field = dyn_cast<FieldDecl>(Member);
    490  return Field && hasCustomAttribute<moz_known_live>(Field);
    491 }
    492 
    493 #define GENERATE_JSTYPEDEF_PAIR(templateName)                                  \
    494  {templateName "Function", templateName "<JSFunction*>"},                     \
    495      {templateName "Id", templateName "<JS::PropertyKey>"},                   \
    496      {templateName "Object", templateName "<JSObject*>"},                     \
    497      {templateName "Script", templateName "<JSScript*>"},                     \
    498      {templateName "String", templateName "<JSString*>"},                     \
    499      {templateName "Symbol", templateName "<JS::Symbol*>"},                   \
    500      {templateName "BigInt", templateName "<JS::BigInt*>"},                   \
    501      {templateName "Value", templateName "<JS::Value>"},                      \
    502      {templateName "ValueVector", templateName "Vector<JS::Value>"},          \
    503      {templateName "ObjectVector", templateName "Vector<JSObject*>"}, {       \
    504    templateName "IdVector", templateName "Vector<JS::PropertyKey>"            \
    505  }
    506 
    507 static const char *const JSHandleRootedTypedefMap[][2] = {
    508    GENERATE_JSTYPEDEF_PAIR("JS::Handle"),
    509    GENERATE_JSTYPEDEF_PAIR("JS::MutableHandle"),
    510    GENERATE_JSTYPEDEF_PAIR("JS::Rooted"),
    511    // Technically there is no PersistentRootedValueVector, and that's okay
    512    GENERATE_JSTYPEDEF_PAIR("JS::PersistentRooted"),
    513 };
    514 
    515 AST_MATCHER(DeclaratorDecl, isUsingJSHandleRootedTypedef) {
    516  QualType Type = Node.getType();
    517  std::string TypeName = Type.getAsString();
    518  for (auto &pair : JSHandleRootedTypedefMap) {
    519    if (!TypeName.compare(pair[0])) {
    520      return true;
    521    }
    522  }
    523  return false;
    524 }
    525 
    526 } // namespace ast_matchers
    527 } // namespace clang
    528 
    529 #undef isRValue
    530 #endif