tor-browser

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

VariableUsageHelpers.cpp (9808B)


      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 "VariableUsageHelpers.h"
      6 #include "Utils.h"
      7 
      8 std::vector<const Stmt *> getUsageAsRvalue(const ValueDecl *ValueDeclaration,
      9                                           const FunctionDecl *FuncDecl) {
     10  std::vector<const Stmt *> UsageStatements;
     11 
     12  // We check the function declaration has a body.
     13  auto Body = FuncDecl->getBody();
     14  if (!Body) {
     15    return std::vector<const Stmt *>();
     16  }
     17 
     18  // We build a Control Flow Graph (CFG) fron the body of the function
     19  // declaration.
     20  std::unique_ptr<CFG> StatementCFG = CFG::buildCFG(
     21      FuncDecl, Body, &FuncDecl->getASTContext(), CFG::BuildOptions());
     22 
     23  // We iterate through all the CFGBlocks, which basically means that we go over
     24  // all the possible branches of the code and therefore cover all statements.
     25  for (auto &Block : *StatementCFG) {
     26    // We iterate through all the statements of the block.
     27    for (auto &BlockItem : *Block) {
     28      auto CFGStatement = BlockItem.getAs<CFGStmt>();
     29      if (!CFGStatement) {
     30        continue;
     31      }
     32 
     33      // FIXME: Right now this function/if chain is very basic and only covers
     34      // the cases we need for escapesFunction()
     35      if (auto BinOp = dyn_cast<BinaryOperator>(CFGStatement->getStmt())) {
     36        // We only care about assignments.
     37        if (BinOp->getOpcode() != BO_Assign) {
     38          continue;
     39        }
     40 
     41        // We want our declaration to be used on the right hand side of the
     42        // assignment.
     43        auto DeclRef = dyn_cast<DeclRefExpr>(IgnoreTrivials(BinOp->getRHS()));
     44        if (!DeclRef) {
     45          continue;
     46        }
     47 
     48        if (DeclRef->getDecl() != ValueDeclaration) {
     49          continue;
     50        }
     51      } else if (auto Return = dyn_cast<ReturnStmt>(CFGStatement->getStmt())) {
     52        // We want our declaration to be used as the expression of the return
     53        // statement.
     54        auto DeclRef = dyn_cast_or_null<DeclRefExpr>(
     55            IgnoreTrivials(Return->getRetValue()));
     56        if (!DeclRef) {
     57          continue;
     58        }
     59 
     60        if (DeclRef->getDecl() != ValueDeclaration) {
     61          continue;
     62        }
     63      } else {
     64        continue;
     65      }
     66 
     67      // We didn't early-continue, so we add the statement to the list.
     68      UsageStatements.push_back(CFGStatement->getStmt());
     69    }
     70  }
     71 
     72  return UsageStatements;
     73 }
     74 
     75 // We declare our EscapesFunctionError enum to be an error code enum.
     76 namespace std {
     77 template <> struct is_error_code_enum<EscapesFunctionError> : true_type {};
     78 } // namespace std
     79 
     80 // We define the EscapesFunctionErrorCategory which contains the error messages
     81 // corresponding to each enum variant.
     82 namespace {
     83 struct EscapesFunctionErrorCategory : std::error_category {
     84  const char *name() const noexcept override;
     85  std::string message(int ev) const override;
     86 };
     87 
     88 const char *EscapesFunctionErrorCategory::name() const noexcept {
     89  return "escapes function";
     90 }
     91 
     92 std::string EscapesFunctionErrorCategory::message(int ev) const {
     93  switch (static_cast<EscapesFunctionError>(ev)) {
     94  case EscapesFunctionError::ConstructorDeclNotFound:
     95    return "constructor declaration not found";
     96 
     97  case EscapesFunctionError::FunctionDeclNotFound:
     98    return "function declaration not found";
     99 
    100  case EscapesFunctionError::FunctionIsBuiltin:
    101    return "function is builtin";
    102 
    103  case EscapesFunctionError::FunctionIsVariadic:
    104    return "function is variadic";
    105 
    106  case EscapesFunctionError::ExprNotInCall:
    107    return "expression is not in call";
    108 
    109  case EscapesFunctionError::NoParamForArg:
    110    return "no parameter for argument";
    111 
    112  case EscapesFunctionError::ArgAndParamNotPointers:
    113    return "argument and parameter are not pointers";
    114  }
    115 }
    116 
    117 const EscapesFunctionErrorCategory TheEscapesFunctionErrorCategory{};
    118 } // namespace
    119 
    120 std::error_code make_error_code(EscapesFunctionError e) {
    121  return {static_cast<int>(e), TheEscapesFunctionErrorCategory};
    122 }
    123 
    124 ErrorOr<std::tuple<const Stmt *, const Decl *>>
    125 escapesFunction(const Expr *Arg, const CXXConstructExpr *Construct) {
    126  // We get the function declaration corresponding to the call.
    127  auto CtorDecl = Construct->getConstructor();
    128  if (!CtorDecl) {
    129    return EscapesFunctionError::ConstructorDeclNotFound;
    130  }
    131 
    132  return escapesFunction(Arg, CtorDecl, Construct->getArgs(),
    133                         Construct->getNumArgs());
    134 }
    135 
    136 ErrorOr<std::tuple<const Stmt *, const Decl *>>
    137 escapesFunction(const Expr *Arg, const CallExpr *Call) {
    138  // We get the function declaration corresponding to the call.
    139  auto FuncDecl = Call->getDirectCallee();
    140  if (!FuncDecl) {
    141    return EscapesFunctionError::FunctionDeclNotFound;
    142  }
    143 
    144  return escapesFunction(Arg, FuncDecl, Call->getArgs(), Call->getNumArgs());
    145 }
    146 
    147 ErrorOr<std::tuple<const Stmt *, const Decl *>>
    148 escapesFunction(const Expr *Arg, const CXXOperatorCallExpr *OpCall) {
    149  // We get the function declaration corresponding to the operator call.
    150  auto FuncDecl = OpCall->getDirectCallee();
    151  if (!FuncDecl) {
    152    return EscapesFunctionError::FunctionDeclNotFound;
    153  }
    154 
    155  auto Args = OpCall->getArgs();
    156  auto NumArgs = OpCall->getNumArgs();
    157  // If this is an infix binary operator defined as a one-param method, we
    158  // remove the first argument as it is inserted explicitly and creates a
    159  // mismatch with the parameters of the method declaration.
    160  if (isInfixBinaryOp(OpCall) && FuncDecl->getNumParams() == 1) {
    161    Args++;
    162    NumArgs--;
    163  }
    164 
    165  return escapesFunction(Arg, FuncDecl, Args, NumArgs);
    166 }
    167 
    168 ErrorOr<std::tuple<const Stmt *, const Decl *>>
    169 escapesFunction(const Expr *Arg, const FunctionDecl *FuncDecl,
    170                const Expr *const *Arguments, unsigned NumArgs) {
    171  if (!NumArgs) {
    172    return std::make_tuple((const Stmt *)nullptr, (const Decl *)nullptr);
    173  }
    174 
    175  if (FuncDecl->getBuiltinID() != 0 ||
    176      ASTIsInSystemHeader(FuncDecl->getASTContext(), *FuncDecl)) {
    177    return EscapesFunctionError::FunctionIsBuiltin;
    178  }
    179 
    180  // FIXME: should probably be handled at some point, but it's too annoying
    181  // for now.
    182  if (FuncDecl->isVariadic()) {
    183    return EscapesFunctionError::FunctionIsVariadic;
    184  }
    185 
    186  // We find the argument number corresponding to the Arg expression.
    187  unsigned ArgNum = 0;
    188  for (unsigned i = 0; i < NumArgs; i++) {
    189    if (IgnoreTrivials(Arg) == IgnoreTrivials(Arguments[i])) {
    190      break;
    191    }
    192    ++ArgNum;
    193  }
    194  // If we don't find it, we early-return NoneType.
    195  if (ArgNum >= NumArgs) {
    196    return EscapesFunctionError::ExprNotInCall;
    197  }
    198 
    199  // Now we get the associated parameter.
    200  if (ArgNum >= FuncDecl->getNumParams()) {
    201    return EscapesFunctionError::NoParamForArg;
    202  }
    203  auto Param = FuncDecl->getParamDecl(ArgNum);
    204 
    205  // We want both the argument and the parameter to be of pointer type.
    206  // FIXME: this is enough for the DanglingOnTemporaryChecker, because the
    207  // analysed methods only return pointers, but more cases should probably be
    208  // handled when we want to use this function more broadly.
    209  if ((!Arg->getType().getNonReferenceType()->isPointerType() &&
    210       Arg->getType().getNonReferenceType()->isBuiltinType()) ||
    211      (!Param->getType().getNonReferenceType()->isPointerType() &&
    212       Param->getType().getNonReferenceType()->isBuiltinType())) {
    213    return EscapesFunctionError::ArgAndParamNotPointers;
    214  }
    215 
    216  // We retrieve the usages of the parameter in the function.
    217  auto Usages = getUsageAsRvalue(Param, FuncDecl);
    218 
    219  // For each usage, we check if it doesn't allow the parameter to escape the
    220  // function scope.
    221  for (auto Usage : Usages) {
    222    // In the case of an assignment.
    223    if (auto BinOp = dyn_cast<BinaryOperator>(Usage)) {
    224      // We retrieve the declaration the parameter is assigned to.
    225      auto DeclRef = dyn_cast<DeclRefExpr>(BinOp->getLHS());
    226      if (!DeclRef) {
    227        continue;
    228      }
    229 
    230      if (auto ParamDeclaration = dyn_cast<ParmVarDecl>(DeclRef->getDecl())) {
    231        // This is the case where the parameter escapes through another
    232        // parameter.
    233 
    234        // FIXME: for now we only care about references because we only detect
    235        // trivial LHS with just a DeclRefExpr, and not more complex cases like:
    236        // void func(Type* param1, Type** param2) {
    237        //   *param2 = param1;
    238        // }
    239        // This should be fixed when we have better/more helper functions to
    240        // help deal with this kind of lvalue expressions.
    241        if (!ParamDeclaration->getType()->isReferenceType()) {
    242          continue;
    243        }
    244 
    245        return std::make_tuple(Usage, (const Decl *)ParamDeclaration);
    246      } else if (auto VarDeclaration = dyn_cast<VarDecl>(DeclRef->getDecl())) {
    247        // This is the case where the parameter escapes through a global/static
    248        // variable.
    249        if (!VarDeclaration->hasGlobalStorage()) {
    250          continue;
    251        }
    252 
    253        return std::make_tuple(Usage, (const Decl *)VarDeclaration);
    254      } else if (auto FieldDeclaration =
    255                     dyn_cast<FieldDecl>(DeclRef->getDecl())) {
    256        // This is the case where the parameter escapes through a field.
    257 
    258        return std::make_tuple(Usage, (const Decl *)FieldDeclaration);
    259      }
    260    } else if (isa<ReturnStmt>(Usage)) {
    261      // This is the case where the parameter escapes through the return value
    262      // of the function.
    263      if (!FuncDecl->getReturnType()->isPointerType() &&
    264          !FuncDecl->getReturnType()->isReferenceType()) {
    265        continue;
    266      }
    267 
    268      return std::make_tuple(Usage, (const Decl *)FuncDecl);
    269    }
    270  }
    271 
    272  // No early-return, this means that we haven't found any case of funciton
    273  // escaping and that therefore the parameter remains in the function scope.
    274  return std::make_tuple((const Stmt *)nullptr, (const Decl *)nullptr);
    275 }