Verification; Check function argument values
This commit is contained in:
parent
270140f1fa
commit
747a01f74d
|
@ -1572,8 +1572,64 @@ void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer,
|
|||
};
|
||||
#endif
|
||||
|
||||
std::function<void(const Token *, const ExprEngine::Value &, ExprEngine::DataBase *)> checkFunctionCall = [=](const Token *tok, const ExprEngine::Value &value, ExprEngine::DataBase *dataBase) {
|
||||
if (!Token::Match(tok->astParent(), "[(,]"))
|
||||
return;
|
||||
int num = (tok == tok->astParent()->astOperand2()) ? 1 : 0;
|
||||
const Token *parent = tok->astParent();
|
||||
while (Token::simpleMatch(parent, ",")) {
|
||||
parent = parent->astParent();
|
||||
++num;
|
||||
}
|
||||
if (!parent || parent->str() != "(")
|
||||
return;
|
||||
|
||||
// Check invalid function argument values..
|
||||
for (const Library::InvalidArgValue &invalidArgValue : Library::getInvalidArgValues(settings->library.validarg(parent->astOperand1(), num))) {
|
||||
bool err = false;
|
||||
std::string bad;
|
||||
switch (invalidArgValue.type) {
|
||||
case Library::InvalidArgValue::eq:
|
||||
err = value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op1));
|
||||
bad = "equals " + invalidArgValue.op1;
|
||||
break;
|
||||
case Library::InvalidArgValue::le:
|
||||
err = value.isLessThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1) + 1);
|
||||
bad = "less equal " + invalidArgValue.op1;
|
||||
break;
|
||||
case Library::InvalidArgValue::lt:
|
||||
err = value.isLessThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1));
|
||||
bad = "less than " + invalidArgValue.op1;
|
||||
break;
|
||||
case Library::InvalidArgValue::ge:
|
||||
err = value.isGreaterThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1) - 1);
|
||||
bad = "greater equal " + invalidArgValue.op1;
|
||||
break;
|
||||
case Library::InvalidArgValue::gt:
|
||||
err = value.isGreaterThan(dataBase, MathLib::toLongNumber(invalidArgValue.op1));
|
||||
bad = "greater than " + invalidArgValue.op1;
|
||||
break;
|
||||
case Library::InvalidArgValue::range:
|
||||
// TODO
|
||||
err = value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op1));
|
||||
err |= value.isEqual(dataBase, MathLib::toLongNumber(invalidArgValue.op2));
|
||||
bad = "range " + invalidArgValue.op1 + "-" + invalidArgValue.op2;
|
||||
break;
|
||||
};
|
||||
|
||||
if (err) {
|
||||
dataBase->addError(tok->linenr());
|
||||
std::list<const Token*> callstack{tok};
|
||||
ErrorLogger::ErrorMessage errmsg(callstack, &tokenizer->list, Severity::SeverityType::error, "verificationInvalidArgValue", "There is function call, cannot determine that argument value is valid. Bad value: " + bad, CWE(0), false);
|
||||
errorLogger->reportErr(errmsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<ExprEngine::Callback> callbacks;
|
||||
callbacks.push_back(divByZero);
|
||||
callbacks.push_back(checkFunctionCall);
|
||||
#ifdef VERIFY_INTEGEROVERFLOW
|
||||
callbacks.push_back(integerOverflow);
|
||||
#endif
|
||||
|
|
|
@ -859,6 +859,56 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co
|
|||
return Error(OK);
|
||||
}
|
||||
|
||||
std::vector<Library::InvalidArgValue> Library::getInvalidArgValues(const std::string &validExpr)
|
||||
{
|
||||
std::vector<Library::InvalidArgValue> valid;
|
||||
TokenList tokenList(nullptr);
|
||||
gettokenlistfromvalid(validExpr, tokenList);
|
||||
for (const Token *tok = tokenList.front(); tok; tok = tok ? tok->next() : nullptr) {
|
||||
if (tok->str() == ",")
|
||||
continue;
|
||||
if (Token::Match(tok, ": %num%")) {
|
||||
valid.push_back(InvalidArgValue{InvalidArgValue::le, tok->next()->str(), std::string()});
|
||||
tok = tok->tokAt(2);
|
||||
} else if (Token::Match(tok, "%num% : %num%")) {
|
||||
valid.push_back(InvalidArgValue{InvalidArgValue::range, tok->str(), tok->strAt(2)});
|
||||
tok = tok->tokAt(3);
|
||||
} else if (Token::Match(tok, "%num% :")) {
|
||||
valid.push_back(InvalidArgValue{InvalidArgValue::ge, tok->str(), std::string()});
|
||||
tok = tok->tokAt(2);
|
||||
} else if (Token::Match(tok, "%num%")) {
|
||||
valid.push_back(InvalidArgValue{InvalidArgValue::eq, tok->str(), std::string()});
|
||||
tok = tok->next();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Library::InvalidArgValue> invalid;
|
||||
if (valid.empty())
|
||||
return invalid;
|
||||
|
||||
if (valid[0].type == InvalidArgValue::ge || valid[0].type == InvalidArgValue::eq)
|
||||
invalid.push_back(InvalidArgValue{InvalidArgValue::lt, valid[0].op1, std::string()});
|
||||
if (valid.back().type == InvalidArgValue::le || valid.back().type == InvalidArgValue::eq)
|
||||
invalid.push_back(InvalidArgValue{InvalidArgValue::gt, valid[0].op1, std::string()});
|
||||
for (int i = 0; i + 1 < valid.size(); i++) {
|
||||
const InvalidArgValue &v1 = valid[i];
|
||||
const InvalidArgValue &v2 = valid[i + 1];
|
||||
if (v1.type == InvalidArgValue::le && v2.type == InvalidArgValue::ge) {
|
||||
if (v1.isInt()) {
|
||||
MathLib::bigint op1 = MathLib::toLongNumber(v1.op1);
|
||||
MathLib::bigint op2 = MathLib::toLongNumber(v2.op1);
|
||||
if (op1 + 1 == op2 - 1)
|
||||
invalid.push_back(InvalidArgValue{InvalidArgValue::eq, MathLib::toString(op1 + 1), std::string()});
|
||||
else
|
||||
invalid.push_back(InvalidArgValue{InvalidArgValue::range, MathLib::toString(op1 + 1), MathLib::toString(op2 - 1)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return invalid;
|
||||
}
|
||||
|
||||
|
||||
bool Library::isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const
|
||||
{
|
||||
const ArgumentChecks *ac = getarg(ftok, argnr);
|
||||
|
|
|
@ -293,7 +293,6 @@ public:
|
|||
Direction direction;
|
||||
};
|
||||
|
||||
|
||||
struct Function {
|
||||
std::map<int, ArgumentChecks> argumentChecks; // argument nr => argument data
|
||||
bool use;
|
||||
|
@ -340,6 +339,16 @@ public:
|
|||
return arg ? arg->valid : emptyString;
|
||||
}
|
||||
|
||||
struct InvalidArgValue {
|
||||
enum Type {le, lt, eq, ge, gt, range} type;
|
||||
std::string op1;
|
||||
std::string op2;
|
||||
bool isInt() const {
|
||||
return MathLib::isInt(op1);
|
||||
}
|
||||
};
|
||||
static std::vector<InvalidArgValue> getInvalidArgValues(const std::string &validExpr);
|
||||
|
||||
const ArgumentChecks::IteratorInfo *getArgIteratorInfo(const Token *ftok, int argnr) const {
|
||||
const ArgumentChecks *arg = getarg(ftok, argnr);
|
||||
return arg && arg->iteratorInfo.it ? &arg->iteratorInfo : nullptr;
|
||||
|
|
Loading…
Reference in New Issue