From 6b72274c6732963f9712980707aab5fb68bad203 Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Sat, 9 Jul 2022 00:40:32 -0500 Subject: [PATCH] Evaluate math library functions in valueflow (#4255) * Evaluate math library functions in valueflow * Format --- cfg/std.cfg | 90 +++++++------- lib/programmemory.cpp | 265 ++++++++++++++++++++++++++++++++++++++---- test/cfg/std.cpp | 35 ++++++ 3 files changed, 324 insertions(+), 66 deletions(-) diff --git a/cfg/std.cfg b/cfg/std.cfg index c7633313a..c89e82de1 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -119,7 +119,7 @@ - + acos(arg1) false @@ -131,7 +131,7 @@ - + acos(arg1) false @@ -143,7 +143,7 @@ - + acos(arg1) false @@ -231,7 +231,7 @@ - + sqrt(arg1) false @@ -243,7 +243,7 @@ - + sqrt(arg1) false @@ -255,7 +255,7 @@ - + sqrt(arg1) false @@ -279,7 +279,7 @@ - + sinh(arg1) false @@ -290,7 +290,7 @@ - + sinh(arg1) false @@ -301,7 +301,7 @@ - + sinh(arg1) false @@ -312,7 +312,7 @@ - + sin(arg1) false @@ -323,7 +323,7 @@ - + sin(arg1) false @@ -334,7 +334,7 @@ - + sin(arg1) false @@ -369,7 +369,7 @@ - + asin(arg1) false @@ -381,7 +381,7 @@ - + asin(arg1) false @@ -393,7 +393,7 @@ - + asin(arg1) false @@ -486,7 +486,7 @@ - + tan(arg1) false @@ -497,7 +497,7 @@ - + tan(arg1) false @@ -508,7 +508,7 @@ - + tan(arg1) false @@ -531,7 +531,7 @@ - + tanh(arg1) false @@ -542,7 +542,7 @@ - + tanh(arg1) false @@ -553,7 +553,7 @@ - + tanh(arg1) false @@ -688,7 +688,7 @@ - + atan(arg1) false @@ -699,7 +699,7 @@ - + atan(arg1) false @@ -710,7 +710,7 @@ - + atan(arg1) false @@ -799,7 +799,7 @@ - + atanh(arg1) false @@ -811,7 +811,7 @@ - + atanh(arg1) false @@ -823,7 +823,7 @@ - + atanh(arg1) false @@ -847,7 +847,7 @@ - + atan2(arg1, arg2) false @@ -861,7 +861,7 @@ - + atan2(arg1, arg2) false @@ -875,7 +875,7 @@ - + atan2(arg1, arg2) false @@ -1090,7 +1090,7 @@ - + cos(arg1) false @@ -1101,7 +1101,7 @@ - + cos(arg1) false @@ -1112,7 +1112,7 @@ - + cos(arg1) false @@ -1357,7 +1357,7 @@ - + exp(arg1) false @@ -1368,7 +1368,7 @@ - + exp(arg1) false @@ -1379,7 +1379,7 @@ - + exp(arg1) false @@ -3217,7 +3217,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + log(arg1) false @@ -3228,7 +3228,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + log(arg1) false @@ -3239,7 +3239,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + log(arg1) false @@ -3544,7 +3544,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + log10(arg1) false @@ -3556,7 +3556,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + log10(arg1) false @@ -3568,7 +3568,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + log10(arg1) false @@ -4126,7 +4126,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + pow(arg1, arg2) false @@ -4140,7 +4140,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + pow(arg1, arg2) false @@ -4154,7 +4154,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - + pow(arg1, arg2) false diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp index ddc5f73dd..8c072238f 100644 --- a/lib/programmemory.cpp +++ b/lib/programmemory.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -615,6 +616,231 @@ static ValueFlow::Value evaluate(const std::string& op, const ValueFlow::Value& return result; } +using BuiltinLibraryFunction = std::function&)>; +static std::unordered_map createBuiltinLibraryFunctions() +{ + std::unordered_map functions; + functions["strlen"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!(v.isTokValue() && v.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + v.valueType = ValueFlow::Value::ValueType::INT; + v.intvalue = Token::getStrLength(v.tokvalue); + v.tokvalue = nullptr; + return v; + }; + functions["sin"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::sin(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["cos"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::cos(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["tan"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::tan(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["asin"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::asin(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["acos"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::acos(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["atan"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::atan(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["atan2"] = [](const std::vector& args) { + if (args.size() != 2) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::atan2(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["pow"] = [](const std::vector& args) { + if (args.size() != 2) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::pow(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["sqrt"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::sqrt(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["exp"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::exp(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["log"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::log(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["log10"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::log10(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["sinh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::sinh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["cosh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::cosh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["tanh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::tanh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["asinh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::asinh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["acosh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::acosh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["atanh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::atanh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + return functions; +} + +static BuiltinLibraryFunction getBuiltinLibraryFunction(const std::string& name) +{ + static const std::unordered_map functions = createBuiltinLibraryFunctions(); + auto it = functions.find(name); + if (it == functions.end()) + return nullptr; + return it->second; +} + static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const Settings* settings) { ValueFlow::Value unknown = ValueFlow::Value::unknown(); @@ -787,29 +1013,26 @@ static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const const Function* f = ftok->function(); // TODO: Evaluate inline functions as well if (!f && settings && expr->str() == "(") { - std::unordered_map args; - int argn = 0; - for (const Token* tok : getArguments(expr)) { - ValueFlow::Value result = execute(tok, pm, settings); - if (!result.isUninitValue()) - args[argn] = result; - argn++; - } - // strlen is a special builtin - if (Token::simpleMatch(ftok, "strlen")) { - if (args.count(0) > 0) { - ValueFlow::Value v = args.at(0); - if (v.isTokValue() && v.tokvalue->tokType() == Token::eString) { - v.valueType = ValueFlow::Value::ValueType::INT; - v.intvalue = Token::getStrLength(v.tokvalue); - v.tokvalue = nullptr; - return v; - } - } + std::vector tokArgs = getArguments(expr); + std::vector args(tokArgs.size()); + std::transform(tokArgs.begin(), tokArgs.end(), args.begin(), [&](const Token* tok) { + return execute(tok, pm, settings); + }); + BuiltinLibraryFunction lf = getBuiltinLibraryFunction(ftok->str()); + if (lf) { + return lf(args); } else { const std::string& returnValue = settings->library.returnValue(ftok); - if (!returnValue.empty()) - return evaluateLibraryFunction(args, returnValue, settings); + if (!returnValue.empty()) { + std::unordered_map arg_map; + int argn = 0; + for (const ValueFlow::Value& result : args) { + if (!result.isUninitValue()) + arg_map[argn] = result; + argn++; + } + return evaluateLibraryFunction(arg_map, returnValue, settings); + } } } // Check if functon modifies argument diff --git a/test/cfg/std.cpp b/test/cfg/std.cpp index b2f793ae4..422dd5cac 100644 --- a/test/cfg/std.cpp +++ b/test/cfg/std.cpp @@ -36,6 +36,41 @@ #include #include +int zerodiv_sqrt() +{ + int i = std::sqrt(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_sin() +{ + int i = std::sin(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int moduloofone_cos() +{ + int i = std::cos(0); + // cppcheck-suppress moduloofone + return 42 % i; +} + +int moduloofone_exp() +{ + int i = std::exp(0); + // cppcheck-suppress moduloofone + return 42 % i; +} + +int moduloofone_pow() +{ + int i = std::pow(2, 0); + // cppcheck-suppress moduloofone + return 42 % i; +} + char* invalidFunctionArgStr_strncpy(char * destination) { // Copies the first num characters of source to destination.