From 747a01f74d5b549938e9a25eb823b2a686f741d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Mon, 23 Dec 2019 22:10:43 +0100 Subject: [PATCH] Verification; Check function argument values --- lib/exprengine.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++ lib/library.cpp | 50 +++++++++++++++++++++++++++++++++++++++++ lib/library.h | 11 ++++++++- 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/lib/exprengine.cpp b/lib/exprengine.cpp index 2be4715c3..7fad46374 100644 --- a/lib/exprengine.cpp +++ b/lib/exprengine.cpp @@ -1572,8 +1572,64 @@ void ExprEngine::runChecks(ErrorLogger *errorLogger, const Tokenizer *tokenizer, }; #endif + std::function 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 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 callbacks; callbacks.push_back(divByZero); + callbacks.push_back(checkFunctionCall); #ifdef VERIFY_INTEGEROVERFLOW callbacks.push_back(integerOverflow); #endif diff --git a/lib/library.cpp b/lib/library.cpp index d48d59baf..44f9c078f 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -859,6 +859,56 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co return Error(OK); } +std::vector Library::getInvalidArgValues(const std::string &validExpr) +{ + std::vector 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 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); diff --git a/lib/library.h b/lib/library.h index 75ba07605..75416b2b8 100644 --- a/lib/library.h +++ b/lib/library.h @@ -293,7 +293,6 @@ public: Direction direction; }; - struct Function { std::map 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 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;