同步操作将从 Gitee 极速下载/Cppcheck 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
/** Cppcheck - A tool for static C/C++ code analysis* Copyright (C) 2007-2020 Cppcheck team.** This program is free software: you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation, either version 3 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program. If not, see <http://www.gnu.org/licenses/>.*///---------------------------------------------------------------------------// Buffer overrun..//---------------------------------------------------------------------------#include "checkbufferoverrun.h"#include "astutils.h"#include "errorlogger.h"#include "library.h"#include "mathlib.h"#include "settings.h"#include "symboldatabase.h"#include "token.h"#include "tokenize.h"#include "utils.h"#include "valueflow.h"#include <tinyxml2.h>#include <algorithm>#include <cstdlib>#include <numeric> // std::accumulate#include <sstream>//---------------------------------------------------------------------------// Register this check class (by creating a static instance of it)namespace {CheckBufferOverrun instance;}//---------------------------------------------------------------------------// CWE ids used:static const CWE CWE131(131U); // Incorrect Calculation of Buffer Sizestatic const CWE CWE170(170U); // Improper Null Terminationstatic const CWE CWE_ARRAY_INDEX_THEN_CHECK(398U); // Indicator of Poor Code Qualitystatic const CWE CWE682(682U); // Incorrect Calculationstatic const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behaviorstatic const CWE CWE_POINTER_ARITHMETIC_OVERFLOW(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behaviorstatic const CWE CWE_BUFFER_UNDERRUN(786U); // Access of Memory Location Before Start of Bufferstatic const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After End of Buffer//---------------------------------------------------------------------------static const ValueFlow::Value *getBufferSizeValue(const Token *tok){const std::list<ValueFlow::Value> &tokenValues = tok->values();const auto it = std::find_if(tokenValues.begin(), tokenValues.end(), std::mem_fn(&ValueFlow::Value::isBufferSizeValue));return it == tokenValues.end() ? nullptr : &*it;}static int getMinFormatStringOutputLength(const std::vector<const Token*> ¶meters, nonneg int formatStringArgNr){if (formatStringArgNr <= 0 || formatStringArgNr > parameters.size())return 0;if (parameters[formatStringArgNr - 1]->tokType() != Token::eString)return 0;const std::string &formatString = parameters[formatStringArgNr - 1]->str();bool percentCharFound = false;int outputStringSize = 0;bool handleNextParameter = false;std::string digits_string;bool i_d_x_f_found = false;int parameterLength = 0;int inputArgNr = formatStringArgNr;for (int i = 1; i + 1 < formatString.length(); ++i) {if (formatString[i] == '\\') {if (i < formatString.length() - 1 && formatString[i + 1] == '0')break;++outputStringSize;++i;continue;}if (percentCharFound) {switch (formatString[i]) {case 'f':case 'x':case 'X':case 'i':i_d_x_f_found = true;handleNextParameter = true;parameterLength = 1; // TODObreak;case 'c':case 'e':case 'E':case 'g':case 'o':case 'u':case 'p':case 'n':handleNextParameter = true;parameterLength = 1; // TODObreak;case 'd':i_d_x_f_found = true;parameterLength = 1;if (inputArgNr < parameters.size() && parameters[inputArgNr]->hasKnownIntValue())parameterLength = MathLib::toString(parameters[inputArgNr]->getKnownIntValue()).length();handleNextParameter = true;break;case 's':parameterLength = 0;if (inputArgNr < parameters.size() && parameters[inputArgNr]->tokType() == Token::eString)parameterLength = Token::getStrLength(parameters[inputArgNr]);handleNextParameter = true;break;}}if (formatString[i] == '%')percentCharFound = !percentCharFound;else if (percentCharFound) {digits_string.append(1, formatString[i]);}if (!percentCharFound)outputStringSize++;if (handleNextParameter) {int tempDigits = std::abs(std::atoi(digits_string.c_str()));if (i_d_x_f_found)tempDigits = std::max(tempDigits, 1);if (digits_string.find('.') != std::string::npos) {const std::string endStr = digits_string.substr(digits_string.find('.') + 1);const int maxLen = std::max(std::abs(std::atoi(endStr.c_str())), 1);if (formatString[i] == 's') {// For strings, the length after the dot "%.2s" will limit// the length of the string.if (parameterLength > maxLen)parameterLength = maxLen;} else {// For integers, the length after the dot "%.2d" can// increase required lengthif (tempDigits < maxLen)tempDigits = maxLen;}}if (tempDigits < parameterLength)outputStringSize += parameterLength;elseoutputStringSize += tempDigits;parameterLength = 0;digits_string.clear();i_d_x_f_found = false;percentCharFound = false;handleNextParameter = false;++inputArgNr;}}return outputStringSize;}//---------------------------------------------------------------------------static bool getDimensionsEtc(const Token * const arrayToken, const Settings *settings, std::vector<Dimension> * const dimensions, ErrorPath * const errorPath, bool * const mightBeLarger, MathLib::bigint* path){const Token *array = arrayToken;while (Token::Match(array, ".|::"))array = array->astOperand2();if (array->variable() && array->variable()->isArray() && !array->variable()->dimensions().empty()) {*dimensions = array->variable()->dimensions();if (dimensions->size() >= 1 && ((*dimensions)[0].num <= 1 || !(*dimensions)[0].tok)) {visitAstNodes(arrayToken,[&](const Token *child) {if (child->originalName() == "->") {*mightBeLarger = true;return ChildrenToVisit::none;}return ChildrenToVisit::op1_and_op2;});}} else if (const Token *stringLiteral = array->getValueTokenMinStrSize(settings)) {Dimension dim;dim.tok = nullptr;dim.num = Token::getStrArraySize(stringLiteral);dim.known = array->hasKnownValue();dimensions->emplace_back(dim);} else if (array->valueType() && array->valueType()->pointer >= 1 && array->valueType()->isIntegral()) {const ValueFlow::Value *value = getBufferSizeValue(array);if (!value)return false;if (path)*path = value->path;*errorPath = value->errorPath;Dimension dim;dim.known = value->isKnown();dim.tok = nullptr;const int typeSize = array->valueType()->typeSize(*settings);if (typeSize == 0)return false;dim.num = value->intvalue / typeSize;dimensions->emplace_back(dim);}return !dimensions->empty();}static std::vector<const ValueFlow::Value *> getOverrunIndexValues(const Token *tok, const Token *arrayToken, const std::vector<Dimension> &dimensions, const std::vector<const Token *> &indexTokens, MathLib::bigint path){const Token *array = arrayToken;while (Token::Match(array, ".|::"))array = array->astOperand2();for (int cond = 0; cond < 2; cond++) {bool equal = false;bool overflow = false;bool allKnown = true;std::vector<const ValueFlow::Value *> indexValues;for (int i = 0; i < dimensions.size() && i < indexTokens.size(); ++i) {const ValueFlow::Value *value = indexTokens[i]->getMaxValue(cond == 1);indexValues.push_back(value);if (!value)continue;if (value->path != path)continue;if (!value->isKnown()) {if (!allKnown)continue;allKnown = false;}if (array->variable() && array->variable()->isArray() && dimensions[i].num == 0)continue;if (value->intvalue == dimensions[i].num)equal = true;else if (value->intvalue > dimensions[i].num)overflow = true;}if (equal && tok->str() != "[")continue;if (!overflow && equal) {const Token *parent = tok;while (Token::simpleMatch(parent, "["))parent = parent->astParent();if (!parent || parent->isUnaryOp("&"))continue;}if (overflow || equal)return indexValues;}return std::vector<const ValueFlow::Value *>();}void CheckBufferOverrun::arrayIndex(){for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {if (tok->str() != "[")continue;const Token *array = tok->astOperand1();while (Token::Match(array, ".|::"))array = array->astOperand2();if (!array || ((!array->variable() || array->variable()->nameToken() == array) && array->tokType() != Token::eString))continue;if (!array->scope()->isExecutable()) {// LHS in non-executable scope => This is just a definitionconst Token *parent = tok;while (parent && !Token::simpleMatch(parent->astParent(), "="))parent = parent->astParent();if (!parent || parent == parent->astParent()->astOperand1())continue;}if (astIsContainer(array))continue;std::vector<const Token *> indexTokens;for (const Token *tok2 = tok; tok2 && tok2->str() == "["; tok2 = tok2->link()->next()) {if (!tok2->astOperand2()) {indexTokens.clear();break;}indexTokens.emplace_back(tok2->astOperand2());}if (indexTokens.empty())continue;std::vector<Dimension> dimensions;ErrorPath errorPath;bool mightBeLarger = false;MathLib::bigint path = 0;if (!getDimensionsEtc(tok->astOperand1(), mSettings, &dimensions, &errorPath, &mightBeLarger, &path))continue;// Positive indexif (!mightBeLarger) { // TODO check arrays with dim 1 alsoconst std::vector<const ValueFlow::Value *> &indexValues = getOverrunIndexValues(tok, tok->astOperand1(), dimensions, indexTokens, path);if (!indexValues.empty())arrayIndexError(tok, dimensions, indexValues);}// Negative indexbool neg = false;std::vector<const ValueFlow::Value *> negativeIndexes;for (const Token * indexToken : indexTokens) {const ValueFlow::Value *negativeValue = indexToken->getValueLE(-1, mSettings);negativeIndexes.emplace_back(negativeValue);if (negativeValue)neg = true;}if (neg) {negativeIndexError(tok, dimensions, negativeIndexes);}}}static std::string stringifyIndexes(const std::string &array, const std::vector<const ValueFlow::Value *> &indexValues){if (indexValues.size() == 1)return MathLib::toString(indexValues[0]->intvalue);std::ostringstream ret;ret << array;for (const ValueFlow::Value *index : indexValues) {ret << "[";if (index)ret << index->intvalue;elseret << "*";ret << "]";}return ret.str();}static std::string arrayIndexMessage(const Token *tok, const std::vector<Dimension> &dimensions, const std::vector<const ValueFlow::Value *> &indexValues, const Token *condition){auto add_dim = [](const std::string &s, const Dimension &dim) {return s + "[" + MathLib::toString(dim.num) + "]";};const std::string array = std::accumulate(dimensions.begin(), dimensions.end(), tok->astOperand1()->expressionString(), add_dim);std::ostringstream errmsg;if (condition)errmsg << ValueFlow::eitherTheConditionIsRedundant(condition)<< " or the array '" + array + "' is accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds.";elseerrmsg << "Array '" << array << "' accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds.";return errmsg.str();}void CheckBufferOverrun::arrayIndexError(const Token *tok, const std::vector<Dimension> &dimensions, const std::vector<const ValueFlow::Value *> &indexes){if (!tok) {reportError(tok, Severity::error, "arrayIndexOutOfBounds", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, false);reportError(tok, Severity::warning, "arrayIndexOutOfBoundsCond", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, false);return;}const Token *condition = nullptr;const ValueFlow::Value *index = nullptr;for (const ValueFlow::Value *indexValue: indexes) {if (!indexValue)continue;if (!indexValue->errorSeverity() && !mSettings->isEnabled(Settings::WARNING))return;if (indexValue->condition)condition = indexValue->condition;if (!index || !indexValue->errorPath.empty())index = indexValue;}reportError(getErrorPath(tok, index, "Array index out of bounds"),index->errorSeverity() ? Severity::error : Severity::warning,index->condition ? "arrayIndexOutOfBoundsCond" : "arrayIndexOutOfBounds",arrayIndexMessage(tok, dimensions, indexes, condition),CWE_BUFFER_OVERRUN,index->isInconclusive());}void CheckBufferOverrun::negativeIndexError(const Token *tok, const std::vector<Dimension> &dimensions, const std::vector<const ValueFlow::Value *> &indexes){if (!tok) {reportError(tok, Severity::error, "negativeIndex", "Negative array index", CWE_BUFFER_UNDERRUN, false);return;}const Token *condition = nullptr;const ValueFlow::Value *negativeValue = nullptr;for (const ValueFlow::Value *indexValue: indexes) {if (!indexValue)continue;if (!indexValue->errorSeverity() && !mSettings->isEnabled(Settings::WARNING))return;if (indexValue->condition)condition = indexValue->condition;if (!negativeValue || !indexValue->errorPath.empty())negativeValue = indexValue;}reportError(getErrorPath(tok, negativeValue, "Negative array index"),negativeValue->errorSeverity() ? Severity::error : Severity::warning,"negativeIndex",arrayIndexMessage(tok, dimensions, indexes, condition),CWE_BUFFER_UNDERRUN,negativeValue->isInconclusive());}//---------------------------------------------------------------------------void CheckBufferOverrun::pointerArithmetic(){if (!mSettings->isEnabled(Settings::PORTABILITY))return;for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {if (!Token::Match(tok, "+|-"))continue;if (!tok->valueType() || tok->valueType()->pointer == 0)continue;if (!tok->astOperand1() || !tok->astOperand2())continue;if (!tok->astOperand1()->valueType() || !tok->astOperand2()->valueType())continue;const Token *arrayToken, *indexToken;if (tok->astOperand1()->valueType()->pointer > 0) {arrayToken = tok->astOperand1();indexToken = tok->astOperand2();} else {arrayToken = tok->astOperand2();indexToken = tok->astOperand1();}if (!indexToken || !indexToken->valueType() || indexToken->valueType()->pointer > 0 || !indexToken->valueType()->isIntegral())continue;std::vector<Dimension> dimensions;ErrorPath errorPath;bool mightBeLarger = false;MathLib::bigint path = 0;if (!getDimensionsEtc(arrayToken, mSettings, &dimensions, &errorPath, &mightBeLarger, &path))continue;if (tok->str() == "+") {// Positive indexif (!mightBeLarger) { // TODO check arrays with dim 1 alsoconst std::vector<const Token *> indexTokens{indexToken};const std::vector<const ValueFlow::Value *> &indexValues = getOverrunIndexValues(tok, arrayToken, dimensions, indexTokens, path);if (!indexValues.empty())pointerArithmeticError(tok, indexToken, indexValues.front());}if (const ValueFlow::Value *neg = indexToken->getValueLE(-1, mSettings))pointerArithmeticError(tok, indexToken, neg);} else if (tok->str() == "-") {// TODO}}}void CheckBufferOverrun::pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue){if (!tok) {reportError(tok, Severity::portability, "pointerOutOfBounds", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, false);reportError(tok, Severity::portability, "pointerOutOfBoundsCond", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, false);return;}std::string errmsg;if (indexValue->condition)errmsg = "Undefined behaviour, when '" + indexToken->expressionString() + "' is " + MathLib::toString(indexValue->intvalue) + " the pointer arithmetic '" + tok->expressionString() + "' is out of bounds.";elseerrmsg = "Undefined behaviour, pointer arithmetic '" + tok->expressionString() + "' is out of bounds.";reportError(getErrorPath(tok, indexValue, "Pointer arithmetic overflow"),Severity::portability,indexValue->condition ? "pointerOutOfBoundsCond" : "pointerOutOfBounds",errmsg,CWE_POINTER_ARITHMETIC_OVERFLOW,indexValue->isInconclusive());}//---------------------------------------------------------------------------ValueFlow::Value CheckBufferOverrun::getBufferSize(const Token *bufTok) const{if (!bufTok->valueType())return ValueFlow::Value(-1);const Variable *var = bufTok->variable();if (!var || var->dimensions().empty()) {const ValueFlow::Value *value = getBufferSizeValue(bufTok);if (value)return *value;}if (!var)return ValueFlow::Value(-1);MathLib::bigint dim = std::accumulate(var->dimensions().begin(), var->dimensions().end(), 1LL, [](MathLib::bigint i1, const Dimension &dim) {return i1 * dim.num;});ValueFlow::Value v;v.setKnown();v.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE;if (var->isPointerArray())v.intvalue = dim * mSettings->sizeof_pointer;else if (var->isPointer())return ValueFlow::Value(-1);else {const MathLib::bigint typeSize = bufTok->valueType()->typeSize(*mSettings);v.intvalue = dim * typeSize;}return v;}//---------------------------------------------------------------------------static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::MinSize &minsize, const std::vector<const Token *> &args, const MathLib::bigint bufferSize, const Settings *settings){const Token * const arg = (minsize.arg > 0 && minsize.arg - 1 < args.size()) ? args[minsize.arg - 1] : nullptr;const Token * const arg2 = (minsize.arg2 > 0 && minsize.arg2 - 1 < args.size()) ? args[minsize.arg2 - 1] : nullptr;switch (minsize.type) {case Library::ArgumentChecks::MinSize::Type::STRLEN:if (settings->library.isargformatstr(ftok, minsize.arg)) {return getMinFormatStringOutputLength(args, minsize.arg) < bufferSize;} else if (arg) {const Token *strtoken = arg->getValueTokenMaxStrLength();if (strtoken)return Token::getStrLength(strtoken) < bufferSize;}break;case Library::ArgumentChecks::MinSize::Type::ARGVALUE:if (arg && arg->hasKnownIntValue())return arg->getKnownIntValue() <= bufferSize;break;case Library::ArgumentChecks::MinSize::Type::SIZEOF:// TODObreak;case Library::ArgumentChecks::MinSize::Type::MUL:if (arg && arg2 && arg->hasKnownIntValue() && arg2->hasKnownIntValue())return (arg->getKnownIntValue() * arg2->getKnownIntValue()) <= bufferSize;break;case Library::ArgumentChecks::MinSize::Type::VALUE:return minsize.value <= bufferSize;case Library::ArgumentChecks::MinSize::Type::NONE:break;}return true;}void CheckBufferOverrun::bufferOverflow(){const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();for (const Scope * scope : symbolDatabase->functionScopes) {for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {if (!Token::Match(tok, "%name% (") || Token::simpleMatch(tok, ") {"))continue;if (!mSettings->library.hasminsize(tok))continue;const std::vector<const Token *> args = getArguments(tok);for (int argnr = 0; argnr < args.size(); ++argnr) {if (!args[argnr]->valueType() || args[argnr]->valueType()->pointer == 0)continue;const std::vector<Library::ArgumentChecks::MinSize> *minsizes = mSettings->library.argminsizes(tok, argnr + 1);if (!minsizes || minsizes->empty())continue;// Get buffer size..const Token *argtok = args[argnr];while (argtok && argtok->isCast())argtok = argtok->astOperand2() ? argtok->astOperand2() : argtok->astOperand1();while (Token::Match(argtok, ".|::"))argtok = argtok->astOperand2();if (!argtok || !argtok->variable())continue;if (argtok->valueType() && argtok->valueType()->pointer == 0)continue;// TODO: strcpy(buf+10, "hello");const ValueFlow::Value bufferSize = getBufferSize(argtok);if (bufferSize.intvalue <= 1)continue;bool error = std::none_of(minsizes->begin(), minsizes->end(), [=](const Library::ArgumentChecks::MinSize &minsize) {return checkBufferSize(tok, minsize, args, bufferSize.intvalue, mSettings);});if (error)bufferOverflowError(args[argnr], &bufferSize);}}}}void CheckBufferOverrun::bufferOverflowError(const Token *tok, const ValueFlow::Value *value){reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? tok->expressionString() : "buf"), CWE_BUFFER_OVERRUN, false);}//---------------------------------------------------------------------------void CheckBufferOverrun::arrayIndexThenCheck(){if (!mSettings->isEnabled(Settings::PORTABILITY))return;const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();for (const Scope * const scope : symbolDatabase->functionScopes) {for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {if (Token::simpleMatch(tok, "sizeof (")) {tok = tok->linkAt(1);continue;}if (Token::Match(tok, "%name% [ %var% ]")) {tok = tok->next();const int indexID = tok->next()->varId();const std::string& indexName(tok->strAt(1));// Iterate AST upwardsconst Token* tok2 = tok;const Token* tok3 = tok2;while (tok2->astParent() && tok2->tokType() != Token::eLogicalOp && tok2->str() != "?") {tok3 = tok2;tok2 = tok2->astParent();}// Ensure that we ended at a logical operator and that we came from its left sideif (tok2->tokType() != Token::eLogicalOp || tok2->astOperand1() != tok3)continue;// check if array index is ok// statement can be closed in parentheses, so "(| " is usingif (Token::Match(tok2, "&& (| %varid% <|<=", indexID))arrayIndexThenCheckError(tok, indexName);else if (Token::Match(tok2, "&& (| %any% >|>= %varid% !!+", indexID))arrayIndexThenCheckError(tok, indexName);}}}}void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::string &indexName){reportError(tok, Severity::style, "arrayIndexThenCheck","$symbol:" + indexName + "\n""Array index '$symbol' is used before limits check.\n""Defensive programming: The variable '$symbol' is used as an array index before it ""is checked that is within limits. This can mean that the array might be accessed out of bounds. ""Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will ""not be accessed if the index is out of limits.", CWE_ARRAY_INDEX_THEN_CHECK, false);}//---------------------------------------------------------------------------void CheckBufferOverrun::stringNotZeroTerminated(){// this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3if (!mSettings->isEnabled(Settings::WARNING) || !mSettings->inconclusive)return;const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();for (const Scope * const scope : symbolDatabase->functionScopes) {for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) {if (!Token::simpleMatch(tok, "strncpy ("))continue;const std::vector<const Token *> args = getArguments(tok);if (args.size() != 3)continue;const Token *sizeToken = args[2];if (!sizeToken->hasKnownIntValue())continue;const ValueFlow::Value &bufferSize = getBufferSize(args[0]);if (bufferSize.intvalue < 0 || sizeToken->getKnownIntValue() < bufferSize.intvalue)continue;const Token *srcValue = args[1]->getValueTokenMaxStrLength();if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue())continue;// Is the buffer zero terminated after the call?bool isZeroTerminated = false;for (const Token *tok2 = tok->next()->link(); tok2 != scope->bodyEnd; tok2 = tok2->next()) {if (!Token::simpleMatch(tok2, "] ="))continue;const Token *rhs = tok2->next()->astOperand2();if (!rhs || !rhs->hasKnownIntValue() || rhs->getKnownIntValue() != 0)continue;if (isSameExpression(mTokenizer->isCPP(), false, args[0], tok2->link()->astOperand1(), mSettings->library, false, false))isZeroTerminated = true;}if (isZeroTerminated)continue;// TODO: Locate unsafe string usage..terminateStrncpyError(tok, args[0]->expressionString());}}}void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname){const std::string shortMessage = "The buffer '$symbol' may not be null-terminated after the call to strncpy().";reportError(tok, Severity::warning, "terminateStrncpy","$symbol:" + varname + '\n' +shortMessage + '\n' +shortMessage + ' ' +"If the source string's size fits or exceeds the given size, strncpy() does not add a ""zero at the end of the buffer. This causes bugs later in the code if the code ""assumes buffer is null-terminated.", CWE170, true);}void CheckBufferOverrun::bufferNotZeroTerminatedError(const Token *tok, const std::string &varname, const std::string &function){const std::string errmsg = "$symbol:" + varname + '\n' +"$symbol:" + function + '\n' +"The buffer '" + varname + "' is not null-terminated after the call to " + function + "().\n""The buffer '" + varname + "' is not null-terminated after the call to " + function + "(). ""This will cause bugs later in the code if the code assumes the buffer is null-terminated.";reportError(tok, Severity::warning, "bufferNotZeroTerminated", errmsg, CWE170, true);}//---------------------------------------------------------------------------// CTU..//---------------------------------------------------------------------------std::string CheckBufferOverrun::MyFileInfo::toString() const{std::string xml;if (!unsafeArrayIndex.empty())xml = " <array-index>\n" + CTU::toString(unsafeArrayIndex) + " </array-index>\n";if (!unsafePointerArith.empty())xml += " <pointer-arith>\n" + CTU::toString(unsafePointerArith) + " </pointer-arith>\n";return xml;}bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Check *check, const Token *argtok, MathLib::bigint *offset, int type){const CheckBufferOverrun *c = dynamic_cast<const CheckBufferOverrun *>(check);if (!c)return false;if (!argtok->valueType() || argtok->valueType()->typeSize(*c->mSettings) == 0)return false;const Token *indexTok = nullptr;if (type == 1 && Token::Match(argtok, "%name% [") && argtok->astParent() == argtok->next() && !Token::simpleMatch(argtok->linkAt(1), "] ["))indexTok = argtok->next()->astOperand2();else if (type == 2 && Token::simpleMatch(argtok->astParent(), "+"))indexTok = (argtok == argtok->astParent()->astOperand1()) ?argtok->astParent()->astOperand2() :argtok->astParent()->astOperand1();if (!indexTok)return false;if (!indexTok->hasKnownIntValue())return false;if (!offset)return false;*offset = indexTok->getKnownIntValue() * argtok->valueType()->typeSize(*c->mSettings);return true;}bool CheckBufferOverrun::isCtuUnsafeArrayIndex(const Check *check, const Token *argtok, MathLib::bigint *offset){return CheckBufferOverrun::isCtuUnsafeBufferUsage(check, argtok, offset, 1);}bool CheckBufferOverrun::isCtuUnsafePointerArith(const Check *check, const Token *argtok, MathLib::bigint *offset){return CheckBufferOverrun::isCtuUnsafeBufferUsage(check, argtok, offset, 2);}/** @brief Parse current TU and extract file info */Check::FileInfo *CheckBufferOverrun::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const{CheckBufferOverrun checkBufferOverrun(tokenizer, settings, nullptr);MyFileInfo *fileInfo = new MyFileInfo;fileInfo->unsafeArrayIndex = CTU::getUnsafeUsage(tokenizer, settings, &checkBufferOverrun, isCtuUnsafeArrayIndex);fileInfo->unsafePointerArith = CTU::getUnsafeUsage(tokenizer, settings, &checkBufferOverrun, isCtuUnsafePointerArith);if (fileInfo->unsafeArrayIndex.empty() && fileInfo->unsafePointerArith.empty()) {delete fileInfo;return nullptr;}return fileInfo;}Check::FileInfo * CheckBufferOverrun::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const{const std::string arrayIndex("array-index");const std::string pointerArith("pointer-arith");MyFileInfo *fileInfo = new MyFileInfo;for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) {if (e->Name() == arrayIndex)fileInfo->unsafeArrayIndex = CTU::loadUnsafeUsageListFromXml(e);else if (e->Name() == pointerArith)fileInfo->unsafePointerArith = CTU::loadUnsafeUsageListFromXml(e);}if (fileInfo->unsafeArrayIndex.empty() && fileInfo->unsafePointerArith.empty()) {delete fileInfo;return nullptr;}return fileInfo;}/** @brief Analyse all file infos for all TU */bool CheckBufferOverrun::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list<Check::FileInfo*> &fileInfo, const Settings& settings, ErrorLogger &errorLogger){if (!ctu)return false;bool foundErrors = false;(void)settings; // This argument is unusedconst std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> callsMap = ctu->getCallsMap();for (Check::FileInfo *fi1 : fileInfo) {const MyFileInfo *fi = dynamic_cast<MyFileInfo*>(fi1);if (!fi)continue;for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeArrayIndex)foundErrors |= analyseWholeProgram1(ctu, callsMap, unsafeUsage, 1, errorLogger);for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafePointerArith)foundErrors |= analyseWholeProgram1(ctu, callsMap, unsafeUsage, 2, errorLogger);}return foundErrors;}bool CheckBufferOverrun::analyseWholeProgram1(const CTU::FileInfo *ctu, const std::map<std::string, std::list<const CTU::FileInfo::CallBase *>> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger){const CTU::FileInfo::FunctionCall *functionCall = nullptr;const std::list<ErrorMessage::FileLocation> &locationList =ctu->getErrorPath(CTU::FileInfo::InvalidValueType::bufferOverflow,unsafeUsage,callsMap,"Using argument ARG",&functionCall,false);if (locationList.empty())return false;const char *errorId = nullptr;std::string errmsg;CWE cwe(0);if (type == 1) {errorId = "ctuArrayIndex";if (unsafeUsage.value > 0)errmsg = "Array index out of bounds; '" + unsafeUsage.myArgumentName + "' buffer size is " + MathLib::toString(functionCall->callArgValue) + " and it is accessed at offset " + MathLib::toString(unsafeUsage.value) + ".";elseerrmsg = "Array index out of bounds; buffer '" + unsafeUsage.myArgumentName + "' is accessed at offset " + MathLib::toString(unsafeUsage.value) + ".";cwe = (unsafeUsage.value > 0) ? CWE_BUFFER_OVERRUN : CWE_BUFFER_UNDERRUN;} else {errorId = "ctuPointerArith";errmsg = "Pointer arithmetic overflow; '" + unsafeUsage.myArgumentName + "' buffer size is " + MathLib::toString(functionCall->callArgValue);cwe = CWE_POINTER_ARITHMETIC_OVERFLOW;}const ErrorMessage errorMessage(locationList,emptyString,Severity::error,errmsg,errorId,cwe, false);errorLogger.reportErr(errorMessage);return true;}void CheckBufferOverrun::objectIndex(){const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();for (const Scope *functionScope : symbolDatabase->functionScopes) {for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {if (!Token::simpleMatch(tok, "["))continue;const Token *obj = tok->astOperand1();const Token *idx = tok->astOperand2();if (!idx || !obj)continue;if (idx->hasKnownIntValue() && idx->getKnownIntValue() == 0)continue;ValueFlow::Value v = getLifetimeObjValue(obj);if (!v.isLocalLifetimeValue())continue;if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address)continue;const Variable *var = v.tokvalue->variable();if (var->isReference())continue;if (var->isRValueReference())continue;if (var->isArray())continue;if (var->isPointer())continue;objectIndexError(tok, &v, idx->hasKnownIntValue());}}}void CheckBufferOverrun::objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known){ErrorPath errorPath;std::string name;if (v) {name = v->tokvalue->variable()->name();errorPath = v->errorPath;}errorPath.emplace_back(tok, "");std::string verb = known ? "is" : "might be";reportError(errorPath,known ? Severity::error : Severity::warning,"objectIndex","The address of local variable '" + name + "' " + verb + " accessed at non-zero index.",CWE758,false);}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。