tor-browser

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

KungFuDeathGripChecker.cpp (4168B)


      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 #include "KungFuDeathGripChecker.h"
      6 #include "CustomMatchers.h"
      7 
      8 void KungFuDeathGripChecker::registerMatchers(MatchFinder *AstMatcher) {
      9  AstMatcher->addMatcher(varDecl(allOf(hasType(isRefPtr()), hasLocalStorage(),
     10                                       hasInitializer(anything())))
     11                             .bind("decl"),
     12                         this);
     13 }
     14 
     15 void KungFuDeathGripChecker::check(const MatchFinder::MatchResult &Result) {
     16  const char *Error = "Unused \"kungFuDeathGrip\" %0 objects constructed from "
     17                      "%1 are prohibited";
     18  const char *Note = "Please switch all accesses to this %0 to go through "
     19                     "'%1', or explicitly cast '%1' to `(void)`";
     20 
     21  const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("decl");
     22  if (D->isReferenced()) {
     23    return;
     24  }
     25 
     26  // Not interested in parameters.
     27  if (isa<ImplicitParamDecl>(D) || isa<ParmVarDecl>(D)) {
     28    return;
     29  }
     30 
     31  const Expr *E = IgnoreTrivials(D->getInit());
     32  const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E);
     33  if (CE && CE->getNumArgs() == 0) {
     34    // We don't report an error when we construct and don't use a nsCOMPtr /
     35    // nsRefPtr with no arguments. We don't report it because the error is not
     36    // related to the current check. In the future it may be reported through a
     37    // more generic mechanism.
     38    return;
     39  }
     40 
     41  // We don't want to look at the single argument conversion constructors
     42  // which are inbetween the declaration and the actual object which we are
     43  // assigning into the nsCOMPtr/RefPtr. To do this, we repeatedly
     44  // IgnoreTrivials, then look at the expression. If it is one of these
     45  // conversion constructors, we ignore it and continue to dig.
     46  while ((CE = dyn_cast<CXXConstructExpr>(E)) && CE->getNumArgs() == 1) {
     47    E = IgnoreTrivials(CE->getArg(0));
     48  }
     49 
     50  // If the argument expression is an xvalue, we are not taking a copy of
     51  // anything.
     52  if (E->isXValue()) {
     53    return;
     54  }
     55 
     56  // It is possible that the QualType doesn't point to a type yet so we are
     57  // not interested.
     58  if (E->getType().isNull()) {
     59    return;
     60  }
     61 
     62  // We allow taking a kungFuDeathGrip of `this` because it cannot change
     63  // beneath us, so calling directly through `this` is OK. This is the same
     64  // for local variable declarations.
     65  //
     66  // We also don't complain about unused RefPtrs which are constructed from
     67  // the return value of a new expression, as these are required in order to
     68  // immediately destroy the value created (which was presumably created for
     69  // its side effects), and are not used as a death grip.
     70  if (isa<CXXThisExpr>(E) || isa<DeclRefExpr>(E) || isa<CXXNewExpr>(E)) {
     71    return;
     72  }
     73 
     74  // These types are assigned into nsCOMPtr and RefPtr for their side effects,
     75  // and not as a kungFuDeathGrip. We don't want to consider RefPtr and nsCOMPtr
     76  // types which are initialized with these types as errors.
     77  const TagDecl *TD = E->getType()->getAsTagDecl();
     78  if (TD && TD->getIdentifier()) {
     79    static const char *IgnoreTypes[] = {
     80        "already_AddRefed",
     81        "nsGetServiceByCID",
     82        "nsGetServiceByCIDWithError",
     83        "nsGetServiceByContractID",
     84        "nsGetServiceByContractIDWithError",
     85        "nsCreateInstanceByCID",
     86        "nsCreateInstanceByContractID",
     87        "nsCreateInstanceFromFactory",
     88    };
     89 
     90    for (uint32_t i = 0; i < sizeof(IgnoreTypes) / sizeof(IgnoreTypes[0]);
     91         ++i) {
     92      if (TD->getName() == IgnoreTypes[i]) {
     93        return;
     94      }
     95    }
     96  }
     97 
     98  // Report the error
     99  const char *ErrThing;
    100  const char *NoteThing;
    101  if (isa<MemberExpr>(E)) {
    102    ErrThing = "members";
    103    NoteThing = "member";
    104  } else {
    105    ErrThing = "temporary values";
    106    NoteThing = "value";
    107  }
    108 
    109  // We cannot provide the note if we don't have an initializer
    110  diag(D->getBeginLoc(), Error, DiagnosticIDs::Error)
    111      << D->getType() << ErrThing;
    112  diag(E->getBeginLoc(), Note, DiagnosticIDs::Note)
    113      << NoteThing << getNameChecked(D);
    114 }