Cppcheck
checkbufferoverrun.h
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 //---------------------------------------------------------------------------
00021 #ifndef CheckBufferOverrunH
00022 #define CheckBufferOverrunH
00023 //---------------------------------------------------------------------------
00024 
00025 #include "config.h"
00026 #include "check.h"
00027 #include "settings.h"
00028 #include "mathlib.h"
00029 #include <list>
00030 #include <vector>
00031 #include <string>
00032 
00033 class ErrorLogger;
00034 class Token;
00035 class Tokenizer;
00036 class Variable;
00037 
00038 /// @addtogroup Checks
00039 /// @{
00040 
00041 /**
00042  * @brief buffer overruns and array index out of bounds
00043  *
00044  * Buffer overrun and array index out of bounds are pretty much the same.
00045  * But I generally use 'array index' if the code contains []. And the given
00046  * index is out of bounds.
00047  * I generally use 'buffer overrun' if you for example call a strcpy or
00048  * other function and pass a buffer and reads or writes too much data.
00049  */
00050 class CPPCHECKLIB CheckBufferOverrun : public Check {
00051 public:
00052 
00053     /** This constructor is used when registering the CheckClass */
00054     CheckBufferOverrun() : Check(myName())
00055     { }
00056 
00057     /** This constructor is used when running checks. */
00058     CheckBufferOverrun(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
00059         : Check(myName(), tokenizer, settings, errorLogger)
00060     { }
00061 
00062     void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
00063         CheckBufferOverrun checkBufferOverrun(tokenizer, settings, errorLogger);
00064         checkBufferOverrun.bufferOverrun();
00065         checkBufferOverrun.negativeIndex();
00066         checkBufferOverrun.arrayIndexThenCheck();
00067 
00068         /** ExecutionPath checking.. */
00069         checkBufferOverrun.executionPaths();
00070         checkBufferOverrun.writeOutsideBufferSize();
00071     }
00072 
00073     /** @brief %Check for buffer overruns */
00074     void bufferOverrun();
00075 
00076     /** @brief Using array index before bounds check */
00077     void arrayIndexThenCheck();
00078 
00079     /** @brief %Check for buffer overruns by inspecting execution paths */
00080     void executionPaths();
00081 
00082     /** @brief %Check using POSIX write function and writing outside buffer size */
00083     void writeOutsideBufferSize();
00084 
00085     /**
00086      * @brief Get minimum length of format string result
00087      * @param input_string format string
00088      * @param parameters given parameters to sprintf
00089      * @return minimum length of resulting string
00090      */
00091     static MathLib::bigint countSprintfLength(const std::string &input_string, const std::list<const Token*> &parameters);
00092 
00093     /**
00094      * @brief %Check code that matches: "sprintf ( %varid% , %str% [,)]" when varid is not 0,
00095      * and report found errors.
00096      * @param tok The "sprintf" token.
00097      * @param size The size of the buffer where sprintf is writing.
00098      */
00099     void checkSprintfCall(const Token *tok, const MathLib::bigint size);
00100 
00101     /** Check for buffer overruns - locate struct variables and check them with the .._CheckScope function */
00102     void checkStructVariable();
00103 
00104     /** Check for buffer overruns - locate global variables and local function variables and check them with the checkScope function */
00105     void checkGlobalAndLocalVariable();
00106 
00107     /** Check for buffer overruns due to allocating strlen(src) bytes instead of (strlen(src)+1) bytes before copying a string */
00108     void checkBufferAllocatedWithStrlen();
00109 
00110     /** Check for buffer overruns due to copying command-line args to fixed-sized buffers without bounds checking */
00111     void checkInsecureCmdLineArgs();
00112 
00113     /** Check for negative index */
00114     void negativeIndex();
00115 
00116     /** Information about N-dimensional array */
00117     class CPPCHECKLIB ArrayInfo {
00118     private:
00119         /** number of elements of array */
00120         std::vector<MathLib::bigint> _num;
00121 
00122         /** full name of variable as pattern */
00123         std::string _varname;
00124 
00125         /** size of each element in array */
00126         MathLib::bigint _element_size;
00127 
00128         /** variable id */
00129         unsigned int _varid;
00130 
00131     public:
00132         ArrayInfo();
00133         ArrayInfo(const ArrayInfo &);
00134         ArrayInfo(const Variable *var, const Tokenizer *tokenizer);
00135         ArrayInfo & operator=(const ArrayInfo &ai);
00136 
00137         /**
00138          * Create array info with specified data
00139          * The intention is that this is only a temporary solution.. all
00140          * checking should be based on ArrayInfo from the start and then
00141          * this will not be needed as the declare can be used instead.
00142          */
00143         ArrayInfo(unsigned int id, const std::string &name, MathLib::bigint size1, MathLib::bigint n);
00144 
00145         /** Create a copy ArrayInfo where the number of elements have been limited by a value */
00146         ArrayInfo limit(MathLib::bigint value) const;
00147 
00148         /** array sizes */
00149         const std::vector<MathLib::bigint> &num() const {
00150             return _num;
00151         }
00152 
00153         /** array size */
00154         MathLib::bigint num(std::size_t index) const {
00155             return _num[index];
00156         }
00157         void num(std::size_t index, MathLib::bigint number) {
00158             _num[index] = number;
00159         }
00160 
00161         /** size of each element */
00162         MathLib::bigint element_size() const {
00163             return _element_size;
00164         }
00165 
00166         /** Variable name */
00167         unsigned int varid() const {
00168             return _varid;
00169         }
00170         void varid(unsigned int id) {
00171             _varid = id;
00172         }
00173 
00174         /** Variable name */
00175         const std::string &varname() const {
00176             return _varname;
00177         }
00178         void varname(const std::string &name) {
00179             _varname = name;
00180         }
00181     };
00182 
00183     /** Check for buffer overruns (based on ArrayInfo) */
00184     void checkScope(const Token *tok, const ArrayInfo &arrayInfo);
00185 
00186     /** Check for buffer overruns */
00187     void checkScope(const Token *tok, const std::vector<std::string> &varname, const ArrayInfo &arrayInfo);
00188 
00189     /** Check scope helper function - parse for body */
00190     void checkScopeForBody(const Token *tok, const ArrayInfo &arrayInfo, bool &bailout);
00191 
00192     /** Helper function used when parsing for-loops */
00193     void parse_for_body(const Token *tok2, 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);
00194 
00195     /** Check readlink or readlinkat() buffer usage */
00196     void checkReadlinkBufferUsage(const Token *tok, const Token *scope_begin, const MathLib::bigint total_size, const bool is_readlinkat);
00197 
00198     /**
00199      * Helper function for checkFunctionCall - check a function parameter
00200      * \param tok token for the function name
00201      * \param par on what parameter is the array used
00202      * \param arrayInfo the array information
00203      * \param callstack call stack. This is used to prevent recursion and to provide better error messages. Pass a empty list from checkScope etc.
00204      */
00205     void checkFunctionParameter(const Token &tok, const unsigned int par, const ArrayInfo &arrayInfo, std::list<const Token *> callstack);
00206 
00207     /**
00208      * Helper function that checks if the array is used and if so calls the checkFunctionCall
00209      * @param tok token that matches "%var% ("
00210      * @param arrayInfo the array information
00211      * \param callstack call stack. This is used to prevent recursion and to provide better error messages. Pass a empty list from checkScope etc.
00212      */
00213     void checkFunctionCall(const Token *tok, const ArrayInfo &arrayInfo, std::list<const Token *> callstack);
00214 
00215     void arrayIndexOutOfBoundsError(const Token *tok, const ArrayInfo &arrayInfo, const std::vector<MathLib::bigint> &index);
00216     void arrayIndexInForLoop(const Token *tok, const ArrayInfo &arrayInfo);
00217 
00218 private:
00219 
00220     bool isArrayOfStruct(const Token* tok, int &position);
00221     void arrayIndexOutOfBoundsError(const std::list<const Token *> &callstack, const ArrayInfo &arrayInfo, const std::vector<MathLib::bigint> &index);
00222     void bufferOverrunError(const Token *tok, const std::string &varnames = "");
00223     void bufferOverrunError(const std::list<const Token *> &callstack, const std::string &varnames = "");
00224     void strncatUsageError(const Token *tok);
00225     void outOfBoundsError(const Token *tok, const std::string &what, const bool show_size_info, const MathLib::bigint &supplied_size, const MathLib::bigint &actual_size);
00226     void sizeArgumentAsCharError(const Token *tok);
00227     void terminateStrncpyError(const Token *tok, const std::string &varname);
00228     void bufferNotZeroTerminatedError(const Token *tok, const std::string &varname, const std::string &function);
00229     void negativeIndexError(const Token *tok, MathLib::bigint index);
00230     void cmdLineArgsError(const Token *tok);
00231     void pointerOutOfBoundsError(const Token *tok, const std::string &object);  // UB when result of calculation is out of bounds
00232     void arrayIndexThenCheckError(const Token *tok, const std::string &indexName);
00233     void possibleBufferOverrunError(const Token *tok, const std::string &src, const std::string &dst, bool cat);
00234     void possibleReadlinkBufferOverrunError(const Token *tok, const std::string &funcname, const std::string &varname);
00235     void argumentSizeError(const Token *tok, const std::string &functionName, const std::string &varname);
00236     void writeOutsideBufferSizeError(const Token *tok, const std::size_t stringLength, const MathLib::bigint writeLength, const std::string& functionName);
00237 
00238 public:
00239     void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
00240         CheckBufferOverrun c(0, settings, errorLogger);
00241         std::vector<MathLib::bigint> indexes;
00242         indexes.push_back(2);
00243         c.arrayIndexOutOfBoundsError(0, ArrayInfo(0, "array", 1, 2), indexes);
00244         c.bufferOverrunError(0, std::string("buffer"));
00245         c.strncatUsageError(0);
00246         c.outOfBoundsError(0, "index", true, 2, 1);
00247         c.sizeArgumentAsCharError(0);
00248         c.terminateStrncpyError(0, "buffer");
00249         c.bufferNotZeroTerminatedError(0, "buffer", "strncpy");
00250         c.negativeIndexError(0, -1);
00251         c.cmdLineArgsError(0);
00252         c.pointerOutOfBoundsError(0, "array");
00253         c.arrayIndexThenCheckError(0, "index");
00254         c.possibleBufferOverrunError(0, "source", "destination", false);
00255         c.possibleReadlinkBufferOverrunError(0, "readlink", "buffer");
00256         c.argumentSizeError(0, "function", "array");
00257         c.writeOutsideBufferSizeError(0,2,3,"write");
00258     }
00259 private:
00260 
00261     static std::string myName() {
00262         return "Bounds checking";
00263     }
00264 
00265     std::string classInfo() const {
00266         return "out of bounds checking\n";
00267     }
00268 };
00269 /// @}
00270 //---------------------------------------------------------------------------
00271 #endif