diff --git a/lib/checktype.cpp b/lib/checktype.cpp index 01c371586..658a16ee5 100644 --- a/lib/checktype.cpp +++ b/lib/checktype.cpp @@ -302,3 +302,72 @@ void CheckType::longCastReturnError(const Token *tok) "int result is returned as long value. If the return value is long to avoid loss of information, then you have loss of information.\n" "int result is returned as long value. If the return value is long to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'return a*b;' => 'return (long)a*b'.", CWE197, false); } + +//--------------------------------------------------------------------------- +// Checking for float to integer overflow +//--------------------------------------------------------------------------- + +void CheckType::checkFloatToIntegerOverflow() +{ + const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); + const std::size_t functions = symbolDatabase->functionScopes.size(); + for (std::size_t i = 0; i < functions; ++i) { + const Scope * scope = symbolDatabase->functionScopes[i]; + for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { + if (tok->str() != "(") + continue; + + if (!tok->astOperand1() || tok->astOperand2()) + continue; + + // is result integer? + const ValueType *vt = tok->valueType(); + if (!vt || !vt->isIntegral()) + continue; + + // is value float? + const ValueType *vt1 = tok->astOperand1()->valueType(); + if (!vt1 || !vt1->isFloat()) + continue; + + const Token *op1 = tok->astOperand1(); + for (std::list::const_iterator it = op1->values.begin(); it != op1->values.end(); ++it) { + if (it->valueType != ValueFlow::Value::FLOAT) + continue; + if (it->inconclusive && !_settings->inconclusive) + continue; + if (it->floatValue > ~0ULL) + floatToIntegerOverflowError(tok, *it); + else if ((-it->floatValue) > (1ULL<<62)) + floatToIntegerOverflowError(tok, *it); + else if (_settings->platformType != Settings::Unspecified) { + int bits = 0; + if (vt->type == ValueType::Type::CHAR) + bits = _settings->char_bit; + else if (vt->type == ValueType::Type::SHORT) + bits = _settings->short_bit; + else if (vt->type == ValueType::Type::INT) + bits = _settings->int_bit; + else if (vt->type == ValueType::Type::LONG) + bits = _settings->long_bit; + else if (vt->type == ValueType::Type::LONGLONG) + bits = _settings->long_long_bit; + else + continue; + if (bits < 64 && it->floatValue > (1 << (bits - 1))) + floatToIntegerOverflowError(tok, *it); + } + } + } + } +} + +void CheckType::floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value) +{ + std::ostringstream errmsg; + errmsg << "Undefined behaviour: float (" << value.floatValue << ") conversion overflow."; + reportError(tok, + Severity::error, + "floatConversionOverflow", + errmsg.str(), CWE190, value.inconclusive); +} diff --git a/lib/checktype.h b/lib/checktype.h index fa5c0b629..3e5e50c53 100644 --- a/lib/checktype.h +++ b/lib/checktype.h @@ -50,6 +50,7 @@ public: checkType.checkIntegerOverflow(); checkType.checkSignConversion(); checkType.checkLongCast(); + checkType.checkFloatToIntegerOverflow(); } /** @brief Run checks against the simplified token list */ @@ -70,6 +71,9 @@ public: /** @brief %Check for implicit long cast of int result */ void checkLongCast(); + + /** @brief %Check for float to integer overflow */ + void checkFloatToIntegerOverflow(); private: // Error messages.. @@ -78,6 +82,7 @@ private: void signConversionError(const Token *tok, const bool constvalue); void longCastAssignError(const Token *tok); void longCastReturnError(const Token *tok); + void floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value); void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { CheckType c(nullptr, settings, errorLogger); @@ -86,6 +91,10 @@ private: c.signConversionError(nullptr, false); c.longCastAssignError(nullptr); c.longCastReturnError(nullptr); + ValueFlow::Value f; + f.valueType = ValueFlow::Value::FLOAT; + f.floatValue = 1E100; + c.floatToIntegerOverflowError(nullptr, f); } static std::string myName() { @@ -98,7 +107,8 @@ private: "- signed integer overflow (only enabled when --platform is used)\n" "- dangerous sign conversion, when signed value can be negative\n" "- possible loss of information when assigning int result to long variable\n" - "- possible loss of information when returning int result as long return value\n"; + "- possible loss of information when returning int result as long return value\n" + "- float conversion overflow\n"; } }; /// @} diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index a7371fb1b..354c46da9 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1157,6 +1157,10 @@ public: return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT); } + bool isFloat() const { + return (type == ValueType::Type::FLOAT || type == ValueType::Type::DOUBLE); + } + bool fromLibraryType(const std::string &typestr, const Settings *settings); std::string str() const; diff --git a/test/testtype.cpp b/test/testtype.cpp index 997fdf1b5..8e2652467 100644 --- a/test/testtype.cpp +++ b/test/testtype.cpp @@ -39,6 +39,7 @@ private: TEST_CASE(signConversion); TEST_CASE(longCastAssign); TEST_CASE(longCastReturn); + TEST_CASE(checkFloatToIntegerOverflow); } void check(const char code[], Settings* settings = 0) { @@ -211,6 +212,33 @@ private: "}\n", &settings); ASSERT_EQUALS("", errout.str()); } + + // This function ensure that test works with different compilers. Floats can + // be stringified differently. + std::string removeFloat(std::string errmsg) { + std::string::size_type pos1 = errmsg.find("float ("); + std::string::size_type pos2 = errmsg.find(") conversion"); + if (pos1 == std::string::npos || pos2 == std::string::npos || pos1 > pos2) + return errmsg; + return errmsg.substr(0,pos1+7) + errmsg.substr(pos2); + } + + void checkFloatToIntegerOverflow() { + check("void f(void) {\n" + " return (int)1E100;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Undefined behaviour: float () conversion overflow.\n", removeFloat(errout.str())); + + check("void f(void) {\n" + " return (int)-1E100;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Undefined behaviour: float () conversion overflow.\n", removeFloat(errout.str())); + + check("void f(void) {\n" + " return (short)1E6;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Undefined behaviour: float () conversion overflow.\n", removeFloat(errout.str())); + } }; REGISTER_TEST(TestType)