Cppcheck
checkautovariables.cpp
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 //---------------------------------------------------------------------------
00020 // Auto variables checks
00021 //---------------------------------------------------------------------------
00022 
00023 #include "checkautovariables.h"
00024 #include "symboldatabase.h"
00025 #include "checkuninitvar.h"
00026 
00027 #include <list>
00028 #include <string>
00029 
00030 //---------------------------------------------------------------------------
00031 
00032 
00033 // Register this check class into cppcheck by creating a static instance of it..
00034 namespace {
00035     static CheckAutoVariables instance;
00036 }
00037 
00038 
00039 bool CheckAutoVariables::isPtrArg(const Token *tok)
00040 {
00041     const Variable *var = tok->variable();
00042 
00043     return(var && var->isArgument() && var->isPointer());
00044 }
00045 
00046 bool CheckAutoVariables::isRefPtrArg(const Token *tok)
00047 {
00048     const Variable *var = tok->variable();
00049 
00050     return(var && var->isArgument() && var->isReference() && var->isPointer());
00051 }
00052 
00053 bool CheckAutoVariables::isNonReferenceArg(const Token *tok)
00054 {
00055     const Variable *var = tok->variable();
00056 
00057     return(var && var->isArgument() && !var->isReference() && (var->isPointer() || var->typeStartToken()->isStandardType() || var->type()));
00058 }
00059 
00060 bool CheckAutoVariables::isAutoVar(const Token *tok)
00061 {
00062     const Variable *var = tok->variable();
00063 
00064     if (!var || !var->isLocal() || var->isStatic())
00065         return false;
00066 
00067     if (var->isReference()) {
00068         // address of reference variable can be taken if the address
00069         // of the variable it points at is not a auto-var
00070         // TODO: check what the reference variable references.
00071         return false;
00072     }
00073 
00074     return true;
00075 }
00076 
00077 bool CheckAutoVariables::isAutoVarArray(const Token *tok)
00078 {
00079     const Variable *var = tok->variable();
00080 
00081     return (var && var->isLocal() && !var->isStatic() && var->isArray());
00082 }
00083 
00084 // Verification that we really take the address of a local variable
00085 static bool checkRvalueExpression(const Variable* var, const Token* next)
00086 {
00087     return((next->str() != "." || (!var->isPointer() && (!var->isClass() || var->type()))) && next->strAt(2) != ".");
00088 }
00089 
00090 static bool variableIsUsedInScope(const Token* start, unsigned int varId, const Scope *scope)
00091 {
00092     for (const Token *tok = start; tok != scope->classEnd; tok = tok->next()) {
00093         if (tok->varId() == varId)
00094             return true;
00095         if (tok->scope()->type == Scope::eFor || tok->scope()->type == Scope::eDo || tok->scope()->type == Scope::eWhile) // In case of loops, better checking would be necessary
00096             return true;
00097         if (Token::simpleMatch(tok, "asm ("))
00098             return true;
00099     }
00100     return false;
00101 }
00102 
00103 void CheckAutoVariables::autoVariables()
00104 {
00105     const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
00106 
00107     const bool reportWarnings(_settings->isEnabled("warning"));
00108 
00109     const std::size_t functions = symbolDatabase->functionScopes.size();
00110     for (std::size_t i = 0; i < functions; ++i) {
00111         const Scope * scope = symbolDatabase->functionScopes[i];
00112         for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) {
00113             // Critical assignment
00114             if (Token::Match(tok, "[;{}] %var% = & %var%") && isRefPtrArg(tok->next()) && isAutoVar(tok->tokAt(4))) {
00115                 const Variable * var = tok->tokAt(4)->variable();
00116                 if (checkRvalueExpression(var, tok->tokAt(5)))
00117                     errorAutoVariableAssignment(tok->next(), false);
00118             } else if (Token::Match(tok, "[;{}] * %var% = & %var%") && isPtrArg(tok->tokAt(2)) && isAutoVar(tok->tokAt(5))) {
00119                 const Variable * var = tok->tokAt(5)->variable();
00120                 if (checkRvalueExpression(var, tok->tokAt(6)))
00121                     errorAutoVariableAssignment(tok->next(), false);
00122             } else if (reportWarnings &&
00123                        Token::Match(tok, "[;{}] %var% =") &&
00124                        isNonReferenceArg(tok->next()) &&
00125                        !variableIsUsedInScope(Token::findsimplematch(tok->tokAt(2), ";"), tok->next()->varId(), scope)) {
00126                 errorUselessAssignmentPtrArg(tok->next());
00127             } else if (Token::Match(tok, "[;{}] %var% . %var% = & %var%")) {
00128                 // TODO: check if the parameter is only changed temporarily (#2969)
00129                 if (_settings->inconclusive) {
00130                     const Variable * var1 = tok->next()->variable();
00131                     if (var1 && var1->isArgument() && var1->isPointer()) {
00132                         const Variable * var2 = tok->tokAt(6)->variable();
00133                         if (isAutoVar(tok->tokAt(6)) && checkRvalueExpression(var2, tok->tokAt(7)))
00134                             errorAutoVariableAssignment(tok->next(), true);
00135                     }
00136                 }
00137                 tok = tok->tokAt(6);
00138             } else if (Token::Match(tok, "[;{}] %var% . %var% = %var% ;")) {
00139                 // TODO: check if the parameter is only changed temporarily (#2969)
00140                 if (_settings->inconclusive) {
00141                     const Variable * var1 = tok->next()->variable();
00142                     if (var1 && var1->isArgument() && var1->isPointer()) {
00143                         if (isAutoVarArray(tok->tokAt(5)))
00144                             errorAutoVariableAssignment(tok->next(), true);
00145                     }
00146                 }
00147                 tok = tok->tokAt(5);
00148             } else if (Token::Match(tok, "[;{}] * %var% = %var% ;")) {
00149                 const Variable * var1 = tok->tokAt(2)->variable();
00150                 if (var1 && var1->isArgument() && Token::Match(var1->nameToken()->tokAt(-3), "%type% * *")) {
00151                     if (isAutoVarArray(tok->tokAt(4)))
00152                         errorAutoVariableAssignment(tok->next(), false);
00153                 }
00154                 tok = tok->tokAt(4);
00155             } else if (Token::Match(tok, "[;{}] %var% [") && Token::Match(tok->linkAt(2), "] = & %var%") && isPtrArg(tok->next()) && isAutoVar(tok->linkAt(2)->tokAt(3))) {
00156                 const Token* const varTok = tok->linkAt(2)->tokAt(3);
00157                 const Variable * var = varTok->variable();
00158                 if (checkRvalueExpression(var, varTok->next()))
00159                     errorAutoVariableAssignment(tok->next(), false);
00160             }
00161             // Critical return
00162             else if (Token::Match(tok, "return & %var% ;") && isAutoVar(tok->tokAt(2))) {
00163                 errorReturnAddressToAutoVariable(tok);
00164             } else if (Token::Match(tok, "return & %var% [") &&
00165                        Token::simpleMatch(tok->linkAt(3), "] ;") &&
00166                        isAutoVarArray(tok->tokAt(2))) {
00167                 errorReturnAddressToAutoVariable(tok);
00168             } else if (Token::Match(tok, "return & %var% ;") && tok->tokAt(2)->varId()) {
00169                 const Variable * var1 = tok->tokAt(2)->variable();
00170                 if (var1 && var1->isArgument() && var1->typeEndToken()->str() != "&")
00171                     errorReturnAddressOfFunctionParameter(tok, tok->strAt(2));
00172             }
00173             // Invalid pointer deallocation
00174             else if (Token::Match(tok, "free ( %var% ) ;") || Token::Match(tok, "delete [| ]| (| %var% !![")) {
00175                 tok = Token::findmatch(tok->next(), "%var%");
00176                 if (isAutoVarArray(tok))
00177                     errorInvalidDeallocation(tok);
00178             }
00179         }
00180     }
00181 }
00182 
00183 //---------------------------------------------------------------------------
00184 
00185 void CheckAutoVariables::returnPointerToLocalArray()
00186 {
00187     const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
00188 
00189     const std::size_t functions = symbolDatabase->functionScopes.size();
00190     for (std::size_t i = 0; i < functions; ++i) {
00191         const Scope * scope = symbolDatabase->functionScopes[i];
00192         if (!scope->function)
00193             continue;
00194 
00195         const Token *tok = scope->function->tokenDef;
00196 
00197         // have we reached a function that returns a pointer
00198         if (tok->previous() && tok->previous()->str() == "*") {
00199             for (const Token *tok2 = scope->classStart->next(); tok2 && tok2 != scope->classEnd; tok2 = tok2->next()) {
00200                 // Return pointer to local array variable..
00201                 if (Token::Match(tok2, "return %var% ;")) {
00202                     if (isAutoVarArray(tok2->next())) {
00203                         errorReturnPointerToLocalArray(tok2);
00204                     }
00205                 }
00206             }
00207         }
00208     }
00209 }
00210 
00211 void CheckAutoVariables::errorReturnAddressToAutoVariable(const Token *tok)
00212 {
00213     reportError(tok, Severity::error, "returnAddressOfAutoVariable", "Address of an auto-variable returned.");
00214 }
00215 
00216 void CheckAutoVariables::errorReturnPointerToLocalArray(const Token *tok)
00217 {
00218     reportError(tok, Severity::error, "returnLocalVariable", "Pointer to local array variable returned.");
00219 }
00220 
00221 void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok, bool inconclusive)
00222 {
00223     if (!inconclusive) {
00224         reportError(tok, Severity::error, "autoVariables",
00225                     "Address of local auto-variable assigned to a function parameter.\n"
00226                     "Dangerous assignment - the function parameter is assigned the address of a local "
00227                     "auto-variable. Local auto-variables are reserved from the stack which "
00228                     "is freed when the function ends. So the pointer to a local variable "
00229                     "is invalid after the function ends.");
00230     } else {
00231         reportError(tok, Severity::error, "autoVariables",
00232                     "Address of local auto-variable assigned to a function parameter.\n"
00233                     "Function parameter is assigned the address of a local auto-variable. "
00234                     "Local auto-variables are reserved from the stack which is freed when "
00235                     "the function ends. The address is invalid after the function ends and it "
00236                     "might 'leak' from the function through the parameter.", true);
00237     }
00238 }
00239 
00240 void CheckAutoVariables::errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname)
00241 {
00242     reportError(tok, Severity::error, "returnAddressOfFunctionParameter",
00243                 "Address of function parameter '" + varname + "' returned.\n"
00244                 "Address of the function parameter '" + varname + "' becomes invalid after the function exits because "
00245                 "function parameters are stored on the stack which is freed when the function exits. Thus the returned "
00246                 "value is invalid.");
00247 }
00248 
00249 void CheckAutoVariables::errorUselessAssignmentPtrArg(const Token *tok)
00250 {
00251     reportError(tok,
00252                 Severity::warning,
00253                 "uselessAssignmentPtrArg",
00254                 "Assignment of function parameter has no effect outside the function.");
00255 }
00256 
00257 //---------------------------------------------------------------------------
00258 
00259 // return temporary?
00260 bool CheckAutoVariables::returnTemporary(const Token *tok) const
00261 {
00262     const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
00263 
00264     bool func = false;     // Might it be a function call?
00265     bool retref = false;   // is there such a function that returns a reference?
00266     bool retvalue = false; // is there such a function that returns a value?
00267 
00268     const Function *function = tok->function();
00269     if (function) {
00270         retref = function->tokenDef->strAt(-1) == "&";
00271         if (!retref) {
00272             const Token *start = function->tokenDef;
00273             while (start->previous() && !Token::Match(start->previous(), ";|}|{|public:|private:|protected:")) {
00274                 if ((start->str() == ")" || start->str() == ">") && start->link())
00275                     start = start->link();
00276                 start = start->previous();
00277             }
00278             if (start->str() == "const")
00279                 start = start->next();
00280             if (start->str() == "::")
00281                 start = start->next();
00282 
00283             if (Token::simpleMatch(start, "std ::")) {
00284                 if (start->strAt(3) != "<" || !Token::simpleMatch(start->linkAt(3), "> ::"))
00285                     retvalue = true;
00286                 else
00287                     retref = true; // Assume that a reference is returned
00288             } else {
00289                 if (symbolDatabase->isClassOrStruct(start->str()))
00290                     retvalue = true;
00291                 else
00292                     retref = true;
00293             }
00294         }
00295         func = true;
00296     }
00297     if (!func && symbolDatabase->isClassOrStruct(tok->str()))
00298         return true;
00299 
00300     return bool(!retref && retvalue);
00301 }
00302 
00303 //---------------------------------------------------------------------------
00304 
00305 void CheckAutoVariables::returnReference()
00306 {
00307     const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
00308 
00309     const std::size_t functions = symbolDatabase->functionScopes.size();
00310     for (std::size_t i = 0; i < functions; ++i) {
00311         const Scope * scope = symbolDatabase->functionScopes[i];
00312         if (!scope->function)
00313             continue;
00314 
00315         const Token *tok = scope->function->tokenDef;
00316 
00317         // have we reached a function that returns a reference?
00318         if (tok->previous() && tok->previous()->str() == "&") {
00319             for (const Token *tok2 = scope->classStart->next(); tok2 && tok2 != scope->classEnd; tok2 = tok2->next()) {
00320                 // return..
00321                 if (Token::Match(tok2, "return %var% ;")) {
00322                     // is the returned variable a local variable?
00323                     if (isAutoVar(tok2->next())) {
00324                         const Variable *var1 = tok2->next()->variable();
00325                         // If reference variable is used, check what it references
00326                         if (Token::Match(var1->nameToken(), "%var% [=(]")) {
00327                             const Token *tok3 = var1->nameToken()->tokAt(2);
00328                             if (!Token::Match(tok3, "%var% [);.]"))
00329                                 continue;
00330 
00331                             // Only report error if variable that is referenced is
00332                             // a auto variable
00333                             if (!isAutoVar(tok3))
00334                                 continue;
00335                         }
00336 
00337                         // report error..
00338                         errorReturnReference(tok2);
00339                     }
00340                 }
00341 
00342                 // return reference to temporary..
00343                 else if (Token::Match(tok2, "return %var% (") &&
00344                          Token::simpleMatch(tok2->linkAt(2), ") ;")) {
00345                     if (returnTemporary(tok2->next())) {
00346                         // report error..
00347                         errorReturnTempReference(tok2);
00348                     }
00349                 }
00350             }
00351         }
00352     }
00353 }
00354 
00355 void CheckAutoVariables::errorReturnReference(const Token *tok)
00356 {
00357     reportError(tok, Severity::error, "returnReference", "Reference to auto variable returned.");
00358 }
00359 
00360 void CheckAutoVariables::errorReturnTempReference(const Token *tok)
00361 {
00362     reportError(tok, Severity::error, "returnTempReference", "Reference to temporary returned.");
00363 }
00364 
00365 void CheckAutoVariables::errorInvalidDeallocation(const Token *tok)
00366 {
00367     reportError(tok,
00368                 Severity::error,
00369                 "autovarInvalidDeallocation",
00370                 "Deallocation of an auto-variable results in undefined behaviour.\n"
00371                 "The deallocation of an auto-variable results in undefined behaviour. You should only free memory "
00372                 "that has been allocated dynamically.");
00373 }