Verification; Check function argument values

This commit is contained in:
Daniel Marjamäki 2019-12-23 22:10:43 +01:00
parent 270140f1fa
commit 747a01f74d
3 changed files with 116 additions and 1 deletions

View File

@ -1572,8 +1572,64 @@ void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer,
}; };
#endif #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; std::vector<ExprEngine::Callback> callbacks;
callbacks.push_back(divByZero); callbacks.push_back(divByZero);
callbacks.push_back(checkFunctionCall);
#ifdef VERIFY_INTEGEROVERFLOW #ifdef VERIFY_INTEGEROVERFLOW
callbacks.push_back(integerOverflow); callbacks.push_back(integerOverflow);
#endif #endif

View File

@ -859,6 +859,56 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co
return Error(OK); 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 bool Library::isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const
{ {
const ArgumentChecks *ac = getarg(ftok, argnr); const ArgumentChecks *ac = getarg(ftok, argnr);

View File

@ -293,7 +293,6 @@ public:
Direction direction; Direction direction;
}; };
struct Function { struct Function {
std::map<int, ArgumentChecks> argumentChecks; // argument nr => argument data std::map<int, ArgumentChecks> argumentChecks; // argument nr => argument data
bool use; bool use;
@ -340,6 +339,16 @@ public:
return arg ? arg->valid : emptyString; 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::IteratorInfo *getArgIteratorInfo(const Token *ftok, int argnr) const {
const ArgumentChecks *arg = getarg(ftok, argnr); const ArgumentChecks *arg = getarg(ftok, argnr);
return arg && arg->iteratorInfo.it ? &arg->iteratorInfo : nullptr; return arg && arg->iteratorInfo.it ? &arg->iteratorInfo : nullptr;