Fix issue 9360: False positive: arrayIndexOutOfBounds when function is called with different array sizes (#2541)
This commit is contained in:
parent
7044c17599
commit
3b20684aca
|
@ -182,7 +182,7 @@ static int getMinFormatStringOutputLength(const std::vector<const Token*> ¶m
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
static bool getDimensionsEtc(const Token * const arrayToken, const Settings *settings, std::vector<Dimension> * const dimensions, ErrorPath * const errorPath, bool * const mightBeLarger)
|
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;
|
const Token *array = arrayToken;
|
||||||
while (Token::Match(array, ".|::"))
|
while (Token::Match(array, ".|::"))
|
||||||
|
@ -210,6 +210,8 @@ static bool getDimensionsEtc(const Token * const arrayToken, const Settings *set
|
||||||
const ValueFlow::Value *value = getBufferSizeValue(array);
|
const ValueFlow::Value *value = getBufferSizeValue(array);
|
||||||
if (!value)
|
if (!value)
|
||||||
return false;
|
return false;
|
||||||
|
if (path)
|
||||||
|
*path = value->path;
|
||||||
*errorPath = value->errorPath;
|
*errorPath = value->errorPath;
|
||||||
Dimension dim;
|
Dimension dim;
|
||||||
dim.known = value->isKnown();
|
dim.known = value->isKnown();
|
||||||
|
@ -223,7 +225,7 @@ static bool getDimensionsEtc(const Token * const arrayToken, const Settings *set
|
||||||
return !dimensions->empty();
|
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)
|
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;
|
const Token *array = arrayToken;
|
||||||
while (Token::Match(array, ".|::"))
|
while (Token::Match(array, ".|::"))
|
||||||
|
@ -239,6 +241,8 @@ static std::vector<const ValueFlow::Value *> getOverrunIndexValues(const Token *
|
||||||
indexValues.push_back(value);
|
indexValues.push_back(value);
|
||||||
if (!value)
|
if (!value)
|
||||||
continue;
|
continue;
|
||||||
|
if (value->path != path)
|
||||||
|
continue;
|
||||||
if (!value->isKnown()) {
|
if (!value->isKnown()) {
|
||||||
if (!allKnown)
|
if (!allKnown)
|
||||||
continue;
|
continue;
|
||||||
|
@ -303,12 +307,13 @@ void CheckBufferOverrun::arrayIndex()
|
||||||
std::vector<Dimension> dimensions;
|
std::vector<Dimension> dimensions;
|
||||||
ErrorPath errorPath;
|
ErrorPath errorPath;
|
||||||
bool mightBeLarger = false;
|
bool mightBeLarger = false;
|
||||||
if (!getDimensionsEtc(tok->astOperand1(), mSettings, &dimensions, &errorPath, &mightBeLarger))
|
MathLib::bigint path = 0;
|
||||||
|
if (!getDimensionsEtc(tok->astOperand1(), mSettings, &dimensions, &errorPath, &mightBeLarger, &path))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Positive index
|
// Positive index
|
||||||
if (!mightBeLarger) { // TODO check arrays with dim 1 also
|
if (!mightBeLarger) { // TODO check arrays with dim 1 also
|
||||||
const std::vector<const ValueFlow::Value *> &indexValues = getOverrunIndexValues(tok, tok->astOperand1(), dimensions, indexTokens);
|
const std::vector<const ValueFlow::Value *> &indexValues = getOverrunIndexValues(tok, tok->astOperand1(), dimensions, indexTokens, path);
|
||||||
if (!indexValues.empty())
|
if (!indexValues.empty())
|
||||||
arrayIndexError(tok, dimensions, indexValues);
|
arrayIndexError(tok, dimensions, indexValues);
|
||||||
}
|
}
|
||||||
|
@ -452,14 +457,15 @@ void CheckBufferOverrun::pointerArithmetic()
|
||||||
std::vector<Dimension> dimensions;
|
std::vector<Dimension> dimensions;
|
||||||
ErrorPath errorPath;
|
ErrorPath errorPath;
|
||||||
bool mightBeLarger = false;
|
bool mightBeLarger = false;
|
||||||
if (!getDimensionsEtc(arrayToken, mSettings, &dimensions, &errorPath, &mightBeLarger))
|
MathLib::bigint path = 0;
|
||||||
|
if (!getDimensionsEtc(arrayToken, mSettings, &dimensions, &errorPath, &mightBeLarger, &path))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (tok->str() == "+") {
|
if (tok->str() == "+") {
|
||||||
// Positive index
|
// Positive index
|
||||||
if (!mightBeLarger) { // TODO check arrays with dim 1 also
|
if (!mightBeLarger) { // TODO check arrays with dim 1 also
|
||||||
const std::vector<const Token *> indexTokens{indexToken};
|
const std::vector<const Token *> indexTokens{indexToken};
|
||||||
const std::vector<const ValueFlow::Value *> &indexValues = getOverrunIndexValues(tok, arrayToken, dimensions, indexTokens);
|
const std::vector<const ValueFlow::Value *> &indexValues = getOverrunIndexValues(tok, arrayToken, dimensions, indexTokens, path);
|
||||||
if (!indexValues.empty())
|
if (!indexValues.empty())
|
||||||
pointerArithmeticError(tok, indexToken, indexValues.front());
|
pointerArithmeticError(tok, indexToken, indexValues.front());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1646,6 +1646,8 @@ void Token::printValueFlow(bool xml, std::ostream &out) const
|
||||||
if (value.indirect > 0)
|
if (value.indirect > 0)
|
||||||
for (int i=0; i<value.indirect; i++)
|
for (int i=0; i<value.indirect; i++)
|
||||||
out << "*";
|
out << "*";
|
||||||
|
if (value.path > 0)
|
||||||
|
out << "@" << value.path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xml)
|
if (xml)
|
||||||
|
|
|
@ -357,6 +357,10 @@ static void combineValueProperties(const ValueFlow::Value &value1, const ValueFl
|
||||||
if (value1.bound == ValueFlow::Value::Bound::Lower || value2.bound == ValueFlow::Value::Bound::Lower)
|
if (value1.bound == ValueFlow::Value::Bound::Lower || value2.bound == ValueFlow::Value::Bound::Lower)
|
||||||
result->bound = ValueFlow::Value::Bound::Lower;
|
result->bound = ValueFlow::Value::Bound::Lower;
|
||||||
}
|
}
|
||||||
|
if (value1.path != value2.path)
|
||||||
|
result->path = -1;
|
||||||
|
else
|
||||||
|
result->path = value1.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Token *getCastTypeStartToken(const Token *parent)
|
static const Token *getCastTypeStartToken(const Token *parent)
|
||||||
|
@ -380,6 +384,9 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti
|
||||||
if (!tok->addValue(value))
|
if (!tok->addValue(value))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (value.path < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
Token *parent = tok->astParent();
|
Token *parent = tok->astParent();
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return;
|
return;
|
||||||
|
@ -389,6 +396,8 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti
|
||||||
if (parent->str() == "+") {
|
if (parent->str() == "+") {
|
||||||
for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) {
|
for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) {
|
||||||
for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
|
for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
|
||||||
|
if (value1.path != value2.path)
|
||||||
|
continue;
|
||||||
ValueFlow::Value result;
|
ValueFlow::Value result;
|
||||||
result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE;
|
||||||
if (value1.isContainerSizeValue() && value2.isContainerSizeValue())
|
if (value1.isContainerSizeValue() && value2.isContainerSizeValue())
|
||||||
|
@ -551,6 +560,8 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti
|
||||||
if (value1.isTokValue() && (!parent->isComparisonOp() || value1.tokvalue->tokType() != Token::eString))
|
if (value1.isTokValue() && (!parent->isComparisonOp() || value1.tokvalue->tokType() != Token::eString))
|
||||||
continue;
|
continue;
|
||||||
for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
|
for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) {
|
||||||
|
if (value1.path != value2.path)
|
||||||
|
continue;
|
||||||
if (noninvertible && value2.isImpossible())
|
if (noninvertible && value2.isImpossible())
|
||||||
continue;
|
continue;
|
||||||
if (!value2.isIntValue() && !value2.isFloatValue() && !value2.isTokValue())
|
if (!value2.isIntValue() && !value2.isFloatValue() && !value2.isTokValue())
|
||||||
|
@ -4793,9 +4804,14 @@ static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue,
|
||||||
setTokenValues(tok, results, settings);
|
setTokenValues(tok, results, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowSubFunction(TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings)
|
static void valueFlowSubFunction(TokenList* tokenlist, SymbolDatabase* symboldatabase, ErrorLogger* errorLogger, const Settings* settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
for (const Scope* scope : symboldatabase->functionScopes) {
|
||||||
|
const Function* function = scope->function;
|
||||||
|
if (!function)
|
||||||
|
continue;
|
||||||
|
int id = 0;
|
||||||
|
for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
|
||||||
if (!Token::Match(tok, "%name% ("))
|
if (!Token::Match(tok, "%name% ("))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -4812,6 +4828,7 @@ static void valueFlowSubFunction(TokenList* tokenlist, ErrorLogger* errorLogger,
|
||||||
if (!calledFunctionScope)
|
if (!calledFunctionScope)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
id++;
|
||||||
// TODO: Rewrite this. It does not work well to inject 1 argument at a time.
|
// TODO: Rewrite this. It does not work well to inject 1 argument at a time.
|
||||||
const std::vector<const Token *> &callArguments = getArguments(tok);
|
const std::vector<const Token *> &callArguments = getArguments(tok);
|
||||||
for (int argnr = 0U; argnr < callArguments.size(); ++argnr) {
|
for (int argnr = 0U; argnr < callArguments.size(); ++argnr) {
|
||||||
|
@ -4843,6 +4860,7 @@ static void valueFlowSubFunction(TokenList* tokenlist, ErrorLogger* errorLogger,
|
||||||
argtok->expressionString() +
|
argtok->expressionString() +
|
||||||
"' value is " +
|
"' value is " +
|
||||||
v.infoString());
|
v.infoString());
|
||||||
|
v.path = 256 * v.path + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// passed values are not "known"..
|
// passed values are not "known"..
|
||||||
|
@ -4854,6 +4872,7 @@ static void valueFlowSubFunction(TokenList* tokenlist, ErrorLogger* errorLogger,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void valueFlowFunctionDefaultParameter(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
static void valueFlowFunctionDefaultParameter(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings)
|
||||||
|
@ -5752,6 +5771,7 @@ ValueFlow::Value::Value(const Token* c, long long val)
|
||||||
conditional(false),
|
conditional(false),
|
||||||
defaultArg(false),
|
defaultArg(false),
|
||||||
indirect(0),
|
indirect(0),
|
||||||
|
path(0),
|
||||||
lifetimeKind(LifetimeKind::Object),
|
lifetimeKind(LifetimeKind::Object),
|
||||||
lifetimeScope(LifetimeScope::Local),
|
lifetimeScope(LifetimeScope::Local),
|
||||||
valueKind(ValueKind::Possible)
|
valueKind(ValueKind::Possible)
|
||||||
|
@ -5833,7 +5853,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
||||||
valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowSubFunction(tokenlist, errorLogger, settings);
|
valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings);
|
valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings);
|
||||||
if (tokenlist->isCPP()) {
|
if (tokenlist->isCPP()) {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "mathlib.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
@ -69,6 +70,7 @@ namespace ValueFlow {
|
||||||
conditional(false),
|
conditional(false),
|
||||||
defaultArg(false),
|
defaultArg(false),
|
||||||
indirect(0),
|
indirect(0),
|
||||||
|
path(0),
|
||||||
lifetimeKind(LifetimeKind::Object),
|
lifetimeKind(LifetimeKind::Object),
|
||||||
lifetimeScope(LifetimeScope::Local),
|
lifetimeScope(LifetimeScope::Local),
|
||||||
valueKind(ValueKind::Possible)
|
valueKind(ValueKind::Possible)
|
||||||
|
@ -247,6 +249,9 @@ namespace ValueFlow {
|
||||||
|
|
||||||
int indirect;
|
int indirect;
|
||||||
|
|
||||||
|
/** Path id */
|
||||||
|
MathLib::bigint path;
|
||||||
|
|
||||||
enum class LifetimeKind {Object, Lambda, Iterator, Address} lifetimeKind;
|
enum class LifetimeKind {Object, Lambda, Iterator, Address} lifetimeKind;
|
||||||
|
|
||||||
enum class LifetimeScope { Local, Argument } lifetimeScope;
|
enum class LifetimeScope { Local, Argument } lifetimeScope;
|
||||||
|
|
|
@ -128,6 +128,7 @@ private:
|
||||||
TEST_CASE(array_index_47); // #5849
|
TEST_CASE(array_index_47); // #5849
|
||||||
TEST_CASE(array_index_48); // #9478
|
TEST_CASE(array_index_48); // #9478
|
||||||
TEST_CASE(array_index_49); // #8653
|
TEST_CASE(array_index_49); // #8653
|
||||||
|
TEST_CASE(array_index_50);
|
||||||
TEST_CASE(array_index_multidim);
|
TEST_CASE(array_index_multidim);
|
||||||
TEST_CASE(array_index_switch_in_for);
|
TEST_CASE(array_index_switch_in_for);
|
||||||
TEST_CASE(array_index_for_in_for); // FP: #2634
|
TEST_CASE(array_index_for_in_for); // FP: #2634
|
||||||
|
@ -1509,6 +1510,18 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void array_index_50() {
|
||||||
|
check("void f(const char * str) {\n"
|
||||||
|
" int len = strlen(str);\n"
|
||||||
|
" (void)str[len - 1];\n"
|
||||||
|
"}\n"
|
||||||
|
"void g() {\n"
|
||||||
|
" f(\"12345678\");\n"
|
||||||
|
" f(\"12345\");\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void array_index_multidim() {
|
void array_index_multidim() {
|
||||||
check("void f()\n"
|
check("void f()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
|
Loading…
Reference in New Issue