|
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 // Buffer overrun.. 00021 //--------------------------------------------------------------------------- 00022 00023 #include "checkbufferoverrun.h" 00024 00025 #include "tokenize.h" 00026 #include "errorlogger.h" 00027 #include "mathlib.h" 00028 #include "symboldatabase.h" 00029 00030 #include <algorithm> 00031 #include <sstream> 00032 #include <list> 00033 #include <cassert> // <- assert 00034 #include <cstdlib> 00035 00036 //--------------------------------------------------------------------------- 00037 00038 // Register this check class (by creating a static instance of it) 00039 namespace { 00040 CheckBufferOverrun instance; 00041 } 00042 00043 //--------------------------------------------------------------------------- 00044 00045 static void makeArrayIndexOutOfBoundsError(std::ostream& oss, const CheckBufferOverrun::ArrayInfo &arrayInfo, const std::vector<MathLib::bigint> &index) 00046 { 00047 oss << "Array '" << arrayInfo.varname(); 00048 for (unsigned int i = 0; i < arrayInfo.num().size(); ++i) 00049 oss << "[" << arrayInfo.num(i) << "]"; 00050 if (index.size() == 1) 00051 oss << "' accessed at index " << index[0] << ", which is"; 00052 else { 00053 oss << "' index " << arrayInfo.varname(); 00054 for (unsigned int i = 0; i < index.size(); ++i) 00055 oss << "[" << index[i] << "]"; 00056 } 00057 oss << " out of bounds."; 00058 } 00059 void CheckBufferOverrun::arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector<MathLib::bigint> &index) 00060 { 00061 std::ostringstream oss; 00062 makeArrayIndexOutOfBoundsError(oss, arrayInfo, index); 00063 reportError(tok, Severity::error, "arrayIndexOutOfBounds", oss.str()); 00064 } 00065 00066 void CheckBufferOverrun::arrayIndexOutOfBoundsError(const std::list<const Token *> &callstack, const ArrayInfo &arrayInfo, const std::vector<MathLib::bigint> &index) 00067 { 00068 std::ostringstream oss; 00069 makeArrayIndexOutOfBoundsError(oss, arrayInfo, index); 00070 reportError(callstack, Severity::error, "arrayIndexOutOfBounds", oss.str()); 00071 } 00072 00073 static std::string bufferOverrunMessage(std::string varnames) 00074 { 00075 varnames.erase(std::remove(varnames.begin(), varnames.end(), ' '), varnames.end()); 00076 00077 std::string errmsg("Buffer is accessed out of bounds"); 00078 if (!varnames.empty()) 00079 errmsg += ": " + varnames; 00080 else 00081 errmsg += "."; 00082 00083 return errmsg; 00084 } 00085 00086 void CheckBufferOverrun::bufferOverrunError(const Token *tok, const std::string &varnames) 00087 { 00088 reportError(tok, Severity::error, "bufferAccessOutOfBounds", bufferOverrunMessage(varnames)); 00089 } 00090 00091 00092 void CheckBufferOverrun::bufferOverrunError(const std::list<const Token *> &callstack, const std::string &varnames) 00093 { 00094 reportError(callstack, Severity::error, "bufferAccessOutOfBounds", bufferOverrunMessage(varnames)); 00095 } 00096 00097 void CheckBufferOverrun::possibleBufferOverrunError(const Token *tok, const std::string &src, const std::string &dst, bool cat) 00098 { 00099 if (cat) 00100 reportError(tok, Severity::warning, "possibleBufferAccessOutOfBounds", 00101 "Possible buffer overflow if strlen(" + src + ") is larger than sizeof(" + dst + ")-strlen(" + dst +").\n" 00102 "Possible buffer overflow if strlen(" + src + ") is larger than sizeof(" + dst + ")-strlen(" + dst +"). " 00103 "The source buffer is larger than the destination buffer so there is the potential for overflowing the destination buffer."); 00104 else 00105 reportError(tok, Severity::warning, "possibleBufferAccessOutOfBounds", 00106 "Possible buffer overflow if strlen(" + src + ") is larger than or equal to sizeof(" + dst + ").\n" 00107 "Possible buffer overflow if strlen(" + src + ") is larger than or equal to sizeof(" + dst + "). " 00108 "The source buffer is larger than the destination buffer so there is the potential for overflowing the destination buffer."); 00109 } 00110 00111 void CheckBufferOverrun::possibleReadlinkBufferOverrunError(const Token* tok, const std::string &funcname, const std::string &varname) 00112 { 00113 const std::string errmsg = funcname + "() might return the full size of '" + varname + "'. Lower the supplied size by one.\n" + 00114 funcname + "() might return the full size of '" + varname + "'. " 00115 "If a " + varname + "[len] = '\\0'; statement follows, it will overrun the buffer. Lower the supplied size by one."; 00116 00117 reportError(tok, Severity::warning, "possibleReadlinkBufferOverrun", errmsg, true); 00118 } 00119 00120 void CheckBufferOverrun::strncatUsageError(const Token *tok) 00121 { 00122 if (_settings && !_settings->isEnabled("warning")) 00123 return; 00124 00125 reportError(tok, Severity::warning, "strncatUsage", 00126 "Dangerous usage of strncat - 3rd parameter is the maximum number of characters to append.\n" 00127 "strncat appends at max its 3rd parameter's amount of characters. The safe way to use " 00128 "strncat is to calculate remaining space in the buffer and use it as 3rd parameter."); 00129 } 00130 00131 void CheckBufferOverrun::outOfBoundsError(const Token *tok, const std::string &what, const bool show_size_info, const MathLib::bigint &supplied_size, const MathLib::bigint &actual_size) 00132 { 00133 std::ostringstream oss; 00134 00135 oss << what << " is out of bounds"; 00136 if (show_size_info) 00137 oss << ": Supplied size " << supplied_size << " is larger than actual size " << actual_size; 00138 oss << '.'; 00139 reportError(tok, Severity::error, "outOfBounds", oss.str()); 00140 } 00141 00142 void CheckBufferOverrun::pointerOutOfBoundsError(const Token *tok, const std::string &object) 00143 { 00144 reportError(tok, Severity::portability, "pointerOutOfBounds", "Undefined behaviour: Pointer arithmetic result does not point into or just past the end of the " + object + ".\n" 00145 "Undefined behaviour: The result of this pointer arithmetic does not point into or just one element past the end of the " + object + ". Further information: https://www.securecoding.cert.org/confluence/display/seccode/ARR30-C.+Do+not+form+or+use+out+of+bounds+pointers+or+array+subscripts"); 00146 } 00147 00148 void CheckBufferOverrun::sizeArgumentAsCharError(const Token *tok) 00149 { 00150 if (_settings && !_settings->isEnabled("warning")) 00151 return; 00152 reportError(tok, Severity::warning, "sizeArgumentAsChar", "The size argument is given as a char constant."); 00153 } 00154 00155 00156 void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname) 00157 { 00158 reportError(tok, Severity::warning, "terminateStrncpy", 00159 "The buffer '" + varname + "' may not be null-terminated after the call to strncpy().\n" 00160 "If the source string's size fits or exceeds the given size, strncpy() does not add a " 00161 "zero at the end of the buffer. This causes bugs later in the code if the code " 00162 "assumes buffer is null-terminated.", true); 00163 } 00164 00165 void CheckBufferOverrun::cmdLineArgsError(const Token *tok) 00166 { 00167 reportError(tok, Severity::error, "insecureCmdLineArgs", "Buffer overrun possible for long command line arguments."); 00168 } 00169 00170 void CheckBufferOverrun::bufferNotZeroTerminatedError(const Token *tok, const std::string &varname, const std::string &function) 00171 { 00172 const std::string errmsg = "The buffer '" + varname + "' is not null-terminated after the call to " + function + "().\n" 00173 "The buffer '" + varname + "' is not null-terminated after the call to " + function + "(). " 00174 "This will cause bugs later in the code if the code assumes the buffer is null-terminated."; 00175 00176 reportError(tok, Severity::warning, "bufferNotZeroTerminated", errmsg, true); 00177 } 00178 00179 void CheckBufferOverrun::argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname) 00180 { 00181 reportError(tok, Severity::warning, "argumentSize", "The array '" + varname + "' is too small, the function '" + functionName + "' expects a bigger one."); 00182 } 00183 00184 //--------------------------------------------------------------------------- 00185 00186 00187 //--------------------------------------------------------------------------- 00188 // Check array usage.. 00189 //--------------------------------------------------------------------------- 00190 00191 /** 00192 * @brief This is a helper class to be used with std::find_if 00193 */ 00194 class TokenStrEquals { 00195 public: 00196 /** 00197 * @param str Token::str() is compared against this. 00198 */ 00199 explicit TokenStrEquals(const std::string &str) 00200 : value(str) { 00201 } 00202 00203 /** 00204 * Called automatically by std::find_if 00205 * @param tok Token inside the list 00206 */ 00207 bool operator()(const Token *tok) const { 00208 return value == tok->str(); 00209 } 00210 00211 private: 00212 TokenStrEquals& operator=(const TokenStrEquals&); // disallow assignments 00213 00214 const std::string value; 00215 }; 00216 00217 00218 /** 00219 * bailout if variable is used inside if/else/switch block or if there is "break" 00220 * @param tok token for "if" or "switch" 00221 * @param varid variable id 00222 * @return is bailout recommended? 00223 */ 00224 static bool bailoutIfSwitch(const Token *tok, const unsigned int varid) 00225 { 00226 // Used later to check if the body belongs to a "if" 00227 bool is_if = tok->str() == "if"; 00228 00229 const Token* end = tok->linkAt(1)->linkAt(1); 00230 if (Token::simpleMatch(end, "} else {")) // scan the else-block 00231 end = end->linkAt(2); 00232 for (; tok != end; tok = tok->next()) { 00233 // If scanning a "if" block then bailout for "break" 00234 if (is_if && (tok->str() == "break" || tok->str() == "continue")) 00235 return true; 00236 00237 // bailout for "return" 00238 else if (tok->str() == "return") 00239 return true; 00240 00241 // bailout if varid is found 00242 else if (tok->varId() == varid) 00243 return true; 00244 } 00245 00246 // No bailout stuff found => return false 00247 return false; 00248 } 00249 00250 /** 00251 * Parse for loop initialization statement. Look for a counter variable 00252 * \param tok [in] first token inside the parentheses 00253 * \param varid [out] varid of counter variable 00254 * \param varname [out] name of counter variable 00255 * \param init_value [out] init value of counter variable 00256 * \return success => pointer to the for loop condition. fail => 0. If 0 is returned and varname has been set then there is 00257 * a missing varid for the counter variable 00258 */ 00259 static const Token *for_init(const Token *tok, unsigned int &varid, std::string &varname, std::string &init_value) 00260 { 00261 if (Token::Match(tok, "%var% = %any% ;")) { 00262 if (tok->tokAt(2)->isNumber()) { 00263 init_value = tok->strAt(2); 00264 } 00265 00266 varid = tok->varId(); 00267 varname = tok->str(); 00268 tok = tok->tokAt(4); 00269 00270 if (varid == 0) 00271 return 0; // failed 00272 } else if (Token::Match(tok, "%type% %var% = %any% ;")) { 00273 if (tok->tokAt(3)->isNumber()) { 00274 init_value = tok->strAt(3); 00275 } 00276 00277 varid = tok->next()->varId(); 00278 varname = tok->next()->str(); 00279 tok = tok->tokAt(5); 00280 } else if (Token::Match(tok, "%type% %type% %var% = %any% ;")) { 00281 if (tok->tokAt(4)->isNumber()) { 00282 init_value = tok->strAt(4); 00283 } 00284 00285 varid = tok->tokAt(2)->varId(); 00286 varname = tok->strAt(2); 00287 tok = tok->tokAt(6); 00288 } else 00289 return 0; 00290 00291 if (!init_value.empty() && (Token::Match(tok, "-- %varid%", varid) || Token::Match(tok, "%varid% --", varid))) { 00292 init_value = MathLib::subtract(init_value, "1"); 00293 } 00294 00295 return tok; 00296 } 00297 00298 00299 /** Parse for condition */ 00300 static bool for_condition(const Token *tok2, unsigned int varid, std::string &min_value, std::string &max_value, bool &maxMinFlipped) 00301 { 00302 if (Token::Match(tok2, "%varid% < %num% ;|&&|%oror%", varid) || 00303 Token::Match(tok2, "%varid% != %num% ; ++ %varid%", varid) || 00304 Token::Match(tok2, "%varid% != %num% ; %varid% ++", varid)) { 00305 maxMinFlipped = false; 00306 const MathLib::bigint value = MathLib::toLongNumber(tok2->strAt(2)); 00307 max_value = MathLib::longToString(value - 1); 00308 } else if (Token::Match(tok2, "%varid% <= %num% ;|&&|%oror%", varid)) { 00309 maxMinFlipped = false; 00310 max_value = tok2->strAt(2); 00311 } else if (Token::Match(tok2, "%num% < %varid% ;|&&|%oror%", varid) || 00312 Token::Match(tok2, "%num% != %varid% ; ++ %varid%", varid) || 00313 Token::Match(tok2, "%num% != %varid% ; %varid% ++", varid)) { 00314 maxMinFlipped = true; 00315 const MathLib::bigint value = MathLib::toLongNumber(tok2->str()); 00316 max_value = min_value; 00317 min_value = MathLib::longToString(value + 1); 00318 } else if (Token::Match(tok2, "%num% <= %varid% ;|&&|%oror%", varid)) { 00319 maxMinFlipped = true; 00320 max_value = min_value; 00321 min_value = tok2->str(); 00322 } else { 00323 // parse condition 00324 while (tok2 && tok2->str() != ";") { 00325 if (tok2->str() == "(") 00326 tok2 = tok2->link(); 00327 else if (tok2->str() == ")") // unexpected ")" => break 00328 break; 00329 if (tok2->str() == "&&" || tok2->str() == "||") { 00330 if (for_condition(tok2->next(), varid, min_value, max_value, maxMinFlipped)) 00331 return true; 00332 } 00333 tok2 = tok2->next(); 00334 } 00335 return false; 00336 } 00337 00338 return true; 00339 } 00340 00341 00342 00343 /** 00344 * calculate maximum value of loop variable 00345 * @param stepvalue token that contains the step value 00346 * @param min_value the minimum value of loop variable 00347 * @param max_value maximum value of the loop variable 00348 */ 00349 static bool for_maxvalue(const Token * const stepvalue, const std::string &min_value, std::string &max_value) 00350 { 00351 if (!MathLib::isInt(stepvalue->str())) 00352 return false; 00353 00354 // We have for example code: "for(i=2;i<22;i+=6) 00355 // We can calculate that max value for i is 20, not 21 00356 // 21-2 = 19 00357 // 19/6 = 3 00358 // 6*3+2 = 20 00359 const MathLib::bigint num = MathLib::toLongNumber(stepvalue->str()); 00360 MathLib::bigint max = MathLib::toLongNumber(max_value); 00361 const MathLib::bigint min = MathLib::toLongNumber(min_value); 00362 max = ((max - min) / num) * num + min; 00363 max_value = MathLib::longToString(max); 00364 return true; 00365 } 00366 00367 00368 /** 00369 * Parse the third sub-statement in for head 00370 * \param tok first token 00371 * \param varid variable id of counter 00372 * \param min_value min value of counter 00373 * \param max_value max value of counter 00374 * \param maxMinFlipped counting from max to min 00375 */ 00376 static bool for3(const Token * const tok, 00377 unsigned int varid, 00378 std::string &min_value, 00379 std::string &max_value, 00380 const bool maxMinFlipped) 00381 { 00382 assert(tok != 0); 00383 if (Token::Match(tok, "%varid% = %num% + %varid% )", varid)) { 00384 if (!for_maxvalue(tok->tokAt(2), min_value, max_value)) 00385 return false; 00386 } else if (Token::Match(tok, "%varid% = %varid% + %num% )", varid)) { 00387 if (!for_maxvalue(tok->tokAt(4), min_value, max_value)) 00388 return false; 00389 } else if (Token::Match(tok, "%varid% = %num% - %varid% )", varid)) { 00390 if (!for_maxvalue(tok->tokAt(2), min_value, max_value)) 00391 return false; 00392 } else if (Token::Match(tok, "%varid% = %varid% - %num% )", varid)) { 00393 if (!for_maxvalue(tok->tokAt(4), min_value, max_value)) 00394 return false; 00395 } else if (Token::Match(tok, "--| %varid% --| )", varid)) { 00396 if (!maxMinFlipped && MathLib::toLongNumber(min_value) < MathLib::toLongNumber(max_value)) { 00397 // Code relies on the fact that integer will overflow: 00398 // for (unsigned int i = 3; i < 5; --i) 00399 00400 // Set min value in this case to zero. 00401 max_value = min_value; 00402 min_value = "0"; 00403 } 00404 } else if (! Token::Match(tok, "++| %varid% ++| )", varid)) { 00405 return false; 00406 } 00407 return true; 00408 } 00409 00410 00411 00412 /** 00413 * Check is the counter variable increased elsewhere inside the loop or used 00414 * for anything else except reading 00415 * \param tok1 first token of for-body 00416 * \param varid counter variable id 00417 * \return bailout needed => true 00418 */ 00419 static bool for_bailout(const Token * const tok1, unsigned int varid) 00420 { 00421 for (const Token *loopTok = tok1; loopTok && loopTok != tok1->link(); loopTok = loopTok->next()) { 00422 if (loopTok->varId() == varid) { 00423 // Counter variable used inside loop 00424 if (Token::Match(loopTok->next(), "++|--|=") || 00425 (loopTok->previous()->type() == Token::eIncDecOp)) { 00426 return true; 00427 } 00428 } 00429 } 00430 return false; 00431 } 00432 00433 00434 void CheckBufferOverrun::parse_for_body(const Token *tok, const ArrayInfo &arrayInfo, const std::string &strindex, bool condition_out_of_bounds, unsigned int counter_varid, const std::string &min_counter_value, const std::string &max_counter_value) 00435 { 00436 const std::string pattern = (arrayInfo.varid() ? std::string("%varid%") : arrayInfo.varname()) + " [ " + strindex + " ]"; 00437 00438 for (const Token* tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { 00439 // TestBufferOverrun::array_index_for_question 00440 if (tok2->str() == "?") { 00441 // does condition check counter variable? 00442 bool usesCounter = false; 00443 const Token *tok3 = tok2->previous(); 00444 while (Token::Match(tok3, "%comp%|%num%|%var%|)")) { 00445 if (tok3->str() == strindex) { 00446 usesCounter = true; 00447 break; 00448 } 00449 tok3 = tok3->previous(); 00450 } 00451 00452 // If strindex is used in the condition then skip the 00453 // conditional expressions 00454 if (usesCounter) { 00455 while (tok2 && !Token::Match(tok2, "[)],;]")) { 00456 if (tok2->str() == "(" || tok2->str() == "[") 00457 tok2 = tok2->link(); 00458 tok2 = tok2->next(); 00459 } 00460 if (!tok2) 00461 break; 00462 continue; 00463 } 00464 } 00465 00466 if (Token::simpleMatch(tok2, "for (") && Token::simpleMatch(tok2->next()->link(), ") {")) { 00467 const Token *endpar = tok2->next()->link(); 00468 const Token *startbody = endpar->next(); 00469 const Token *endbody = startbody->link(); 00470 tok2 = endbody; 00471 continue; 00472 } 00473 00474 if (Token::Match(tok2, "if|switch")) { 00475 if (bailoutIfSwitch(tok2, arrayInfo.varid())) 00476 break; 00477 } 00478 00479 if (condition_out_of_bounds && Token::Match(tok2, pattern.c_str(), arrayInfo.varid())) { 00480 bufferOverrunError(tok2, arrayInfo.varname()); 00481 break; 00482 } 00483 00484 else if (arrayInfo.varid() && tok2->varId() && counter_varid > 0 && !min_counter_value.empty() && !max_counter_value.empty()) { 00485 // Is the loop variable used to calculate the array index? 00486 // In this scope it is determined if such calculated 00487 // array indexes are out of bounds. 00488 // Only the minimum and maximum results of the calculation is 00489 // determined 00490 00491 // Minimum calculated array index 00492 int min_index = 0; 00493 00494 // Maximum calculated array index 00495 int max_index = 0; 00496 00497 if (Token::Match(tok2, "%varid% [ %var% +|-|*|/ %num% ]", arrayInfo.varid()) && 00498 tok2->tokAt(2)->varId() == counter_varid) { 00499 // operator: +-*/ 00500 const char action = tok2->strAt(3)[0]; 00501 00502 // second operator 00503 const std::string &second(tok2->strAt(4)); 00504 00505 //printf("min_index: %s %c %s\n", min_counter_value.c_str(), action, second.c_str()); 00506 //printf("max_index: %s %c %s\n", max_counter_value.c_str(), action, second.c_str()); 00507 min_index = std::atoi(MathLib::calculate(min_counter_value, second, action).c_str()); 00508 max_index = std::atoi(MathLib::calculate(max_counter_value, second, action).c_str()); 00509 } else if (Token::Match(tok2, "%varid% [ %num% +|-|*|/ %var% ]", arrayInfo.varid()) && 00510 tok2->tokAt(4)->varId() == counter_varid) { 00511 // operator: +-*/ 00512 const char action = tok2->strAt(3)[0]; 00513 00514 // first operand 00515 const std::string &first(tok2->strAt(2)); 00516 00517 //printf("min_index: %s %c %s\n", first.c_str(), action, min_counter_value.c_str()); 00518 //printf("max_index: %s %c %s\n", first.c_str(), action, max_counter_value.c_str()); 00519 00520 min_index = std::atoi(MathLib::calculate(first, min_counter_value, action).c_str()); 00521 max_index = std::atoi(MathLib::calculate(first, max_counter_value, action).c_str()); 00522 } 00523 00524 else { 00525 continue; 00526 } 00527 00528 //printf("min_index = %d, max_index = %d, size = %d\n", min_index, max_index, size); 00529 if (min_index < 0 || max_index < 0) { 00530 std::vector<MathLib::bigint> indexes; 00531 indexes.push_back(std::min(min_index, max_index)); 00532 arrayIndexOutOfBoundsError(tok2, arrayInfo, indexes); 00533 } 00534 00535 // skip 0 length arrays 00536 if (arrayInfo.num(0) == 0) 00537 ; 00538 00539 // taking address. 00540 else if (tok2->previous()->str() == "&" && max_index == arrayInfo.num(0)) 00541 ; 00542 00543 else if (arrayInfo.num(0) && (min_index >= arrayInfo.num(0) || max_index >= arrayInfo.num(0))) { 00544 std::vector<MathLib::bigint> indexes; 00545 indexes.push_back(std::max(min_index, max_index)); 00546 arrayIndexOutOfBoundsError(tok2, arrayInfo, indexes); 00547 } 00548 } 00549 } 00550 } 00551 00552 00553 void CheckBufferOverrun::checkFunctionParameter(const Token &tok, unsigned int par, const ArrayInfo &arrayInfo, std::list<const Token *> callstack) 00554 { 00555 // total_size : which parameter in function call takes the total size? 00556 std::map<std::string, unsigned int> total_size; 00557 00558 total_size["fgets"] = 2; // The second argument for fgets can't exceed the total size of the array 00559 total_size["memcmp"] = 3; 00560 total_size["memcpy"] = 3; 00561 total_size["memmove"] = 3; 00562 total_size["memchr"] = 3; 00563 00564 if (par == 1) { 00565 // reading from array 00566 // if it is zero terminated properly there won't be buffer overruns 00567 total_size["strncat"] = 3; 00568 total_size["strncpy"] = 3; 00569 total_size["memset"] = 3; 00570 total_size["fread"] = 1001; // parameter 2 * parameter 3 00571 total_size["fwrite"] = 1001; // parameter 2 * parameter 3 00572 } 00573 00574 else if (par == 2) { 00575 total_size["read"] = 3; 00576 total_size["pread"] = 3; 00577 total_size["write"] = 3; 00578 total_size["recv"] = 3; 00579 total_size["recvfrom"] = 3; 00580 total_size["send"] = 3; 00581 total_size["sendto"] = 3; 00582 } 00583 00584 std::map<std::string, unsigned int>::const_iterator it = total_size.find(tok.str()); 00585 if (it != total_size.end()) { 00586 if (arrayInfo.element_size() == 0) 00587 return; 00588 00589 // arg : the index of the "wanted" argument in the function call. 00590 unsigned int arg = it->second; 00591 00592 // Parse function call. When a ',' is seen, arg is decremented. 00593 // if arg becomes 1 then the current function parameter is the wanted parameter. 00594 // if arg becomes 1001 then multiply current and next argument. 00595 const Token *tok2 = tok.tokAt(2)->nextArgument(); 00596 if (arg == 3) 00597 tok2 = tok2->nextArgument(); 00598 if ((arg == 2 || arg == 3) && tok2) { 00599 if (Token::Match(tok2, "%num% ,|)")) { 00600 const MathLib::bigint sz = MathLib::toLongNumber(tok2->str()); 00601 MathLib::bigint elements = 1; 00602 for (unsigned int i = 0; i < arrayInfo.num().size(); ++i) 00603 elements *= arrayInfo.num(i); 00604 if (sz < 0 || sz > int(elements * arrayInfo.element_size())) { 00605 bufferOverrunError(callstack, arrayInfo.varname()); 00606 } 00607 } 00608 00609 else if (Token::Match(tok2->next(), ",|)") && tok2->type() == Token::eChar) { 00610 sizeArgumentAsCharError(tok2); 00611 } 00612 } else if (arg == 1001) { // special code. This parameter multiplied with the next must not exceed total_size 00613 if (Token::Match(tok2, "%num% , %num% ,|)")) { 00614 const MathLib::bigint sz = MathLib::toLongNumber(MathLib::multiply(tok2->str(), tok2->strAt(2))); 00615 MathLib::bigint elements = 1; 00616 for (unsigned int i = 0; i < arrayInfo.num().size(); ++i) 00617 elements *= arrayInfo.num(i); 00618 if (sz < 0 || sz > int(elements * arrayInfo.element_size())) { 00619 bufferOverrunError(&tok, arrayInfo.varname()); 00620 } 00621 } 00622 } 00623 } 00624 00625 // Calling a user function? 00626 // only 1-dimensional arrays can be checked currently 00627 else if (arrayInfo.num().size() == 1) { 00628 const Function* func = tok.function(); 00629 00630 if (func && func->hasBody) { 00631 // Get corresponding parameter.. 00632 const Variable* parameter = func->getArgumentVar(par-1); 00633 00634 // Ensure that it has a compatible size.. 00635 if (!parameter || _tokenizer->sizeOfType(parameter->typeStartToken()) != arrayInfo.element_size()) 00636 return; 00637 00638 // No variable id occur for instance when: 00639 // - Variable function arguments: "void f(...)" 00640 // - Unnamed parameter: "void f(char *)" 00641 if (parameter->varId() == 0) 00642 return; 00643 00644 // Check the parameter usage in the function scope.. 00645 for (const Token* ftok = func->functionScope->classStart; ftok != func->functionScope->classEnd; ftok = ftok->next()) { 00646 if (Token::Match(ftok, "if|for|while (")) { 00647 // bailout if there is buffer usage.. 00648 if (bailoutIfSwitch(ftok, parameter->varId())) { 00649 break; 00650 } 00651 00652 // no bailout is needed. skip the if-block 00653 else { 00654 // goto end of if block.. 00655 ftok = ftok->next()->link()->next()->link(); 00656 if (Token::simpleMatch(ftok, "} else {")) 00657 ftok = ftok->linkAt(2); 00658 if (!ftok) 00659 break; 00660 continue; 00661 } 00662 } 00663 00664 if (ftok->str() == "}") 00665 break; 00666 00667 if (ftok->varId() == parameter->varId()) { 00668 if (Token::Match(ftok->previous(), "-- %var%") || 00669 Token::Match(ftok, "%var% --")) 00670 break; 00671 00672 if (Token::Match(ftok->previous(), ";|{|}|%op% %var% [ %num% ]")) { 00673 const MathLib::bigint index = MathLib::toLongNumber(ftok->strAt(2)); 00674 if (index >= 0 && arrayInfo.num(0) > 0 && index >= arrayInfo.num(0)) { 00675 std::list<const Token *> callstack2(callstack); 00676 callstack2.push_back(ftok); 00677 00678 std::vector<MathLib::bigint> indexes; 00679 indexes.push_back(index); 00680 00681 arrayIndexOutOfBoundsError(callstack2, arrayInfo, indexes); 00682 } 00683 } 00684 } 00685 00686 // Calling function.. 00687 if (Token::Match(ftok, "%var% (")) { 00688 ArrayInfo ai(arrayInfo); 00689 ai.varid(parameter->varId()); 00690 checkFunctionCall(ftok, ai, callstack); 00691 } 00692 } 00693 } 00694 } 00695 00696 // Check 'float x[10]' arguments in declaration 00697 if (_settings->isEnabled("warning")) { 00698 const Function* func = tok.function(); 00699 00700 // If argument is '%type% a[num]' then check bounds against num 00701 if (func) { 00702 const Variable* argument = func->getArgumentVar(par-1); 00703 const Token *nameToken; 00704 if (argument && Token::Match(argument->typeStartToken(), "%type% %var% [ %num% ] [,)[]") 00705 && (nameToken = argument->nameToken()) != NULL) { 00706 const Token *tok2 = nameToken->next(); 00707 00708 MathLib::bigint argsize = _tokenizer->sizeOfType(argument->typeStartToken()); 00709 if (argsize == 100) // unknown size 00710 argsize = 0; 00711 while (Token::Match(tok2, "[ %num% ] [,)[]")) { 00712 argsize *= MathLib::toLongNumber(tok2->strAt(1)); 00713 tok2 = tok2->tokAt(3); 00714 } 00715 00716 MathLib::bigint arraysize = arrayInfo.element_size(); 00717 if (arraysize == 100) // unknown size 00718 arraysize = 0; 00719 for (unsigned int i = 0; i < arrayInfo.num().size(); i++) 00720 arraysize *= arrayInfo.num(i); 00721 00722 if (Token::Match(tok2, "[,)]") && arraysize > 0 && argsize > arraysize) 00723 argumentSizeError(&tok, tok.str(), arrayInfo.varname()); 00724 } 00725 } 00726 } 00727 } 00728 00729 00730 void CheckBufferOverrun::checkFunctionCall(const Token *tok, const ArrayInfo &arrayInfo, std::list<const Token *> callstack) 00731 { 00732 // Don't go deeper than 2 levels, the checking can get very slow 00733 // when there is no limit 00734 if (callstack.size() >= 2) 00735 return; 00736 00737 // Prevent recursion 00738 for (std::list<const Token*>::const_iterator it = callstack.begin(); it != callstack.end(); ++it) { 00739 // Same function name => bail out 00740 if (tok->str() == (*it)->str()) 00741 return; 00742 } 00743 callstack.push_back(tok); 00744 00745 const Token *tok2 = tok->tokAt(2); 00746 // 1st parameter.. 00747 if (Token::Match(tok2, "%varid% ,|)", arrayInfo.varid())) 00748 checkFunctionParameter(*tok, 1, arrayInfo, callstack); 00749 else if (Token::Match(tok2, "%varid% + %num% ,|)", arrayInfo.varid())) { 00750 const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(tok2->strAt(2)))); 00751 checkFunctionParameter(*tok, 1, ai, callstack); 00752 } 00753 00754 // goto 2nd parameter and check it.. 00755 tok2 = tok2->nextArgument(); 00756 if (Token::Match(tok2, "%varid% ,|)", arrayInfo.varid())) 00757 checkFunctionParameter(*tok, 2, arrayInfo, callstack); 00758 else if (Token::Match(tok2, "%varid% + %num% ,|)", arrayInfo.varid())) { 00759 const ArrayInfo ai(arrayInfo.limit(MathLib::toLongNumber(tok2->strAt(2)))); 00760 checkFunctionParameter(*tok, 2, ai, callstack); 00761 } 00762 } 00763 00764 00765 void CheckBufferOverrun::checkScopeForBody(const Token *tok, const ArrayInfo &arrayInfo, bool &bailout) 00766 { 00767 bailout = false; 00768 const Token *tok2 = tok->tokAt(2); 00769 const MathLib::bigint size = arrayInfo.num(0); 00770 00771 // Check if there is a break in the body.. 00772 { 00773 const Token *bodyStart = tok->next()->link()->next(); 00774 const Token *bodyEnd = bodyStart->link(); 00775 if (Token::findsimplematch(bodyStart, "break ;", bodyEnd)) 00776 return; 00777 } 00778 00779 std::string counter_name; 00780 unsigned int counter_varid = 0; 00781 std::string counter_init_value; 00782 00783 tok2 = for_init(tok2, counter_varid, counter_name, counter_init_value); 00784 if (tok2 == 0 && !counter_name.empty()) 00785 _tokenizer->getSymbolDatabase()->debugMessage(tok, "for loop variable \'" + counter_name + "\' has varid 0."); 00786 if (tok2 == 0 || counter_varid == 0) 00787 return; 00788 00789 bool maxMinFlipped = false; 00790 std::string min_counter_value = counter_init_value; 00791 std::string max_counter_value; 00792 if (!for_condition(tok2, counter_varid, min_counter_value, max_counter_value, maxMinFlipped)) { 00793 // Can't understand the condition. Check that the start value 00794 // is used correctly 00795 const Token *startForScope = tok->next()->link()->next(); 00796 if (!for_bailout(startForScope, counter_varid)) { 00797 // Get index variable and stopsize. 00798 bool condition_out_of_bounds = bool(size > 0); 00799 if (MathLib::toLongNumber(counter_init_value) < size) 00800 condition_out_of_bounds = false; 00801 00802 parse_for_body(startForScope, arrayInfo, counter_name, condition_out_of_bounds, counter_varid, counter_init_value, counter_init_value); 00803 } 00804 return; 00805 } 00806 00807 // Get index variable and stopsize. 00808 bool condition_out_of_bounds = bool(size > 0); 00809 if (MathLib::toLongNumber(max_counter_value) < size) 00810 condition_out_of_bounds = false; 00811 00812 // Goto the end of the condition 00813 while (tok2 && tok2->str() != ";") { 00814 if (tok2->str() == "(") 00815 tok2 = tok2->link(); 00816 else if (tok2->str() == ")") // unexpected ")" => break 00817 break; 00818 tok2 = tok2->next(); 00819 } 00820 if (!tok2 || tok2->str() != ";") 00821 return; 00822 if (!for3(tok2->next(), counter_varid, min_counter_value, max_counter_value, maxMinFlipped)) 00823 return; 00824 00825 if (Token::Match(tok2->next(), "%var% =") && MathLib::toLongNumber(max_counter_value) < size) 00826 condition_out_of_bounds = false; 00827 00828 // Goto the end parentheses of the for-statement: "for (x; y; z)" .. 00829 tok2 = tok->next()->link(); 00830 if (!tok2 || !tok2->tokAt(5)) { 00831 bailout = true; 00832 return; 00833 } 00834 00835 // Check is the counter variable increased elsewhere inside the loop or used 00836 // for anything else except reading 00837 if (for_bailout(tok2->next(), counter_varid)) { 00838 bailout = true; 00839 return; 00840 } 00841 00842 parse_for_body(tok2->next(), arrayInfo, counter_name, condition_out_of_bounds, counter_varid, min_counter_value, max_counter_value); 00843 } 00844 00845 void CheckBufferOverrun::arrayIndexInForLoop(const Token *tok, const ArrayInfo &arrayInfo) 00846 { 00847 const MathLib::bigint size = arrayInfo.num(0); 00848 const Token *tok3 = tok->tokAt(2); 00849 std::string counter_name; 00850 unsigned int counter_varid = 0; 00851 std::string counter_init_value; 00852 00853 tok3 = for_init(tok3, counter_varid, counter_name, counter_init_value); 00854 if (tok3 == 0 && !counter_name.empty()) 00855 _tokenizer->getSymbolDatabase()->debugMessage(tok, "for loop variable \'" + counter_name + "\' has varid 0."); 00856 if (tok3 == 0 || counter_varid == 0) 00857 return; 00858 00859 bool maxMinFlipped = false; 00860 std::string min_counter_value = counter_init_value; 00861 std::string max_counter_value; 00862 MathLib::bigint max_value = MathLib::toLongNumber(max_counter_value); 00863 00864 for_condition(tok3, counter_varid, min_counter_value, max_counter_value, maxMinFlipped); 00865 while (tok3 && tok3->str() != ";") { 00866 tok3 = tok3->next(); 00867 } 00868 00869 for (const Token* tok2 = tok; tok2; tok2 = tok2->next()) { 00870 if (Token::Match(tok2, "%var% < %num%")) { 00871 max_value = MathLib::toLongNumber(tok2->strAt(2)); 00872 max_value = max_value - 1; 00873 } 00874 } 00875 00876 if (max_value > size) { 00877 if (tok3 && tok3->strAt(1) == ")") { 00878 bool usedInArray = false; 00879 for (const Token *loopTok = tok3->tokAt(2); loopTok->str() != "}" ; loopTok = loopTok->next()) { 00880 if (loopTok->varId() == arrayInfo.varid() && loopTok->tokAt(2)->varId() == counter_varid) 00881 usedInArray = true; 00882 } 00883 00884 for (const Token *loopTok = tok3->tokAt(2); loopTok->str() != "}" ; loopTok = loopTok->next()) { 00885 if (usedInArray && (counter_varid == loopTok->varId())) { 00886 if (loopTok->strAt(1) == "++" || 00887 (loopTok->previous()->type() == Token::eIncDecOp)) { 00888 bufferOverrunError(tok, arrayInfo.varname()); 00889 } 00890 } 00891 } 00892 } 00893 } 00894 } 00895 00896 void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::string> &varname, const ArrayInfo &arrayInfo) 00897 { 00898 const MathLib::bigint size = arrayInfo.num(0); 00899 if (size == 0) // unknown size 00900 return; 00901 00902 const MathLib::bigint total_size = arrayInfo.element_size() * arrayInfo.num(0); 00903 unsigned int varid = arrayInfo.varid(); 00904 00905 std::string varnames; 00906 for (unsigned int i = 0; i < varname.size(); ++i) 00907 varnames += (i == 0 ? "" : " . ") + varname[i]; 00908 00909 const unsigned char varc = static_cast<unsigned char>(varname.empty() ? 0U : (varname.size() - 1) * 2U); 00910 00911 if (tok->str() == "return") { 00912 tok = tok->next(); 00913 if (!tok) 00914 return; 00915 } 00916 00917 // Array index.. 00918 if ((varid > 0 && Token::Match(tok, "%varid% [ %num% ]", varid)) || 00919 (varid == 0 && Token::Match(tok, (varnames + " [ %num% ]").c_str()))) { 00920 const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(2 + varc)); 00921 if (index >= size) { 00922 std::vector<MathLib::bigint> indexes; 00923 indexes.push_back(index); 00924 arrayIndexOutOfBoundsError(tok->tokAt(varc), arrayInfo, indexes); 00925 } 00926 } 00927 00928 // If the result of pointer arithmetic means that the pointer is 00929 // out of bounds then this flag will be set. 00930 bool pointerIsOutOfBounds = false; 00931 00932 for (const Token* const end = tok->scope()->classEnd; tok != end; tok = tok->next()) { 00933 if (varid != 0 && Token::Match(tok, "%varid% = new|malloc|realloc", varid)) { 00934 // Abort 00935 break; 00936 } 00937 00938 // reassign buffer 00939 if (varid > 0 && Token::Match(tok, "[;{}] %varid% = %any%", varid)) { 00940 // using varid .. bailout 00941 if (tok->tokAt(3)->varId() != varid) 00942 break; 00943 pointerIsOutOfBounds = false; 00944 } 00945 00946 // Array index.. 00947 if ((varid > 0 && ((tok->str() == "return" || (!tok->isName() && !Token::Match(tok, "[.&]"))) && Token::Match(tok->next(), "%varid% [ %num% ]", varid))) || 00948 (varid == 0 && ((tok->str() == "return" || (!tok->isName() && !Token::Match(tok, "[.&]"))) && (Token::Match(tok->next(), (varnames + " [ %num% ]").c_str()) || Token::Match(tok->next(), (varname[0] +" [ %num% ] . " + varname[1] + " [ %num% ]").c_str()))))) { 00949 std::vector<MathLib::bigint> indexes; 00950 const Token *tok2 = tok->tokAt(2 + varc); 00951 for (; Token::Match(tok2, "[ %num% ]"); tok2 = tok2->tokAt(3)) { 00952 const MathLib::bigint index = MathLib::toLongNumber(tok2->strAt(1)); 00953 indexes.push_back(index); 00954 } 00955 for (; Token::Match(tok2->tokAt(3), "[ %num% ]"); tok2 = tok2->tokAt(3)) { 00956 const MathLib::bigint index = MathLib::toLongNumber(tok2->strAt(4)); 00957 indexes.push_back(index); 00958 } 00959 00960 if (indexes.size() == arrayInfo.num().size()) { 00961 // Check if the indexes point outside the whole array.. 00962 // char a[10][10]; 00963 // a[0][20] <-- ok. 00964 // a[9][20] <-- error. 00965 00966 // total number of elements of array.. 00967 MathLib::bigint totalElements = 1; 00968 00969 // total index.. 00970 MathLib::bigint totalIndex = 0; 00971 00972 // calculate the totalElements and totalIndex.. 00973 for (unsigned int i = 0; i < indexes.size(); ++i) { 00974 std::size_t ri = indexes.size() - 1 - i; 00975 totalIndex += indexes[ri] * totalElements; 00976 totalElements *= arrayInfo.num(ri); 00977 } 00978 00979 // totalElements == 0 => Unknown size 00980 if (totalElements == 0) 00981 continue; 00982 00983 const Token *tok3 = tok->previous(); 00984 while (tok3 && Token::Match(tok3->previous(), "%var% .")) 00985 tok3 = tok3->tokAt(-2); 00986 00987 // just taking the address? 00988 const bool addr(tok3 && (tok3->str() == "&" || 00989 Token::simpleMatch(tok3->previous(), "& ("))); 00990 00991 // taking address of 1 past end? 00992 if (addr && totalIndex == totalElements) 00993 continue; 00994 00995 // Is totalIndex in bounds? 00996 if (totalIndex > totalElements || totalIndex < 0) { 00997 arrayIndexOutOfBoundsError(tok->tokAt(1 + varc), arrayInfo, indexes); 00998 } 00999 // Is any array index out of bounds? 01000 else { 01001 // check each index for overflow 01002 for (unsigned int i = 0; i < indexes.size(); ++i) { 01003 if (indexes[i] >= arrayInfo.num(i)) { 01004 if (indexes.size() == 1U) { 01005 arrayIndexOutOfBoundsError(tok->tokAt(1 + varc), arrayInfo, indexes); 01006 break; // only warn about the first one 01007 } 01008 01009 // The access is still within the memory range for the array 01010 // so it may be intentional. 01011 else if (_settings->inconclusive) { 01012 arrayIndexOutOfBoundsError(tok->tokAt(1 + varc), arrayInfo, indexes); 01013 break; // only warn about the first one 01014 } 01015 } 01016 } 01017 } 01018 } 01019 tok = tok2; 01020 continue; 01021 } 01022 01023 // memset, memcmp, memcpy, strncpy, fgets.. 01024 if (varid == 0 && size > 0) { 01025 std::list<const Token *> callstack; 01026 callstack.push_back(tok); 01027 if (Token::Match(tok, ("%var% ( " + varnames + " ,").c_str())) 01028 checkFunctionParameter(*tok, 1, arrayInfo, callstack); 01029 if (Token::Match(tok, ("%var% ( %var% , " + varnames + " ,").c_str())) 01030 checkFunctionParameter(*tok, 2, arrayInfo, callstack); 01031 } 01032 01033 // Loop.. 01034 if (Token::simpleMatch(tok, "for (")) { 01035 const ArrayInfo arrayInfo1(varid, varnames, (unsigned int)size, (unsigned int)total_size); 01036 bool bailout = false; 01037 checkScopeForBody(tok, arrayInfo1, bailout); 01038 if (bailout) 01039 break; 01040 continue; 01041 } 01042 01043 // Writing data into array.. 01044 if ((varid > 0 && Token::Match(tok, "strcpy|strcat ( %varid% , %str% )", varid)) || 01045 (varid == 0 && Token::Match(tok, ("strcpy|strcat ( " + varnames + " , %str% )").c_str()))) { 01046 const std::size_t len = Token::getStrLength(tok->tokAt(varc + 4)); 01047 if (total_size > 0 && len >= (unsigned int)total_size) { 01048 bufferOverrunError(tok, varid > 0 ? std::string() : varnames); 01049 continue; 01050 } 01051 } else if ((varid > 0 && Token::Match(tok, "strcpy|strcat ( %varid% , %var% )", varid)) || 01052 (varid == 0 && Token::Match(tok, ("strcpy|strcat ( " + varnames + " , %var% )").c_str()))) { 01053 const Variable *var = tok->tokAt(4)->variable(); 01054 if (var && var->isArray() && var->dimensions().size() == 1) { 01055 const std::size_t len = (std::size_t)var->dimension(0); 01056 if (total_size > 0 && len > (unsigned int)total_size) { 01057 if (_settings->inconclusive) 01058 possibleBufferOverrunError(tok, tok->strAt(4), tok->strAt(2), tok->str() == "strcat"); 01059 continue; 01060 } 01061 } 01062 } 01063 01064 // Detect few strcat() calls 01065 const std::string strcatPattern = varid > 0 ? std::string("strcat ( %varid% , %str% ) ;") : ("strcat ( " + varnames + " , %str% ) ;"); 01066 if (Token::Match(tok, strcatPattern.c_str(), varid)) { 01067 std::size_t charactersAppend = 0; 01068 const Token *tok2 = tok; 01069 01070 while (Token::Match(tok2, strcatPattern.c_str(), varid)) { 01071 charactersAppend += Token::getStrLength(tok2->tokAt(4 + varc)); 01072 if (charactersAppend >= static_cast<std::size_t>(total_size)) { 01073 bufferOverrunError(tok2); 01074 break; 01075 } 01076 tok2 = tok2->tokAt(7 + varc); 01077 } 01078 } 01079 01080 // sprintf.. 01081 // TODO: change total_size to an unsigned value and remove the "&& total_size > 0" check. 01082 const std::string sprintfPattern = varid > 0 ? std::string("sprintf ( %varid% , %str% [,)]") : ("sprintf ( " + varnames + " , %str% [,)]"); 01083 if (Token::Match(tok, sprintfPattern.c_str(), varid) && total_size > 0) { 01084 checkSprintfCall(tok, static_cast<unsigned int>(total_size)); 01085 } 01086 01087 // snprintf.. 01088 const std::string snprintfPattern = varid > 0 ? std::string("snprintf ( %varid% , %num% ,") : ("snprintf ( " + varnames + " , %num% ,"); 01089 if (Token::Match(tok, snprintfPattern.c_str(), varid)) { 01090 const MathLib::bigint n = MathLib::toLongNumber(tok->strAt(4 + varc)); 01091 if (n > total_size) 01092 outOfBoundsError(tok->tokAt(4 + varc), "snprintf size", true, n, total_size); 01093 } 01094 01095 // Check function call.. 01096 if (Token::Match(tok, "%var% (") && total_size > 0) { 01097 // No varid => function calls are not handled 01098 if (varid == 0) 01099 continue; 01100 01101 const ArrayInfo arrayInfo1(varid, varnames, total_size / size, size); 01102 const std::list<const Token *> callstack; 01103 checkFunctionCall(tok, arrayInfo1, callstack); 01104 } 01105 01106 // undefined behaviour: result of pointer arithmetic is out of bounds 01107 else if (varid && Token::Match(tok, "= %varid% + %num% ;", varid)) { 01108 const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(3)); 01109 if (index > size && _settings->isEnabled("portability")) 01110 pointerOutOfBoundsError(tok->next(), "buffer"); 01111 if (index >= size && Token::Match(tok->tokAt(-2), "[;{}] %varid% =", varid)) 01112 pointerIsOutOfBounds = true; 01113 } 01114 01115 else if (pointerIsOutOfBounds && Token::Match(tok, "[;{}=] * %varid% [;=]", varid)) { 01116 outOfBoundsError(tok->tokAt(2), tok->strAt(2), false, 0, 0); 01117 } 01118 } 01119 } 01120 01121 01122 void CheckBufferOverrun::checkScope(const Token *tok, const ArrayInfo &arrayInfo) 01123 { 01124 const MathLib::bigint total_size = arrayInfo.num(0) * arrayInfo.element_size(); 01125 01126 const Token *scope_begin = tok->previous(); 01127 assert(scope_begin != 0); 01128 01129 for (const Token* const end = tok->scope()->classEnd; tok != end; tok = tok->next()) { 01130 // Skip array declarations 01131 if (Token::Match(tok, "[;{}] %type% *| %var% [") && tok->strAt(1) != "return") { 01132 tok = tok->tokAt(3); 01133 continue; 01134 } 01135 01136 else if (Token::Match(tok, "%varid% [ %num% ]", arrayInfo.varid())) { 01137 std::vector<MathLib::bigint> indexes; 01138 for (const Token *tok2 = tok->next(); Token::Match(tok2, "[ %num% ]"); tok2 = tok2->tokAt(3)) { 01139 const MathLib::bigint index = MathLib::toLongNumber(tok2->strAt(1)); 01140 if (index < 0) { 01141 indexes.clear(); 01142 break; 01143 } 01144 indexes.push_back(index); 01145 } 01146 if (indexes.size() == arrayInfo.num().size()) { 01147 // Check if the indexes point outside the whole array.. 01148 // char a[10][10]; 01149 // a[0][20] <-- ok. 01150 // a[9][20] <-- error. 01151 01152 // total number of elements of array.. 01153 MathLib::bigint totalElements = 1; 01154 01155 // total index.. 01156 MathLib::bigint totalIndex = 0; 01157 01158 // calculate the totalElements and totalIndex.. 01159 for (unsigned int i = 0; i < indexes.size(); ++i) { 01160 std::size_t ri = indexes.size() - 1 - i; 01161 totalIndex += indexes[ri] * totalElements; 01162 totalElements *= arrayInfo.num(ri); 01163 } 01164 01165 // totalElements == 0 => Unknown size 01166 if (totalElements == 0) 01167 continue; 01168 01169 const Token *tok2 = tok->previous(); 01170 while (tok2 && Token::Match(tok2->previous(), "%var% .")) 01171 tok2 = tok2->tokAt(-2); 01172 01173 // just taking the address? 01174 const bool addr(tok2 && (tok2->str() == "&" || 01175 Token::simpleMatch(tok2->previous(), "& ("))); 01176 01177 // taking address of 1 past end? 01178 if (addr && totalIndex == totalElements) 01179 continue; 01180 01181 // Is totalIndex in bounds? 01182 if (totalIndex >= totalElements) { 01183 arrayIndexOutOfBoundsError(tok, arrayInfo, indexes); 01184 } 01185 // Is any array index out of bounds? 01186 else { 01187 // check each index for overflow 01188 for (unsigned int i = 0; i < indexes.size(); ++i) { 01189 if (indexes[i] >= arrayInfo.num(i)) { 01190 // The access is still within the memory range for the array 01191 // so it may be intentional. 01192 if (_settings->inconclusive) { 01193 arrayIndexOutOfBoundsError(tok, arrayInfo, indexes); 01194 break; // only warn about the first one 01195 } 01196 } 01197 } 01198 } 01199 } 01200 } 01201 01202 // Loop.. 01203 else if (Token::simpleMatch(tok, "for (")) { 01204 bool bailout = false; 01205 arrayIndexInForLoop(tok, arrayInfo); 01206 checkScopeForBody(tok, arrayInfo, bailout); 01207 if (bailout) 01208 break; 01209 continue; 01210 } 01211 01212 01213 // Check function call.. 01214 if (Token::Match(tok, "%var% (")) { 01215 const std::list<const Token *> callstack; 01216 checkFunctionCall(tok, arrayInfo, callstack); 01217 } 01218 01219 if (Token::Match(tok, "strncpy|memcpy|memmove ( %varid% , %str% , %num% )", arrayInfo.varid())) { 01220 unsigned int num = (unsigned int)MathLib::toLongNumber(tok->strAt(6)); 01221 if (Token::getStrLength(tok->tokAt(4)) >= (unsigned int)total_size && (unsigned int)total_size == num) { 01222 if (_settings->inconclusive) 01223 bufferNotZeroTerminatedError(tok, tok->strAt(2), tok->str()); 01224 } 01225 } 01226 01227 if ((Token::Match(tok, "strncpy|strncat ( %varid% ,", arrayInfo.varid()) && Token::Match(tok->linkAt(1)->tokAt(-2), ", %num% )"))) { 01228 const Token* param3 = tok->linkAt(1)->previous(); 01229 01230 // check for strncpy which is not terminated 01231 if (tok->str() == "strncpy") { 01232 // strncpy takes entire variable length as input size 01233 unsigned int num = (unsigned int)MathLib::toLongNumber(param3->str()); 01234 01235 // this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3 01236 if (num >= total_size && _settings->isEnabled("warning") && _settings->inconclusive) { 01237 const Token *tok2 = tok->next()->link()->next(); 01238 for (; tok2; tok2 = tok2->next()) { 01239 if (tok2->varId() == tok->tokAt(2)->varId()) { 01240 if (!Token::Match(tok2, "%varid% [ %any% ] = 0 ;", tok->tokAt(2)->varId())) { 01241 terminateStrncpyError(tok, tok->strAt(2)); 01242 } 01243 01244 break; 01245 } 01246 } 01247 } 01248 } 01249 01250 // Dangerous usage of strncat.. 01251 else if (tok->str() == "strncat") { 01252 const MathLib::bigint n = MathLib::toLongNumber(param3->str()); 01253 if (n >= total_size) 01254 strncatUsageError(tok); 01255 } 01256 01257 // Dangerous usage of strncpy + strncat.. 01258 if (Token::Match(param3->tokAt(2), "; strncat ( %varid% ,", arrayInfo.varid()) && Token::Match(param3->linkAt(4)->tokAt(-2), ", %num% )")) { 01259 const MathLib::bigint n = MathLib::toLongNumber(param3->str()) + MathLib::toLongNumber(param3->linkAt(4)->strAt(-1)); 01260 if (n > total_size) 01261 strncatUsageError(param3->tokAt(3)); 01262 } 01263 } 01264 01265 // Writing data into array.. 01266 if (Token::Match(tok, "strcpy|strcat ( %varid% , %str% )", arrayInfo.varid())) { 01267 const std::size_t len = Token::getStrLength(tok->tokAt(4)); 01268 if (total_size > 0 && len >= (unsigned int)total_size) { 01269 bufferOverrunError(tok, arrayInfo.varname()); 01270 continue; 01271 } 01272 } 01273 01274 // Detect few strcat() calls 01275 if (total_size > 0 && Token::Match(tok, "strcat ( %varid% , %str% ) ;", arrayInfo.varid())) { 01276 std::size_t charactersAppend = 0; 01277 const Token *tok2 = tok; 01278 01279 while (tok2 && Token::Match(tok2, "strcat ( %varid% , %str% ) ;", arrayInfo.varid())) { 01280 charactersAppend += Token::getStrLength(tok2->tokAt(4)); 01281 if (charactersAppend >= (unsigned int)total_size) { 01282 bufferOverrunError(tok2, arrayInfo.varname()); 01283 break; 01284 } 01285 tok2 = tok2->tokAt(7); 01286 } 01287 } 01288 01289 01290 if (Token::Match(tok, "sprintf ( %varid% , %str% [,)]", arrayInfo.varid())) { 01291 checkSprintfCall(tok, total_size); 01292 } 01293 01294 // snprintf.. 01295 if (total_size > 0 && Token::Match(tok, "snprintf ( %varid% , %num% ,", arrayInfo.varid())) { 01296 const MathLib::bigint n = MathLib::toLongNumber(tok->strAt(4)); 01297 if (n > total_size) 01298 outOfBoundsError(tok->tokAt(4), "snprintf size", true, n, total_size); 01299 } 01300 01301 // readlink() / readlinkat() buffer usage 01302 if (_settings->standards.posix) { 01303 if (Token::simpleMatch(tok, "readlink (") && Token::Match(tok->tokAt(2)->nextArgument(), "%varid% , %num% )", arrayInfo.varid())) 01304 checkReadlinkBufferUsage(tok, scope_begin, total_size, false); 01305 else if (Token::simpleMatch(tok, "readlinkat (") && Token::Match(tok->tokAt(2)->nextArgument()->nextArgument(), "%varid% , %num% )", arrayInfo.varid())) 01306 checkReadlinkBufferUsage(tok, scope_begin, total_size, true); 01307 } 01308 01309 // undefined behaviour: result of pointer arithmetic is out of bounds 01310 if (_settings->isEnabled("portability") && Token::Match(tok, "= %varid% + %num% ;", arrayInfo.varid())) { 01311 const MathLib::bigint index = MathLib::toLongNumber(tok->strAt(3)); 01312 if (index < 0 || index > arrayInfo.num(0)) { 01313 pointerOutOfBoundsError(tok->next(), "array"); 01314 } 01315 } 01316 } 01317 } 01318 01319 //--------------------------------------------------------------------------- 01320 // Checking member variables of structs.. 01321 //--------------------------------------------------------------------------- 01322 bool CheckBufferOverrun::isArrayOfStruct(const Token* tok, int &position) 01323 { 01324 if (Token::Match(tok->next(), "%var% [ %num% ] ")) { 01325 tok = tok->tokAt(4); 01326 int i = 1; 01327 while (true) { 01328 if (Token::Match(tok->next(), "[ %num% ] ")) { 01329 i++; 01330 tok = tok->tokAt(4); 01331 } else 01332 break; 01333 } 01334 if (Token::Match(tok->next(),";")) { 01335 position = i; 01336 return true; 01337 } 01338 } 01339 return false; 01340 } 01341 01342 void CheckBufferOverrun::checkReadlinkBufferUsage(const Token* tok, const Token *scope_begin, const MathLib::bigint total_size, const bool is_readlinkat) 01343 { 01344 const Token* bufParam = tok->tokAt(2)->nextArgument(); 01345 if (is_readlinkat) 01346 bufParam = bufParam->nextArgument(); 01347 const std::string funcname = is_readlinkat ? "readlinkat" : "readlink"; 01348 01349 const MathLib::bigint n = MathLib::toLongNumber(bufParam->strAt(2)); 01350 if (total_size > 0 && n > total_size) 01351 outOfBoundsError(bufParam, funcname + "() buf size", true, n, total_size); 01352 01353 if (!_settings->inconclusive) 01354 return; 01355 01356 // readlink()/readlinkat() never terminates the buffer, check the end of the scope for buffer termination. 01357 bool found_termination = false; 01358 const Token *scope_end = scope_begin->link(); 01359 for (const Token *tok2 = bufParam->tokAt(4); tok2 && tok2 != scope_end; tok2 = tok2->next()) { 01360 if (Token::Match(tok2, "%varid% [ %any% ] = 0 ;", bufParam->varId())) { 01361 found_termination = true; 01362 break; 01363 } 01364 } 01365 01366 if (!found_termination) { 01367 bufferNotZeroTerminatedError(tok, bufParam->str(), funcname); 01368 } else if (n == total_size) { 01369 possibleReadlinkBufferOverrunError(tok, funcname, bufParam->str()); 01370 } 01371 } 01372 01373 //--------------------------------------------------------------------------- 01374 // Checking local variables in a scope 01375 //--------------------------------------------------------------------------- 01376 01377 void CheckBufferOverrun::checkGlobalAndLocalVariable() 01378 { 01379 // check string literals 01380 for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { 01381 if (Token::Match(tok, "%str% [ %num% ]")) { 01382 std::string str = tok->strValue(); 01383 std::size_t index = (std::size_t)std::atoi(tok->strAt(2).c_str()); 01384 if (index > str.length()) { 01385 bufferOverrunError(tok, tok->str()); 01386 } 01387 } 01388 } 01389 01390 // check all known fixed size arrays first by just looking them up 01391 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 01392 for (unsigned int i = 1; i <= _tokenizer->varIdCount(); i++) { 01393 const Variable *var = symbolDatabase->getVariableFromVarId(i); 01394 if (var && var->isArray() && var->dimension(0) > 0) { 01395 const ArrayInfo arrayInfo(var, _tokenizer); 01396 const Token *tok = var->nameToken(); 01397 while (tok && tok->str() != ";") { 01398 if (tok->str() == "{") { 01399 if (Token::simpleMatch(tok->previous(), "= {")) 01400 tok = tok->link(); 01401 else 01402 break; 01403 } 01404 tok = tok->next(); 01405 } 01406 if (!tok) 01407 break; 01408 if (tok->str() == "{") 01409 tok = tok->next(); 01410 checkScope(tok, arrayInfo); 01411 } 01412 } 01413 01414 // find all dynamically allocated arrays next 01415 const std::size_t functions = symbolDatabase->functionScopes.size(); 01416 for (std::size_t i = 0; i < functions; ++i) { 01417 const Scope * scope = symbolDatabase->functionScopes[i]; 01418 01419 for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { 01420 // if the previous token exists, it must be either a variable name or "[;{}]" 01421 if (tok->previous() && (!tok->previous()->isName() && !Token::Match(tok->previous(), "[;{}]"))) 01422 continue; 01423 01424 // size : Max array index 01425 MathLib::bigint size = 0; 01426 01427 // type : The type of a array element 01428 std::string type; 01429 01430 // varid : The variable id for the array 01431 const Variable *var = 0; 01432 01433 // nextTok : number of tokens used in variable declaration - used to skip to next statement. 01434 int nextTok = 0; 01435 01436 _errorLogger->reportProgress(_tokenizer->getSourceFilePath(), 01437 "Check (BufferOverrun::checkGlobalAndLocalVariable)", 01438 tok->progressValue()); 01439 01440 if (Token::Match(tok, "[*;{}] %var% = new %type% [ %num% ]")) { 01441 size = MathLib::toLongNumber(tok->strAt(6)); 01442 type = tok->strAt(4); 01443 var = tok->next()->variable(); 01444 nextTok = 8; 01445 } else if (Token::Match(tok, "[*;{}] %var% = new %type% ( %num% )")) { 01446 size = 1; 01447 type = tok->strAt(4); 01448 var = tok->next()->variable(); 01449 nextTok = 8; 01450 } else if (Token::Match(tok, "[;{}] %var% = %str% ;") && 01451 tok->next()->varId() > 0 && 01452 NULL != Token::findmatch(_tokenizer->tokens(), "[;{}] const| %type% * %varid% ;", tok->next()->varId())) { 01453 size = 1 + int(tok->tokAt(3)->strValue().size()); 01454 type = "char"; 01455 var = tok->next()->variable(); 01456 nextTok = 4; 01457 } else if (Token::Match(tok, "[*;{}] %var% = malloc|alloca ( %num% ) ;")) { 01458 size = MathLib::toLongNumber(tok->strAt(5)); 01459 type = "char"; // minimum type, typesize=1 01460 var = tok->next()->variable(); 01461 nextTok = 7; 01462 01463 /** @todo false negatives: this may be too conservative */ 01464 if (!var || var->typeEndToken()->str() != "*" || var->typeStartToken()->next() != var->typeEndToken()) 01465 continue; 01466 01467 // get name of variable 01468 type = var->typeStartToken()->str(); 01469 01470 // malloc() gets count of bytes and not count of 01471 // elements, so we should calculate count of elements 01472 // manually 01473 unsigned int sizeOfType = _tokenizer->sizeOfType(var->typeStartToken()); 01474 if (sizeOfType > 0) 01475 size /= static_cast<int>(sizeOfType); 01476 } else { 01477 continue; 01478 } 01479 01480 if (var == 0) 01481 continue; 01482 01483 Token sizeTok(0); 01484 sizeTok.str(type); 01485 const MathLib::bigint total_size = size * static_cast<int>(_tokenizer->sizeOfType(&sizeTok)); 01486 if (total_size == 0) 01487 continue; 01488 01489 std::vector<std::string> v; 01490 ArrayInfo temp(var->varId(), tok->next()->str(), total_size / size, size); 01491 checkScope(tok->tokAt(nextTok), v, temp); 01492 } 01493 } 01494 } 01495 //--------------------------------------------------------------------------- 01496 01497 01498 //--------------------------------------------------------------------------- 01499 // Checking member variables of structs.. 01500 //--------------------------------------------------------------------------- 01501 01502 void CheckBufferOverrun::checkStructVariable() 01503 { 01504 const SymbolDatabase * symbolDatabase = _tokenizer->getSymbolDatabase(); 01505 01506 // find every class and struct 01507 const std::size_t classes = symbolDatabase->classAndStructScopes.size(); 01508 for (std::size_t i = 0; i < classes; ++i) { 01509 const Scope * scope = symbolDatabase->classAndStructScopes[i]; 01510 01511 // check all variables to see if they are arrays 01512 std::list<Variable>::const_iterator var; 01513 for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { 01514 if (var->isArray()) { 01515 // create ArrayInfo from the array variable 01516 ArrayInfo arrayInfo(&*var, _tokenizer); 01517 01518 // find every function 01519 const std::size_t functions = symbolDatabase->functionScopes.size(); 01520 for (std::size_t j = 0; j < functions; ++j) { 01521 const Scope * func_scope = symbolDatabase->functionScopes[j]; 01522 01523 // If struct is declared in a function then check 01524 // if scope_func matches 01525 if (scope->nestedIn->type == Scope::eFunction && 01526 scope->nestedIn != &*func_scope) { 01527 continue; 01528 } 01529 01530 // check for member variables 01531 if (func_scope->functionOf == &*scope) { 01532 // only check non-empty function 01533 if (func_scope->classStart->next() != func_scope->classEnd) { 01534 // start checking after the { 01535 const Token *tok = func_scope->classStart->next(); 01536 checkScope(tok, arrayInfo); 01537 } 01538 } 01539 01540 // skip inner scopes.. 01541 /** @todo false negatives: handle inner scopes someday */ 01542 if (scope->nestedIn->isClassOrStruct()) 01543 continue; 01544 01545 std::vector<std::string> varname; 01546 varname.push_back(""); 01547 varname.push_back(arrayInfo.varname()); 01548 01549 // search the function and it's parameters 01550 for (const Token *tok3 = func_scope->classDef; tok3 && tok3 != func_scope->classEnd; tok3 = tok3->next()) { 01551 // search for the class/struct name 01552 if (tok3->str() != scope->className) 01553 continue; 01554 01555 // find all array variables 01556 int posOfSemicolon = -1; 01557 01558 // Declare variable: Fred fred1; 01559 if (Token::Match(tok3->next(), "%var% ;")) 01560 varname[0] = tok3->strAt(1); 01561 01562 else if (isArrayOfStruct(tok3,posOfSemicolon)) { 01563 varname[0] = tok3->strAt(1); 01564 01565 int pos = 2; 01566 for (int k = 0 ; k < posOfSemicolon; k++) { 01567 for (int index = pos; index < (pos + 3); index++) 01568 tok3->strAt(index); 01569 pos += 3; 01570 } 01571 } 01572 01573 // Declare pointer or reference: Fred *fred1 01574 else if (Token::Match(tok3->next(), "*|& %var% [,);=]")) 01575 varname[0] = tok3->strAt(2); 01576 01577 else 01578 continue; 01579 01580 // check for variable sized structure 01581 if (scope->type == Scope::eStruct && var->isPublic()) { 01582 // last member of a struct with array size of 0 or 1 could be a variable sized structure 01583 if (var->dimensions().size() == 1 && var->dimension(0) < 2 && 01584 var->index() == (scope->varlist.size() - 1)) { 01585 // dynamically allocated so could be variable sized structure 01586 if (tok3->next()->str() == "*") { 01587 // check for allocation 01588 if ((Token::Match(tok3->tokAt(3), "; %var% = malloc ( %num% ) ;") || 01589 (Token::Match(tok3->tokAt(3), "; %var% = (") && 01590 Token::Match(tok3->linkAt(6), ") malloc ( %num% ) ;"))) && 01591 (tok3->strAt(4) == tok3->strAt(2))) { 01592 MathLib::bigint size; 01593 01594 // find size of allocation 01595 if (tok3->strAt(3) == "(") // has cast 01596 size = MathLib::toLongNumber(tok3->linkAt(6)->strAt(3)); 01597 else 01598 size = MathLib::toLongNumber(tok3->strAt(8)); 01599 01600 // We don't calculate the size of a structure even when we know 01601 // the size of the members. We just assign a length of 100 for 01602 // any struct. If the size is less than 100, we assume the 01603 // programmer knew the size and specified it rather than using 01604 // sizeof(struct). If the size is greater than 100, we assume 01605 // the programmer specified the size as sizeof(struct) + number. 01606 // Either way, this is just a guess and could be wrong. The 01607 // information to make the right decision has been simplified 01608 // away by the time we get here. 01609 if (size != 100) { // magic number for size of struct 01610 // check if a real size was specified and give up 01611 // malloc(10) rather than malloc(sizeof(struct)) 01612 if (size < 100) 01613 continue; 01614 01615 // calculate real array size based on allocated size 01616 MathLib::bigint elements = (size - 100) / arrayInfo.element_size(); 01617 arrayInfo.num(0, arrayInfo.num(0) + elements); 01618 } 01619 } 01620 01621 // size unknown so assume it is a variable sized structure 01622 else 01623 continue; 01624 } 01625 } 01626 } 01627 01628 // Goto end of statement. 01629 const Token *CheckTok = NULL; 01630 while (tok3 && tok3 != func_scope->classEnd) { 01631 // End of statement. 01632 if (tok3->str() == ";") { 01633 CheckTok = tok3; 01634 break; 01635 } 01636 01637 // End of function declaration.. 01638 if (Token::simpleMatch(tok3, ") ;")) 01639 break; 01640 01641 // Function implementation.. 01642 if (Token::simpleMatch(tok3, ") {")) { 01643 CheckTok = tok3->tokAt(2); 01644 break; 01645 } 01646 01647 tok3 = tok3->next(); 01648 } 01649 01650 if (!tok3) 01651 break; 01652 01653 if (!CheckTok) 01654 continue; 01655 01656 // Check variable usage.. 01657 ArrayInfo temp = arrayInfo; 01658 temp.varid(0); // do variable lookup by variable and member names rather than varid 01659 std::string varnames; // use class and member name for messages 01660 for (unsigned int k = 0; k < varname.size(); ++k) 01661 varnames += (k == 0 ? "" : ".") + varname[k]; 01662 01663 temp.varname(varnames); 01664 checkScope(CheckTok, varname, temp); 01665 } 01666 } 01667 } 01668 } 01669 } 01670 } 01671 //--------------------------------------------------------------------------- 01672 01673 void CheckBufferOverrun::bufferOverrun() 01674 { 01675 checkGlobalAndLocalVariable(); 01676 checkStructVariable(); 01677 checkBufferAllocatedWithStrlen(); 01678 checkInsecureCmdLineArgs(); 01679 } 01680 //--------------------------------------------------------------------------- 01681 01682 01683 MathLib::bigint CheckBufferOverrun::countSprintfLength(const std::string &input_string, const std::list<const Token*> ¶meters) 01684 { 01685 bool percentCharFound = false; 01686 std::size_t input_string_size = 1; 01687 bool handleNextParameter = false; 01688 std::string digits_string = ""; 01689 bool i_d_x_f_found = false; 01690 std::list<const Token*>::const_iterator paramIter = parameters.begin(); 01691 std::size_t parameterLength = 0; 01692 for (std::string::size_type i = 0; i < input_string.length(); ++i) { 01693 if (input_string[i] == '\\') { 01694 if (input_string[i+1] == '0') 01695 break; 01696 01697 ++input_string_size; 01698 ++i; 01699 continue; 01700 } 01701 01702 if (percentCharFound) { 01703 switch (input_string[i]) { 01704 case 'f': 01705 case 'x': 01706 case 'X': 01707 case 'i': 01708 i_d_x_f_found = true; 01709 case 'c': 01710 case 'e': 01711 case 'E': 01712 case 'g': 01713 case 'o': 01714 case 'u': 01715 case 'p': 01716 case 'n': 01717 handleNextParameter = true; 01718 break; 01719 case 'd': 01720 i_d_x_f_found = true; 01721 if (paramIter != parameters.end() && *paramIter && (*paramIter)->type() != Token::eString) 01722 parameterLength = (*paramIter)->str().length(); 01723 01724 handleNextParameter = true; 01725 break; 01726 case 's': 01727 if (paramIter != parameters.end() && *paramIter && (*paramIter)->type() == Token::eString) 01728 parameterLength = Token::getStrLength(*paramIter); 01729 01730 handleNextParameter = true; 01731 break; 01732 } 01733 } 01734 01735 if (input_string[i] == '%') 01736 percentCharFound = !percentCharFound; 01737 else if (percentCharFound) { 01738 digits_string.append(1, input_string[i]); 01739 } 01740 01741 if (!percentCharFound) 01742 input_string_size++; 01743 01744 if (handleNextParameter) { 01745 unsigned int tempDigits = static_cast<unsigned int>(std::abs(std::atoi(digits_string.c_str()))); 01746 if (i_d_x_f_found) 01747 tempDigits = std::max(static_cast<unsigned int>(tempDigits), 1U); 01748 01749 if (digits_string.find('.') != std::string::npos) { 01750 const std::string endStr = digits_string.substr(digits_string.find('.') + 1); 01751 unsigned int maxLen = std::max(static_cast<unsigned int>(std::abs(std::atoi(endStr.c_str()))), 1U); 01752 01753 if (input_string[i] == 's') { 01754 // For strings, the length after the dot "%.2s" will limit 01755 // the length of the string. 01756 if (parameterLength > maxLen) 01757 parameterLength = maxLen; 01758 } else { 01759 // For integers, the length after the dot "%.2d" can 01760 // increase required length 01761 if (tempDigits < maxLen) 01762 tempDigits = maxLen; 01763 } 01764 } 01765 01766 if (tempDigits < parameterLength) 01767 input_string_size += parameterLength; 01768 else 01769 input_string_size += tempDigits; 01770 01771 parameterLength = 0; 01772 digits_string = ""; 01773 i_d_x_f_found = false; 01774 percentCharFound = false; 01775 handleNextParameter = false; 01776 if (paramIter != parameters.end()) 01777 ++paramIter; 01778 } 01779 } 01780 01781 return (MathLib::bigint)input_string_size; 01782 } 01783 01784 void CheckBufferOverrun::checkSprintfCall(const Token *tok, const MathLib::bigint size) 01785 { 01786 if (size == 0) 01787 return; 01788 01789 std::list<const Token*> parameters; 01790 const Token* vaArg = tok->tokAt(2)->nextArgument()->nextArgument(); 01791 while (vaArg) { 01792 if (Token::Match(vaArg->next(), "[,)]")) { 01793 if (vaArg->type() == Token::eString) 01794 parameters.push_back(vaArg); 01795 01796 else if (vaArg->isNumber()) 01797 parameters.push_back(vaArg); 01798 01799 else 01800 parameters.push_back(0); 01801 } else // Parameter is more complex than just a value or variable. Ignore it for now and skip to next token. 01802 parameters.push_back(0); 01803 01804 vaArg = vaArg->nextArgument(); 01805 } 01806 01807 MathLib::bigint len = countSprintfLength(tok->tokAt(2)->nextArgument()->strValue(), parameters); 01808 if (len > size) { 01809 bufferOverrunError(tok); 01810 } 01811 } 01812 01813 01814 01815 //--------------------------------------------------------------------------- 01816 // Checking for allocating insufficient memory for copying a string by 01817 // allocating only strlen(src) bytes instead of strlen(src) + 1 bytes (one 01818 // extra for the terminating null character). 01819 // Example: 01820 // char *b = malloc(strlen(a)); // Should be malloc(strlen(a) + 1); 01821 // strcpy(b, a); // <== Buffer overrun 01822 //--------------------------------------------------------------------------- 01823 void CheckBufferOverrun::checkBufferAllocatedWithStrlen() 01824 { 01825 const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); 01826 01827 const std::size_t functions = symbolDatabase->functionScopes.size(); 01828 for (std::size_t i = 0; i < functions; ++i) { 01829 const Scope * scope = symbolDatabase->functionScopes[i]; 01830 for (const Token *tok = scope->classStart->next(); tok && tok != scope->classEnd; tok = tok->next()) { 01831 unsigned int dstVarId; 01832 unsigned int srcVarId; 01833 01834 // Look for allocation of a buffer based on the size of a string 01835 if (Token::Match(tok, "%var% = malloc|g_malloc|g_try_malloc ( strlen ( %var% ) )")) { 01836 dstVarId = tok->varId(); 01837 srcVarId = tok->tokAt(6)->varId(); 01838 tok = tok->tokAt(8); 01839 } else if (Token::Match(tok, "%var% = new char [ strlen ( %var% ) ]")) { 01840 dstVarId = tok->varId(); 01841 srcVarId = tok->tokAt(7)->varId(); 01842 tok = tok->tokAt(9); 01843 } else if (Token::Match(tok, "%var% = realloc|g_realloc|g_try_realloc ( %var% , strlen ( %var% ) )")) { 01844 dstVarId = tok->varId(); 01845 srcVarId = tok->tokAt(8)->varId(); 01846 tok = tok->tokAt(10); 01847 } else 01848 continue; 01849 01850 // To avoid false positives and added complexity, we will only look for 01851 // improper usage of the buffer within the block that it was allocated 01852 for (const Token* const end = tok->scope()->classEnd; tok && tok->next() && tok != end; tok = tok->next()) { 01853 // If the buffers are modified, we can't be sure of their sizes 01854 if (tok->varId() == srcVarId || tok->varId() == dstVarId) 01855 break; 01856 01857 if (Token::Match(tok, "strcpy ( %varid% , %var% )", dstVarId) && 01858 tok->tokAt(4)->varId() == srcVarId) { 01859 bufferOverrunError(tok); 01860 } else if (Token::Match(tok, "sprintf ( %varid% , %str% , %var% )", dstVarId) && 01861 tok->tokAt(6)->varId() == srcVarId && 01862 tok->strAt(4).find("%s") != std::string::npos) { 01863 bufferOverrunError(tok); 01864 } 01865 } 01866 if (!tok) 01867 return; 01868 } 01869 } 01870 } 01871 01872 //--------------------------------------------------------------------------- 01873 // Checking for buffer overflow caused by copying command line arguments 01874 // into fixed-sized buffers without checking to make sure that the command 01875 // line arguments will not overflow the buffer. 01876 // 01877 // int main(int argc, char* argv[]) 01878 // { 01879 // char prog[10]; 01880 // strcpy(prog, argv[0]); <-- Possible buffer overrun 01881 // } 01882 //--------------------------------------------------------------------------- 01883 void CheckBufferOverrun::checkInsecureCmdLineArgs() 01884 { 01885 const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); 01886 01887 std::size_t functions = symbolDatabase->functionScopes.size(); 01888 for (std::size_t i = 0; i < functions; ++i) { 01889 const Scope * scope = symbolDatabase->functionScopes[i]; 01890 Function * j = scope->function; 01891 if (j) { 01892 const Token* tok = j->token; 01893 01894 // Get the name of the argv variable 01895 unsigned int varid = 0; 01896 if (Token::Match(tok, "main ( int %var% , char * %var% [ ] ,|)")) { 01897 varid = tok->tokAt(7)->varId(); 01898 01899 } else if (Token::Match(tok, "main ( int %var% , char * * %var% ,|)")) { 01900 varid = tok->tokAt(8)->varId(); 01901 } 01902 if (varid == 0) 01903 continue; 01904 01905 // Jump to the opening curly brace 01906 tok = tok->next()->link(); 01907 if (!Token::simpleMatch(tok, ") {")) 01908 continue; 01909 tok = tok->next(); 01910 01911 // Search within main() for possible buffer overruns involving argv 01912 for (const Token* end = tok->link(); tok != end; tok = tok->next()) { 01913 // If argv is modified or tested, its size may be being limited properly 01914 if (tok->varId() == varid) 01915 break; 01916 01917 // Match common patterns that can result in a buffer overrun 01918 // e.g. strcpy(buffer, argv[0]) 01919 if (Token::Match(tok, "strcpy|strcat ( %var% , * %varid%", varid) || 01920 Token::Match(tok, "strcpy|strcat ( %var% , %varid% [", varid)) { 01921 cmdLineArgsError(tok); 01922 } else if (Token::Match(tok, "sprintf ( %var% , %str% , %varid% [", varid) && 01923 tok->strAt(4).find("%s") != std::string::npos) { 01924 cmdLineArgsError(tok); 01925 } else if (Token::Match(tok, "sprintf ( %var% , %str% , * %varid%", varid) && 01926 tok->strAt(4).find("%s") != std::string::npos) { 01927 cmdLineArgsError(tok); 01928 } 01929 } 01930 } 01931 } 01932 } 01933 //--------------------------------------------------------------------------- 01934 01935 01936 void CheckBufferOverrun::negativeIndexError(const Token *tok, MathLib::bigint index) 01937 { 01938 std::ostringstream ostr; 01939 ostr << "Array index " << index << " is out of bounds."; 01940 reportError(tok, Severity::error, "negativeIndex", ostr.str()); 01941 } 01942 01943 void CheckBufferOverrun::negativeIndex() 01944 { 01945 const char pattern[] = "[ %num% ]"; 01946 for (const Token *tok = Token::findmatch(_tokenizer->tokens(), pattern); tok; tok = Token::findmatch(tok->next(),pattern)) { 01947 const MathLib::bigint index = MathLib::toLongNumber(tok->next()->str()); 01948 if (index < 0) { 01949 // Negative index. Check if it's an array. 01950 const Token *tok2 = tok; 01951 while (tok2->strAt(-1) == "]") 01952 tok2 = tok2->previous()->link(); 01953 01954 if (tok2->previous() && tok2->previous()->varId()) { 01955 const Variable *var = tok2->previous()->variable(); 01956 if (var && var->isArray()) 01957 negativeIndexError(tok, index); 01958 } 01959 } 01960 } 01961 } 01962 01963 01964 01965 01966 #include "executionpath.h" 01967 01968 /// @addtogroup Checks 01969 /// @{ 01970 01971 01972 01973 CheckBufferOverrun::ArrayInfo::ArrayInfo() 01974 : _element_size(0), _varid(0) 01975 { 01976 } 01977 01978 CheckBufferOverrun::ArrayInfo::ArrayInfo(const CheckBufferOverrun::ArrayInfo &ai) 01979 { 01980 *this = ai; 01981 } 01982 01983 CheckBufferOverrun::ArrayInfo::ArrayInfo(const Variable *var, const Tokenizer *tokenizer) 01984 : _varname(var->name()), _varid(var->varId()) 01985 { 01986 for (std::size_t i = 0; i < var->dimensions().size(); i++) 01987 _num.push_back(var->dimension(i)); 01988 if (var->typeEndToken()->str() == "*") 01989 _element_size = tokenizer->sizeOfType(var->typeEndToken()); 01990 else if (var->typeStartToken()->str() == "struct") 01991 _element_size = 100; 01992 else 01993 _element_size = tokenizer->sizeOfType(var->typeEndToken()); 01994 } 01995 01996 CheckBufferOverrun::ArrayInfo & CheckBufferOverrun::ArrayInfo::operator=(const CheckBufferOverrun::ArrayInfo &ai) 01997 { 01998 if (&ai != this) { 01999 _element_size = ai._element_size; 02000 _num = ai._num; 02001 _varid = ai._varid; 02002 _varname = ai._varname; 02003 } 02004 return *this; 02005 } 02006 02007 /** 02008 * Create array info with specified data 02009 * The intention is that this is only a temporary solution.. all 02010 * checking should be based on ArrayInfo from the start and then 02011 * this will not be needed as the declare can be used instead. 02012 */ 02013 CheckBufferOverrun::ArrayInfo::ArrayInfo(unsigned int id, const std::string &name, MathLib::bigint size1, MathLib::bigint n) 02014 : _varname(name), _element_size(size1), _varid(id) 02015 { 02016 _num.push_back(n); 02017 } 02018 02019 CheckBufferOverrun::ArrayInfo CheckBufferOverrun::ArrayInfo::limit(MathLib::bigint value) const 02020 { 02021 MathLib::bigint uvalue = std::max(MathLib::bigint(0), value); 02022 MathLib::bigint n = 1; 02023 for (unsigned int i = 0; i < _num.size(); ++i) 02024 n *= _num[i]; 02025 if (uvalue > n) 02026 n = uvalue; 02027 return ArrayInfo(_varid, _varname, _element_size, n - uvalue); 02028 } 02029 02030 02031 /** 02032 * @brief %Check for buffer overruns (using ExecutionPath) 02033 */ 02034 02035 class ExecutionPathBufferOverrun : public ExecutionPath { 02036 public: 02037 /** Startup constructor */ 02038 ExecutionPathBufferOverrun(Check *c, const std::map<unsigned int, CheckBufferOverrun::ArrayInfo> &arrayinfo) 02039 : ExecutionPath(c, 0), arrayInfo(arrayinfo), value(0) { 02040 } 02041 02042 private: 02043 /** @brief Copy this check. Called from the ExecutionPath baseclass. */ 02044 ExecutionPath *copy() { 02045 return new ExecutionPathBufferOverrun(*this); 02046 } 02047 02048 /** @brief is other execution path equal? */ 02049 bool is_equal(const ExecutionPath *e) const { 02050 const ExecutionPathBufferOverrun *c = static_cast<const ExecutionPathBufferOverrun *>(e); 02051 return (value == c->value); 02052 } 02053 02054 /** @brief Buffer information */ 02055 const std::map<unsigned int, CheckBufferOverrun::ArrayInfo> &arrayInfo; 02056 02057 /** no implementation => compiler error if used by accident */ 02058 void operator=(const ExecutionPathBufferOverrun &); 02059 02060 /** internal constructor for creating extra checks */ 02061 ExecutionPathBufferOverrun(Check *c, const std::map<unsigned int, CheckBufferOverrun::ArrayInfo> &arrayinfo, unsigned int varid_) 02062 : ExecutionPath(c, varid_), 02063 arrayInfo(arrayinfo), 02064 value(0) { // Pretend that variables are initialized to 0. This checking is not about uninitialized variables. 02065 } 02066 02067 /** @brief Variable value. */ 02068 MathLib::bigint value; 02069 02070 /** 02071 * @brief Assign value to a variable 02072 * @param checks the execution paths 02073 * @param varid the variable id 02074 * @param value the assigned value 02075 */ 02076 static void assign_value(std::list<ExecutionPath *> &checks, unsigned int varid, const std::string &value) { 02077 if (varid == 0) 02078 return; 02079 02080 std::list<ExecutionPath *>::const_iterator it; 02081 for (it = checks.begin(); it != checks.end(); ++it) { 02082 ExecutionPathBufferOverrun *c = dynamic_cast<ExecutionPathBufferOverrun *>(*it); 02083 if (c && c->varId == varid) 02084 c->value = MathLib::toLongNumber(value); 02085 } 02086 } 02087 02088 /** 02089 * @brief Found array usage, analyse the array usage 02090 * @param tok token where usage occurs (only used when reporting the error) 02091 * @param checks The execution paths 02092 * @param varid1 variable id for the array 02093 * @param varid2 variable id for the index 02094 */ 02095 static void array_index(const Token *tok, std::list<ExecutionPath *> &checks, unsigned int varid1, unsigned int varid2) { 02096 if (checks.empty() || varid1 == 0 || varid2 == 0) 02097 return; 02098 02099 // Locate array info corresponding to varid1 02100 ExecutionPathBufferOverrun *c = dynamic_cast<ExecutionPathBufferOverrun *>(checks.front()); 02101 std::map<unsigned int, CheckBufferOverrun::ArrayInfo>::const_iterator it1; 02102 it1 = c->arrayInfo.find(varid1); 02103 if (it1 == c->arrayInfo.end()) 02104 return; 02105 const CheckBufferOverrun::ArrayInfo& ai = it1->second; 02106 02107 // Check if varid2 variable has a value that is out of bounds 02108 std::list<ExecutionPath *>::const_iterator it; 02109 for (it = checks.begin(); it != checks.end(); ++it) { 02110 c = dynamic_cast<ExecutionPathBufferOverrun *>(*it); 02111 if (c && c->varId == varid2 && c->value >= ai.num(0)) { 02112 // variable value is out of bounds, report error 02113 CheckBufferOverrun *checkBufferOverrun = dynamic_cast<CheckBufferOverrun *>(c->owner); 02114 if (checkBufferOverrun) { 02115 std::vector<MathLib::bigint> index; 02116 index.push_back(c->value); 02117 checkBufferOverrun->arrayIndexOutOfBoundsError(tok, ai, index); 02118 break; 02119 } 02120 } 02121 } 02122 } 02123 02124 const Token *parse(const Token &tok, std::list<ExecutionPath *> &checks) const { 02125 if (Token::Match(tok.previous(), "[;{}]")) { 02126 // Declaring variable.. 02127 if (Token::Match(&tok, "%type% %var% ;") /*&& (tok.isStandardType() || isC)*/) { 02128 checks.push_back(new ExecutionPathBufferOverrun(owner, arrayInfo, tok.next()->varId())); 02129 return tok.tokAt(2); 02130 } 02131 02132 // Assign variable.. 02133 if (Token::Match(&tok, "%var% = %num% ;")) { 02134 assign_value(checks, tok.varId(), tok.strAt(2)); 02135 return tok.tokAt(3); 02136 } 02137 } 02138 02139 // Assign variable (unknown value = 0).. 02140 if (Token::Match(&tok, "%var% =")) { 02141 assign_value(checks, tok.varId(), "0"); 02142 return &tok; 02143 } 02144 02145 // Assign variable (unknown value = 0).. 02146 if (Token::Match(tok.tokAt(-2), "(|, & %var% ,|)")) { 02147 assign_value(checks, tok.varId(), "0"); 02148 return &tok; 02149 } 02150 02151 // Array index.. 02152 if (Token::Match(&tok, "%var% [ %var% ]")) { 02153 array_index(&tok, checks, tok.varId(), tok.tokAt(2)->varId()); 02154 return tok.tokAt(3); 02155 } 02156 02157 return &tok; 02158 } 02159 }; 02160 02161 /// @} 02162 02163 02164 void CheckBufferOverrun::executionPaths() 02165 { 02166 // Parse all variables and extract array info.. 02167 std::map<unsigned int, ArrayInfo> arrayInfo; 02168 for (unsigned int i = 1; i <= _tokenizer->varIdCount(); i++) { 02169 const Variable *var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(i); 02170 if (var && var->isArray() && var->dimension(0) > 0) 02171 arrayInfo[i] = ArrayInfo(var, _tokenizer); 02172 } 02173 02174 // Perform checking - check how the arrayInfo arrays are used 02175 ExecutionPathBufferOverrun c(this, arrayInfo); 02176 checkExecutionPaths(_tokenizer->getSymbolDatabase(), &c); 02177 } 02178 02179 02180 02181 02182 void CheckBufferOverrun::arrayIndexThenCheck() 02183 { 02184 if (!_settings->isEnabled("style")) 02185 return; 02186 02187 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 02188 const std::size_t functions = symbolDatabase->functionScopes.size(); 02189 for (std::size_t i = 0; i < functions; ++i) { 02190 const Scope * scope = symbolDatabase->functionScopes[i]; 02191 for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { 02192 if (Token::Match(tok, "%var% [ %var% ]")) { 02193 const std::string& indexName(tok->strAt(2)); 02194 02195 // skip array index.. 02196 tok = tok->tokAt(4); 02197 while (tok && tok->str() == "[") 02198 tok = tok->link()->next(); 02199 02200 // syntax error 02201 if (!tok) 02202 return; 02203 02204 // skip comparison 02205 if (tok->type() == Token::eComparisonOp && tok->strAt(2) == "&&") 02206 tok = tok->tokAt(2); 02207 02208 // check if array index is ok 02209 if (Token::Match(tok, ("&& " + indexName + " <|<=").c_str())) 02210 arrayIndexThenCheckError(tok, indexName); 02211 } 02212 } 02213 } 02214 } 02215 02216 void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::string &indexName) 02217 { 02218 reportError(tok, Severity::style, "arrayIndexThenCheck", 02219 "Array index '" + indexName + "' is used before limits check.\n" 02220 "Defensive programming: The variable '" + indexName + "' is used as an array index before it " 02221 "is check that is within limits. This can mean that the array might be accessed out of bounds. " 02222 "Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will " 02223 "not be accessed if the index is out of limits."); 02224 } 02225 02226 // ------------------------------------------------------------------------------------- 02227 // Check the second and the third parameter of the POSIX function write and validate 02228 // their values. 02229 // The parameters have the following meaning: 02230 // - 1.parameter: file descripter (not required for this check) 02231 // - 2.parameter: is a null terminated character string of the content to write. 02232 // - 3.parameter: the number of bytes to write. 02233 // 02234 // This check is triggered if the size of the string ( 2. parameter) is lower than 02235 // the number of bytes provided at the 3. parameter. 02236 // 02237 // References: 02238 // - http://gd.tuwien.ac.at/languages/c/programming-bbrown/c_075.htm 02239 // - http://codewiki.wikidot.com/c:system-calls:write 02240 // ------------------------------------------------------------------------------------- 02241 void CheckBufferOverrun::writeOutsideBufferSize() 02242 { 02243 if (!_settings->standards.posix) 02244 return; 02245 02246 const SymbolDatabase* symbolDatabase = _tokenizer->getSymbolDatabase(); 02247 const std::size_t functions = symbolDatabase->functionScopes.size(); 02248 for (std::size_t i = 0; i < functions; ++i) { 02249 const Scope * scope = symbolDatabase->functionScopes[i]; 02250 for (const Token *tok = scope->classStart; tok && tok != scope->classEnd; tok = tok->next()) { 02251 if (Token::Match(tok, "pwrite|write (") && Token::Match(tok->tokAt(2)->nextArgument(), "%str% , %num%")) { 02252 const std::string & functionName(tok->str()); 02253 tok = tok->tokAt(4); // set tokenptr to %str% parameter 02254 const std::size_t stringLength = Token::getStrLength(tok); 02255 tok = tok->tokAt(2); // set tokenptr to %num% parameter 02256 const MathLib::bigint writeLength = MathLib::toLongNumber(tok->str()); 02257 if (static_cast<unsigned long int>(writeLength) > stringLength) 02258 writeOutsideBufferSizeError(tok, stringLength, writeLength,functionName); 02259 } 02260 } 02261 } 02262 } 02263 02264 void CheckBufferOverrun::writeOutsideBufferSizeError(const Token *tok, const std::size_t stringLength, const MathLib::bigint writeLength, const std::string &strFunctionName) 02265 { 02266 reportError(tok, Severity::error, "writeOutsideBufferSize", 02267 "Writing '" +MathLib::longToString(writeLength-stringLength)+"' bytes outside buffer size.\n" 02268 "The number of bytes to write ('" +MathLib::longToString(writeLength)+ "' bytes) are bigger than the source buffer ('" +MathLib::longToString(stringLength)+ "' bytes)." 02269 " Please check the second and the third parameter of the function '"+strFunctionName+"'."); 02270 } 02271 // ------------------------------------------------------------------------------------- 02272 // -------------------------------------------------------------------------------------
1.7.6.1