Cppcheck
checkbufferoverrun.cpp
Go to the documentation of this file.
00001 /*
00002  * Cppcheck - A tool for static C/C++ code analysis
00003  * Copyright (C) 2007-2013 Daniel Marjamäki and Cppcheck team.
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation, either version 3 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 //---------------------------------------------------------------------------
00020 // 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*> &parameters)
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 // -------------------------------------------------------------------------------------