Add a check for float conversion overflows

This commit is contained in:
Daniel Marjamäki 2016-11-22 22:37:13 +01:00
parent 8870195794
commit 0b3751520b
4 changed files with 112 additions and 1 deletions

View File

@ -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<ValueFlow::Value>::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);
}

View File

@ -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";
}
};
/// @}

View File

@ -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;

View File

@ -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)