ValueFlow: start adding valueflow handling of strings and pointer aliases
This commit is contained in:
parent
57c055fcc4
commit
79fc549de0
|
@ -303,10 +303,12 @@ static bool checkMinSizes(const std::list<Library::ArgumentChecks::MinSize> &min
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Library::ArgumentChecks::MinSize::STRLEN:
|
case Library::ArgumentChecks::MinSize::STRLEN: {
|
||||||
if (argtok->type() == Token::eString && Token::getStrLength(argtok) >= arraySize)
|
const Token *strtoken = argtok->getValueTokenMaxStrLength();
|
||||||
|
if (strtoken && Token::getStrLength(strtoken) >= arraySize)
|
||||||
error = true;
|
error = true;
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
case Library::ArgumentChecks::MinSize::SIZEOF:
|
case Library::ArgumentChecks::MinSize::SIZEOF:
|
||||||
if (argtok->type() == Token::eString && Token::getStrLength(argtok) >= arraySize)
|
if (argtok->type() == Token::eString && Token::getStrLength(argtok) >= arraySize)
|
||||||
error = true;
|
error = true;
|
||||||
|
@ -573,7 +575,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
|
||||||
// taking address of 1 past end?
|
// taking address of 1 past end?
|
||||||
if (totalIndex == totalElements) {
|
if (totalIndex == totalElements) {
|
||||||
const bool addr = (tok3 && (tok3->str() == "&" ||
|
const bool addr = (tok3 && (tok3->str() == "&" ||
|
||||||
Token::simpleMatch(tok3->previous(), "& (")));
|
Token::simpleMatch(tok3->previous(), "& (")));
|
||||||
if (addr)
|
if (addr)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1213,15 +1213,23 @@ void Token::printValueFlow(bool xml, std::ostream &out) const
|
||||||
out << " " << tok->str() << ":{";
|
out << " " << tok->str() << ":{";
|
||||||
for (std::list<ValueFlow::Value>::const_iterator it=tok->values.begin(); it!=tok->values.end(); ++it) {
|
for (std::list<ValueFlow::Value>::const_iterator it=tok->values.begin(); it!=tok->values.end(); ++it) {
|
||||||
if (xml) {
|
if (xml) {
|
||||||
out << " <value intvalue=\"" << it->intvalue << "\"";
|
out << " <value ";
|
||||||
if (it->condition) {
|
if (it->tokvalue)
|
||||||
|
out << "tokvalue=\"" << it->tokvalue << '\"';
|
||||||
|
else
|
||||||
|
out << "intvalue=\"" << it->intvalue << '\"';
|
||||||
|
if (it->condition)
|
||||||
out << " condition-line=\"" << it->condition->linenr() << '\"';
|
out << " condition-line=\"" << it->condition->linenr() << '\"';
|
||||||
}
|
|
||||||
out << "/>" << std::endl;
|
out << "/>" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
out << (it == tok->values.begin() ? "" : ",") << it->intvalue;
|
if (it != tok->values.begin())
|
||||||
|
out << ",";
|
||||||
|
if (it->tokvalue)
|
||||||
|
out << it->tokvalue->str();
|
||||||
|
else
|
||||||
|
out << it->intvalue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (xml)
|
if (xml)
|
||||||
|
@ -1238,7 +1246,7 @@ const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Sett
|
||||||
const ValueFlow::Value *ret = nullptr;
|
const ValueFlow::Value *ret = nullptr;
|
||||||
std::list<ValueFlow::Value>::const_iterator it;
|
std::list<ValueFlow::Value>::const_iterator it;
|
||||||
for (it = values.begin(); it != values.end(); ++it) {
|
for (it = values.begin(); it != values.end(); ++it) {
|
||||||
if (it->intvalue <= val) {
|
if (it->intvalue <= val && !it->tokvalue) {
|
||||||
if (!ret || ret->inconclusive || (ret->condition && !it->inconclusive))
|
if (!ret || ret->inconclusive || (ret->condition && !it->inconclusive))
|
||||||
ret = &(*it);
|
ret = &(*it);
|
||||||
if (!ret->inconclusive && !ret->condition)
|
if (!ret->inconclusive && !ret->condition)
|
||||||
|
@ -1259,7 +1267,7 @@ const ValueFlow::Value * Token::getValueGE(const MathLib::bigint val, const Sett
|
||||||
const ValueFlow::Value *ret = nullptr;
|
const ValueFlow::Value *ret = nullptr;
|
||||||
std::list<ValueFlow::Value>::const_iterator it;
|
std::list<ValueFlow::Value>::const_iterator it;
|
||||||
for (it = values.begin(); it != values.end(); ++it) {
|
for (it = values.begin(); it != values.end(); ++it) {
|
||||||
if (it->intvalue >= val) {
|
if (it->intvalue >= val && !it->tokvalue) {
|
||||||
if (!ret || ret->inconclusive || (ret->condition && !it->inconclusive))
|
if (!ret || ret->inconclusive || (ret->condition && !it->inconclusive))
|
||||||
ret = &(*it);
|
ret = &(*it);
|
||||||
if (!ret->inconclusive && !ret->condition)
|
if (!ret->inconclusive && !ret->condition)
|
||||||
|
|
20
lib/token.h
20
lib/token.h
|
@ -648,7 +648,7 @@ public:
|
||||||
const ValueFlow::Value * getValue(const MathLib::bigint val) const {
|
const ValueFlow::Value * getValue(const MathLib::bigint val) const {
|
||||||
std::list<ValueFlow::Value>::const_iterator it;
|
std::list<ValueFlow::Value>::const_iterator it;
|
||||||
for (it = values.begin(); it != values.end(); ++it) {
|
for (it = values.begin(); it != values.end(); ++it) {
|
||||||
if (it->intvalue == val)
|
if (it->intvalue == val && !it->tokvalue)
|
||||||
return &(*it);
|
return &(*it);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -658,6 +658,8 @@ public:
|
||||||
const ValueFlow::Value *ret = nullptr;
|
const ValueFlow::Value *ret = nullptr;
|
||||||
std::list<ValueFlow::Value>::const_iterator it;
|
std::list<ValueFlow::Value>::const_iterator it;
|
||||||
for (it = values.begin(); it != values.end(); ++it) {
|
for (it = values.begin(); it != values.end(); ++it) {
|
||||||
|
if (it->tokvalue)
|
||||||
|
continue;
|
||||||
if ((!ret || it->intvalue > ret->intvalue) &&
|
if ((!ret || it->intvalue > ret->intvalue) &&
|
||||||
((it->condition != NULL) == condition))
|
((it->condition != NULL) == condition))
|
||||||
ret = &(*it);
|
ret = &(*it);
|
||||||
|
@ -668,6 +670,22 @@ public:
|
||||||
const ValueFlow::Value * getValueLE(const MathLib::bigint val, const Settings *settings) const;
|
const ValueFlow::Value * getValueLE(const MathLib::bigint val, const Settings *settings) const;
|
||||||
const ValueFlow::Value * getValueGE(const MathLib::bigint val, const Settings *settings) const;
|
const ValueFlow::Value * getValueGE(const MathLib::bigint val, const Settings *settings) const;
|
||||||
|
|
||||||
|
const Token *getValueTokenMaxStrLength() const {
|
||||||
|
const Token *ret = nullptr;
|
||||||
|
std::size_t maxlength = 0U;
|
||||||
|
std::list<ValueFlow::Value>::const_iterator it;
|
||||||
|
for (it = values.begin(); it != values.end(); ++it) {
|
||||||
|
if (it->tokvalue && it->tokvalue->type() == Token::eString) {
|
||||||
|
std::size_t length = Token::getStrLength(it->tokvalue);
|
||||||
|
if (!ret || length > maxlength) {
|
||||||
|
maxlength = length;
|
||||||
|
ret = it->tokvalue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void next(Token *nextToken) {
|
void next(Token *nextToken) {
|
||||||
|
|
|
@ -254,13 +254,24 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value)
|
||||||
// if value already exists, don't add it again
|
// if value already exists, don't add it again
|
||||||
std::list<ValueFlow::Value>::iterator it;
|
std::list<ValueFlow::Value>::iterator it;
|
||||||
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
|
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
|
||||||
if (it->intvalue == value.intvalue) {
|
// different intvalue => continue
|
||||||
if (it->inconclusive && !value.inconclusive) {
|
if (it->intvalue != value.intvalue)
|
||||||
*it = value;
|
continue;
|
||||||
break;
|
|
||||||
}
|
// different tokvalue => continue
|
||||||
return;
|
if ((it->tokvalue == nullptr) != (value.tokvalue == nullptr))
|
||||||
|
continue;
|
||||||
|
if ((value.tokvalue != nullptr) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// same value, but old value is inconclusive so replace it
|
||||||
|
if (it->inconclusive && !value.inconclusive) {
|
||||||
|
*it = value;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same value already exists, don't add new value
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it == tok->values.end()) {
|
if (it == tok->values.end()) {
|
||||||
|
@ -330,6 +341,38 @@ static void valueFlowNumber(TokenList *tokenlist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void valueFlowString(TokenList *tokenlist)
|
||||||
|
{
|
||||||
|
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
|
if (tok->type() == Token::eString) {
|
||||||
|
ValueFlow::Value strvalue;
|
||||||
|
strvalue.tokvalue = tok;
|
||||||
|
setTokenValue(tok, strvalue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void valueFlowPointerAlias(TokenList *tokenlist)
|
||||||
|
{
|
||||||
|
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
|
// not address of
|
||||||
|
if (tok->str() != "&" || tok->astOperand2())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// parent should be a '='
|
||||||
|
if (!Token::simpleMatch(tok->astParent(), "="))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// child should be some buffer or variable
|
||||||
|
if (!Token::Match(tok->astOperand1(), "%var%|.|["))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ValueFlow::Value value;
|
||||||
|
value.tokvalue = tok;
|
||||||
|
setTokenValue(tok, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void valueFlowBitAnd(TokenList *tokenlist)
|
static void valueFlowBitAnd(TokenList *tokenlist)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
|
@ -1414,6 +1457,8 @@ void ValueFlow::setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const
|
||||||
tok->values.clear();
|
tok->values.clear();
|
||||||
|
|
||||||
valueFlowNumber(tokenlist);
|
valueFlowNumber(tokenlist);
|
||||||
|
valueFlowString(tokenlist);
|
||||||
|
valueFlowPointerAlias(tokenlist);
|
||||||
valueFlowFunctionReturn(tokenlist, errorLogger, settings);
|
valueFlowFunctionReturn(tokenlist, errorLogger, settings);
|
||||||
valueFlowBitAnd(tokenlist);
|
valueFlowBitAnd(tokenlist);
|
||||||
valueFlowForLoop(tokenlist, errorLogger, settings);
|
valueFlowForLoop(tokenlist, errorLogger, settings);
|
||||||
|
|
|
@ -29,12 +29,15 @@ class Settings;
|
||||||
namespace ValueFlow {
|
namespace ValueFlow {
|
||||||
class Value {
|
class Value {
|
||||||
public:
|
public:
|
||||||
Value(long long val = 0) : intvalue(val), varvalue(val), condition(0), varId(0U), conditional(false), inconclusive(false) {}
|
Value(long long val = 0) : intvalue(val), tokvalue(nullptr), varvalue(val), condition(0), varId(0U), conditional(false), inconclusive(false) {}
|
||||||
Value(const Token *c, long long val) : intvalue(val), varvalue(val), condition(c), varId(0U), conditional(false), inconclusive(false) {}
|
Value(const Token *c, long long val) : intvalue(val), tokvalue(nullptr), varvalue(val), condition(c), varId(0U), conditional(false), inconclusive(false) {}
|
||||||
|
|
||||||
/** int value */
|
/** int value */
|
||||||
long long intvalue;
|
long long intvalue;
|
||||||
|
|
||||||
|
/** token value - the token that has the value. this is used for pointer aliases, strings, etc. */
|
||||||
|
const Token *tokvalue;
|
||||||
|
|
||||||
/** For calculated values - variable value that calculated value depends on */
|
/** For calculated values - variable value that calculated value depends on */
|
||||||
long long varvalue;
|
long long varvalue;
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ private:
|
||||||
|
|
||||||
void run() {
|
void run() {
|
||||||
TEST_CASE(valueFlowNumber);
|
TEST_CASE(valueFlowNumber);
|
||||||
|
TEST_CASE(valueFlowString);
|
||||||
|
TEST_CASE(valueFlowPointerAlias);
|
||||||
|
|
||||||
TEST_CASE(valueFlowBitAnd);
|
TEST_CASE(valueFlowBitAnd);
|
||||||
|
|
||||||
|
@ -81,7 +83,7 @@ private:
|
||||||
if (tok->str() == "x" && tok->linenr() == linenr) {
|
if (tok->str() == "x" && tok->linenr() == linenr) {
|
||||||
std::list<ValueFlow::Value>::const_iterator it;
|
std::list<ValueFlow::Value>::const_iterator it;
|
||||||
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
|
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
|
||||||
if (it->intvalue == value)
|
if (it->intvalue == value && !it->tokvalue)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +93,34 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool testValueOfX(const char code[], unsigned int linenr, const char value[]) {
|
||||||
|
Settings settings;
|
||||||
|
|
||||||
|
// strcpy cfg
|
||||||
|
const char cfg[] = "<?xml version=\"1.0\"?>\n"
|
||||||
|
"<def>\n"
|
||||||
|
" <function name=\"strcpy\"> <arg nr=\"1\"><not-null/></arg> </function>\n"
|
||||||
|
"</def>";
|
||||||
|
settings.library.loadxmldata(cfg, sizeof(cfg));
|
||||||
|
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
|
||||||
|
for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
|
||||||
|
if (tok->str() == "x" && tok->linenr() == linenr) {
|
||||||
|
std::list<ValueFlow::Value>::const_iterator it;
|
||||||
|
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
|
||||||
|
if (Token::simpleMatch(it->tokvalue, value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void bailout(const char code[]) {
|
void bailout(const char code[]) {
|
||||||
Settings settings;
|
Settings settings;
|
||||||
settings.debugwarnings = true;
|
settings.debugwarnings = true;
|
||||||
|
@ -114,7 +144,7 @@ private:
|
||||||
|
|
||||||
ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) {
|
ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) {
|
||||||
std::list<ValueFlow::Value> values = tokenValues(code, tokstr);
|
std::list<ValueFlow::Value> values = tokenValues(code, tokstr);
|
||||||
return values.size() == 1U ? values.front() : ValueFlow::Value();
|
return values.size() == 1U && !values.front().tokvalue ? values.front() : ValueFlow::Value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowNumber() {
|
void valueFlowNumber() {
|
||||||
|
@ -126,6 +156,29 @@ private:
|
||||||
ASSERT_EQUALS(123, valueOfTok(code, "123").intvalue);
|
ASSERT_EQUALS(123, valueOfTok(code, "123").intvalue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void valueFlowString() {
|
||||||
|
const char *code;
|
||||||
|
|
||||||
|
code = "const char * f() {\n"
|
||||||
|
" static const char *x;\n"
|
||||||
|
" if (a) x = \"123\";\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(true, testValueOfX(code, 4, "\"123\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
void valueFlowPointerAlias() {
|
||||||
|
const char *code;
|
||||||
|
|
||||||
|
code = "const char * f() {\n"
|
||||||
|
" static const char *x;\n"
|
||||||
|
" static char ret[10];\n"
|
||||||
|
" if (a) x = &ret[0];\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(true, testValueOfX(code, 5, "& ret [ 0 ]"));
|
||||||
|
}
|
||||||
|
|
||||||
void valueFlowCalculations() {
|
void valueFlowCalculations() {
|
||||||
const char *code;
|
const char *code;
|
||||||
/*
|
/*
|
||||||
|
@ -602,6 +655,7 @@ private:
|
||||||
" a = 2 + x;\n"
|
" a = 2 + x;\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(true, testValueOfX(code, 4U, 1));
|
ASSERT_EQUALS(true, testValueOfX(code, 4U, 1));
|
||||||
|
ASSERT_EQUALS(true, testValueOfX(code, 4U, 2));
|
||||||
|
|
||||||
code = "void f() {\n"
|
code = "void f() {\n"
|
||||||
" int x = 123;\n"
|
" int x = 123;\n"
|
||||||
|
|
Loading…
Reference in New Issue