Fix severity of c++14 shifts with too many bits (#2213)

For c++14, shifting a variable with a value larger than or equal to the
number of bits in the variable is undefined. Left-shifting with a value
equal to the number of bits of the variable is implementation defined.

See also trac ticket #9306.
This commit is contained in:
Rikard Falkeborn 2019-09-25 20:16:04 +02:00 committed by Daniel Marjamäki
parent 9e76630a4b
commit 4dbf006dc7
2 changed files with 25 additions and 8 deletions

View File

@ -57,11 +57,6 @@ void CheckType::checkTooBigBitwiseShift()
if (mSettings->platformType == Settings::Unspecified)
return;
const bool cpp14 = mSettings->standards.cpp >= Standards::CPP14;
if (cpp14 && !mSettings->isEnabled(Settings::PORTABILITY))
return;
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
// C++ and macro: OUT(x<<y)
if (mTokenizer->isCPP() && Token::Match(tok, "[;{}] %name% (") && Token::simpleMatch(tok->linkAt(2), ") ;") && tok->next()->isUpperCaseName() && !tok->next()->function())
@ -96,7 +91,7 @@ void CheckType::checkTooBigBitwiseShift()
// Get biggest rhs value. preferably a value which doesn't have 'condition'.
const ValueFlow::Value * value = tok->astOperand2()->getValueGE(lhsbits, mSettings);
if (value && mSettings->isEnabled(value, false) && !cpp14)
if (value && mSettings->isEnabled(value, false))
tooBigBitwiseShiftError(tok, lhsbits, *value);
else if (lhstype->sign == ValueType::Sign::SIGNED) {
value = tok->astOperand2()->getValueGE(lhsbits-1, mSettings);
@ -150,6 +145,8 @@ void CheckType::tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, con
if (cpp14)
severity = Severity::portability;
if ((severity == Severity::portability) && !mSettings->isEnabled(Settings::PORTABILITY))
return;
reportError(errorPath, severity, id, errmsg.str(), CWE758, rhsbits.isInconclusive());
}

View File

@ -41,7 +41,7 @@ private:
TEST_CASE(checkFloatToIntegerOverflow);
}
void check(const char code[], Settings* settings = nullptr, const char filename[] = "test.cpp") {
void check(const char code[], Settings* settings = nullptr, const char filename[] = "test.cpp", const std::string& standard = "c++11") {
// Clear the error buffer..
errout.str("");
@ -50,7 +50,8 @@ private:
settings = &_settings;
}
settings->addEnabled("warning");
settings->standards.setCPP("c++11");
settings->addEnabled("portability");
settings->standards.setCPP(standard);
// Tokenize..
Tokenizer tokenizer(settings, this);
@ -71,6 +72,8 @@ private:
{
const std::string types[] = {"unsigned char", /*[unsigned]*/"char", "bool", "unsigned short", "unsigned int", "unsigned long"};
for (const std::string& type : types) {
check((type + " f(" + type +" x) { return x << 31; }").c_str(), &settings);
ASSERT_EQUALS("", errout.str());
check((type + " f(" + type +" x) { return x << 33; }").c_str(), &settings);
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 32-bit value by 33 bits is undefined behaviour\n", errout.str());
check((type + " f(int x) { return (x = (" + type + ")x << 32); }").c_str(), &settings);
@ -84,6 +87,7 @@ private:
{
const std::string types[] = {"signed char", "signed short", /*[signed]*/"short", "wchar_t", /*[signed]*/"int", "signed int", /*[signed]*/"long", "signed long"};
for (const std::string& type : types) {
// c++11
check((type + " f(" + type +" x) { return x << 33; }").c_str(), &settings);
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 32-bit value by 33 bits is undefined behaviour\n", errout.str());
check((type + " f(int x) { return (x = (" + type + ")x << 32); }").c_str(), &settings);
@ -92,6 +96,12 @@ private:
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour\n", errout.str());
check((type + " foo(" + type + " x) { return x << 30; }").c_str(), &settings);
ASSERT_EQUALS("", errout.str());
// c++14
check((type + " foo(" + type + " x) { return x << 31; }").c_str(), &settings, "test.cpp", "c++14");
ASSERT_EQUALS("[test.cpp:1]: (portability) Shifting signed 32-bit value by 31 bits is implementation-defined behaviour\n", errout.str());
check((type + " f(int x) { return (x = (" + type + ")x << 32); }").c_str(), &settings, "test.cpp", "c++14");
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 32-bit value by 32 bits is undefined behaviour\n", errout.str());
}
}
// 64 bit width types
@ -121,6 +131,16 @@ private:
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting signed 64-bit value by 63 bits is undefined behaviour\n", errout.str());
check("signed long long f(signed long long x) { return x << 62; }",&settings);
ASSERT_EQUALS("", errout.str());
// c++14
check("signed long long foo(signed long long x) { return x << 64; }",&settings, "test.cpp", "c++14");
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 64-bit value by 64 bits is undefined behaviour\n", errout.str());
check("signed long long f(long long x) { return (x = (signed long long)x << 64); }",&settings, "test.cpp", "c++14");
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 64-bit value by 64 bits is undefined behaviour\n", errout.str());
check("signed long long f(signed long long x) { return x << 63; }",&settings, "test.cpp", "c++14");
ASSERT_EQUALS("[test.cpp:1]: (portability) Shifting signed 64-bit value by 63 bits is implementation-defined behaviour\n", errout.str());
check("signed long long f(signed long long x) { return x << 62; }",&settings);
ASSERT_EQUALS("", errout.str());
}
check("void foo() {\n"