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 
getUsageAsRvalue(const ValueDecl * ValueDeclaration,const FunctionDecl * FuncDecl)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       Optional<CFGStmt> 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 
name() const88 const char *EscapesFunctionErrorCategory::name() const noexcept {
89   return "escapes function";
90 }
91 
message(int ev) const92 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 
make_error_code(EscapesFunctionError e)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 *>>
escapesFunction(const Expr * Arg,const CXXConstructExpr * Construct)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 *>>
escapesFunction(const Expr * Arg,const CallExpr * Call)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 *>>
escapesFunction(const Expr * Arg,const CXXOperatorCallExpr * OpCall)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 *>>
escapesFunction(const Expr * Arg,const FunctionDecl * FuncDecl,const Expr * const * Arguments,unsigned NumArgs)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 }
276