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;
|
||||
}
|
||||
break;
|
||||
case Library::ArgumentChecks::MinSize::STRLEN:
|
||||
if (argtok->type() == Token::eString && Token::getStrLength(argtok) >= arraySize)
|
||||
case Library::ArgumentChecks::MinSize::STRLEN: {
|
||||
const Token *strtoken = argtok->getValueTokenMaxStrLength();
|
||||
if (strtoken && Token::getStrLength(strtoken) >= arraySize)
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Library::ArgumentChecks::MinSize::SIZEOF:
|
||||
if (argtok->type() == Token::eString && Token::getStrLength(argtok) >= arraySize)
|
||||
error = true;
|
||||
|
@ -573,7 +575,7 @@ void CheckBufferOverrun::checkScope(const Token *tok, const std::vector<std::str
|
|||
// taking address of 1 past end?
|
||||
if (totalIndex == totalElements) {
|
||||
const bool addr = (tok3 && (tok3->str() == "&" ||
|
||||
Token::simpleMatch(tok3->previous(), "& (")));
|
||||
Token::simpleMatch(tok3->previous(), "& (")));
|
||||
if (addr)
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1213,15 +1213,23 @@ void Token::printValueFlow(bool xml, std::ostream &out) const
|
|||
out << " " << tok->str() << ":{";
|
||||
for (std::list<ValueFlow::Value>::const_iterator it=tok->values.begin(); it!=tok->values.end(); ++it) {
|
||||
if (xml) {
|
||||
out << " <value intvalue=\"" << it->intvalue << "\"";
|
||||
if (it->condition) {
|
||||
out << " <value ";
|
||||
if (it->tokvalue)
|
||||
out << "tokvalue=\"" << it->tokvalue << '\"';
|
||||
else
|
||||
out << "intvalue=\"" << it->intvalue << '\"';
|
||||
if (it->condition)
|
||||
out << " condition-line=\"" << it->condition->linenr() << '\"';
|
||||
}
|
||||
out << "/>" << std::endl;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -1238,7 +1246,7 @@ const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Sett
|
|||
const ValueFlow::Value *ret = nullptr;
|
||||
std::list<ValueFlow::Value>::const_iterator 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))
|
||||
ret = &(*it);
|
||||
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;
|
||||
std::list<ValueFlow::Value>::const_iterator 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))
|
||||
ret = &(*it);
|
||||
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 {
|
||||
std::list<ValueFlow::Value>::const_iterator it;
|
||||
for (it = values.begin(); it != values.end(); ++it) {
|
||||
if (it->intvalue == val)
|
||||
if (it->intvalue == val && !it->tokvalue)
|
||||
return &(*it);
|
||||
}
|
||||
return NULL;
|
||||
|
@ -658,6 +658,8 @@ public:
|
|||
const ValueFlow::Value *ret = nullptr;
|
||||
std::list<ValueFlow::Value>::const_iterator it;
|
||||
for (it = values.begin(); it != values.end(); ++it) {
|
||||
if (it->tokvalue)
|
||||
continue;
|
||||
if ((!ret || it->intvalue > ret->intvalue) &&
|
||||
((it->condition != NULL) == condition))
|
||||
ret = &(*it);
|
||||
|
@ -668,6 +670,22 @@ public:
|
|||
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 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:
|
||||
|
||||
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
|
||||
std::list<ValueFlow::Value>::iterator it;
|
||||
for (it = tok->values.begin(); it != tok->values.end(); ++it) {
|
||||
if (it->intvalue == value.intvalue) {
|
||||
if (it->inconclusive && !value.inconclusive) {
|
||||
*it = value;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
// different intvalue => continue
|
||||
if (it->intvalue != value.intvalue)
|
||||
continue;
|
||||
|
||||
// different tokvalue => continue
|
||||
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()) {
|
||||
|
@ -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)
|
||||
{
|
||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||
|
@ -1414,6 +1457,8 @@ void ValueFlow::setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const
|
|||
tok->values.clear();
|
||||
|
||||
valueFlowNumber(tokenlist);
|
||||
valueFlowString(tokenlist);
|
||||
valueFlowPointerAlias(tokenlist);
|
||||
valueFlowFunctionReturn(tokenlist, errorLogger, settings);
|
||||
valueFlowBitAnd(tokenlist);
|
||||
valueFlowForLoop(tokenlist, errorLogger, settings);
|
||||
|
|
|
@ -29,12 +29,15 @@ class Settings;
|
|||
namespace ValueFlow {
|
||||
class Value {
|
||||
public:
|
||||
Value(long long val = 0) : intvalue(val), 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(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), tokvalue(nullptr), varvalue(val), condition(c), varId(0U), conditional(false), inconclusive(false) {}
|
||||
|
||||
/** int value */
|
||||
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 */
|
||||
long long varvalue;
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ private:
|
|||
|
||||
void run() {
|
||||
TEST_CASE(valueFlowNumber);
|
||||
TEST_CASE(valueFlowString);
|
||||
TEST_CASE(valueFlowPointerAlias);
|
||||
|
||||
TEST_CASE(valueFlowBitAnd);
|
||||
|
||||
|
@ -81,7 +83,7 @@ private:
|
|||
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 (it->intvalue == value)
|
||||
if (it->intvalue == value && !it->tokvalue)
|
||||
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[]) {
|
||||
Settings settings;
|
||||
settings.debugwarnings = true;
|
||||
|
@ -114,7 +144,7 @@ private:
|
|||
|
||||
ValueFlow::Value valueOfTok(const char code[], const char 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() {
|
||||
|
@ -126,6 +156,29 @@ private:
|
|||
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() {
|
||||
const char *code;
|
||||
/*
|
||||
|
@ -602,6 +655,7 @@ private:
|
|||
" a = 2 + x;\n"
|
||||
"}";
|
||||
ASSERT_EQUALS(true, testValueOfX(code, 4U, 1));
|
||||
ASSERT_EQUALS(true, testValueOfX(code, 4U, 2));
|
||||
|
||||
code = "void f() {\n"
|
||||
" int x = 123;\n"
|
||||
|
|
Loading…
Reference in New Issue