tor-browser

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

MustReturnFromCallerChecker.cpp (4329B)


      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 "MustReturnFromCallerChecker.h"
      6 #include "CustomMatchers.h"
      7 
      8 void MustReturnFromCallerChecker::registerMatchers(MatchFinder *AstMatcher) {
      9  // Look for a call to a MOZ_MUST_RETURN_FROM_CALLER member
     10  AstMatcher->addMatcher(
     11      cxxMemberCallExpr(
     12          on(declRefExpr(to(parmVarDecl()))),
     13          callee(functionDecl(isMozMustReturnFromCaller())),
     14          anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")),
     15                hasAncestor(functionDecl().bind("containing-func"))))
     16          .bind("call"),
     17      this);
     18 }
     19 
     20 void MustReturnFromCallerChecker::check(
     21    const MatchFinder::MatchResult &Result) {
     22  const auto *ContainingLambda =
     23      Result.Nodes.getNodeAs<LambdaExpr>("containing-lambda");
     24  const auto *ContainingFunc =
     25      Result.Nodes.getNodeAs<FunctionDecl>("containing-func");
     26  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
     27 
     28  Stmt *Body = nullptr;
     29  if (ContainingLambda) {
     30    Body = ContainingLambda->getBody();
     31  } else if (ContainingFunc) {
     32    Body = ContainingFunc->getBody();
     33  } else {
     34    return;
     35  }
     36  assert(Body && "Should have a body by this point");
     37 
     38  // Generate the CFG for the enclosing function or decl.
     39  CFG::BuildOptions Options;
     40  std::unique_ptr<CFG> TheCFG =
     41      CFG::buildCFG(nullptr, Body, Result.Context, Options);
     42  if (!TheCFG) {
     43    return;
     44  }
     45 
     46  // Determine which block in the CFG we want to look at the successors of.
     47  StmtToBlockMap BlockMap(TheCFG.get(), Result.Context);
     48  size_t CallIndex;
     49  const auto *Block = BlockMap.blockContainingStmt(Call, &CallIndex);
     50  if (!Block) {
     51    // This statement is not within the CFG!
     52    return;
     53  }
     54 
     55  if (!immediatelyReturns(Block, Result.Context, CallIndex + 1)) {
     56    diag(Call->getBeginLoc(),
     57         "You must immediately return after calling this function",
     58         DiagnosticIDs::Error);
     59  }
     60 }
     61 
     62 bool MustReturnFromCallerChecker::isIgnorable(const Stmt *S) {
     63  auto AfterTrivials = IgnoreTrivials(S);
     64 
     65  // After a call to MOZ_MUST_RETURN_FROM_CALLER function it's ok to have any of
     66  // these expressions.
     67  if (isa<ReturnStmt>(AfterTrivials) || isa<CXXConstructExpr>(AfterTrivials) ||
     68      isa<DeclRefExpr>(AfterTrivials) || isa<MemberExpr>(AfterTrivials) ||
     69      isa<IntegerLiteral>(AfterTrivials) ||
     70      isa<FloatingLiteral>(AfterTrivials) ||
     71      isa<CXXNullPtrLiteralExpr>(AfterTrivials) ||
     72      isa<CXXBoolLiteralExpr>(AfterTrivials)) {
     73    return true;
     74  }
     75 
     76  // Solitary `this` should be permited, like in the context `return this;`
     77  if (auto TE = dyn_cast<CXXThisExpr>(AfterTrivials)) {
     78    if (TE->child_begin() == TE->child_end()) {
     79      return true;
     80    }
     81    return false;
     82  }
     83 
     84  // For UnaryOperator make sure we only accept arithmetic operations.
     85  if (auto UO = dyn_cast<UnaryOperator>(AfterTrivials)) {
     86    if (!UO->isArithmeticOp()) {
     87      return false;
     88    }
     89    return isIgnorable(UO->getSubExpr());
     90  }
     91 
     92  // It's also OK to call any function or method which is annotated with
     93  // MOZ_MAY_CALL_AFTER_MUST_RETURN. We consider all CXXConversionDecls
     94  // to be MOZ_MAY_CALL_AFTER_MUST_RETURN (like operator T*()).
     95  if (auto CE = dyn_cast<CallExpr>(AfterTrivials)) {
     96    auto Callee = CE->getDirectCallee();
     97    if (Callee && hasCustomAttribute<moz_may_call_after_must_return>(Callee)) {
     98      return true;
     99    }
    100 
    101    if (Callee && isa<CXXConversionDecl>(Callee)) {
    102      return true;
    103    }
    104  }
    105  return false;
    106 }
    107 
    108 bool MustReturnFromCallerChecker::immediatelyReturns(
    109    RecurseGuard<const CFGBlock *> Block, ASTContext *TheContext,
    110    size_t FromIdx) {
    111  if (Block.isRepeat()) {
    112    return false;
    113  }
    114 
    115  for (size_t I = FromIdx; I < Block->size(); ++I) {
    116    auto S = (*Block)[I].getAs<CFGStmt>();
    117    if (!S) {
    118      continue;
    119    }
    120 
    121    // Some statements should be ignored by default due to their CFG context.
    122    if (isIgnorable(S->getStmt())) {
    123      continue;
    124    }
    125 
    126    // Otherwise, this expression is problematic.
    127    return false;
    128  }
    129 
    130  for (auto Succ = Block->succ_begin(); Succ != Block->succ_end(); ++Succ) {
    131    if (!immediatelyReturns(Block.recurse(*Succ), TheContext, 0)) {
    132      return false;
    133    }
    134  }
    135  return true;
    136 }