|
Cppcheck
|
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 }
1.7.6.1