Add a check for float conversion overflows
This commit is contained in:
parent
8870195794
commit
0b3751520b
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
};
|
||||
/// @}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue