Fixed #2866 (Detect sign extension bugs)
This commit is contained in:
parent
657b003dc8
commit
8cd2c3115e
|
@ -2761,15 +2761,22 @@ void CheckOther::checkCharVariable()
|
||||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||||
{
|
{
|
||||||
// Declaring the variable..
|
// Declaring the variable..
|
||||||
if (Token::Match(tok, "[{};(,] char %var% [;=,)]"))
|
if (Token::Match(tok, "[{};(,] const| char *| %var% [;=,)]") ||
|
||||||
|
Token::Match(tok, "[{};(,] const| char %var% ["))
|
||||||
{
|
{
|
||||||
|
// goto 'char' token
|
||||||
|
tok = tok->next();
|
||||||
|
if (tok->str() == "const")
|
||||||
|
tok = tok->next();
|
||||||
|
|
||||||
// Check for unsigned char
|
// Check for unsigned char
|
||||||
if (tok->tokAt(1)->isUnsigned())
|
if (tok->isUnsigned())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Set tok to point to the variable name
|
// Set tok to point to the variable name
|
||||||
tok = tok->tokAt(2);
|
tok = tok->next();
|
||||||
if (tok->str() == "char")
|
const bool isPointer(tok->str() == "*" || tok->strAt(1) == "[");
|
||||||
|
if (tok->str() == "*")
|
||||||
tok = tok->next();
|
tok = tok->next();
|
||||||
|
|
||||||
// Check usage of char variable..
|
// Check usage of char variable..
|
||||||
|
@ -2786,9 +2793,6 @@ void CheckOther::checkCharVariable()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (tok2->str() == "return")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::string temp = "%var% [ " + tok->str() + " ]";
|
std::string temp = "%var% [ " + tok->str() + " ]";
|
||||||
if ((tok2->str() != ".") && Token::Match(tok2->next(), temp.c_str()))
|
if ((tok2->str() != ".") && Token::Match(tok2->next(), temp.c_str()))
|
||||||
{
|
{
|
||||||
|
@ -2803,7 +2807,7 @@ void CheckOther::checkCharVariable()
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// it's ok with a bitwise and where the other operand is 0xff or less..
|
// it's ok with a bitwise and where the other operand is 0xff or less..
|
||||||
if (std::string(tok2->strAt(4)) == "&")
|
if (tok2->strAt(4) == "&")
|
||||||
{
|
{
|
||||||
if (tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3)))
|
if (tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3)))
|
||||||
continue;
|
continue;
|
||||||
|
@ -2819,6 +2823,21 @@ void CheckOther::checkCharVariable()
|
||||||
charBitOpError(tok2);
|
charBitOpError(tok2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isPointer && Token::Match(tok2, "[;{}] %var% = %any% [&|] ( * %varid% ) ;", tok->varId()))
|
||||||
|
{
|
||||||
|
// it's ok with a bitwise and where the other operand is 0xff or less..
|
||||||
|
if (tok2->strAt(4) == "&" && tok2->tokAt(3)->isNumber() && MathLib::isGreater("0x100", tok2->strAt(3)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// is the result stored in a short|int|long?
|
||||||
|
if (!Token::findmatch(_tokenizer->tokens(), "short|int|long %varid%", tok2->next()->varId()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// This is an error..
|
||||||
|
charBitOpError(tok2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3477,12 +3496,30 @@ void CheckOther::constStatementError(const Token *tok, const std::string &type)
|
||||||
|
|
||||||
void CheckOther::charArrayIndexError(const Token *tok)
|
void CheckOther::charArrayIndexError(const Token *tok)
|
||||||
{
|
{
|
||||||
reportError(tok, Severity::warning, "charArrayIndex", "Warning - using char variable as array index");
|
reportError(tok,
|
||||||
|
Severity::warning,
|
||||||
|
"charArrayIndex",
|
||||||
|
"When using a char variable as array index, sign extension will mean buffer overflow.\n"
|
||||||
|
"When using a char variable as array index, sign extension will cause buffer overflows. For example:\n"
|
||||||
|
" char c = 0x80;\n"
|
||||||
|
" char buf[512];\n"
|
||||||
|
" buf[c] = 13; // buffer overflow, index must be a value between 0 and 511.\n"
|
||||||
|
" printf(\"%i\", buf[0x80]);\n"
|
||||||
|
"There is a big chance that the value that is printed won't be 13.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckOther::charBitOpError(const Token *tok)
|
void CheckOther::charBitOpError(const Token *tok)
|
||||||
{
|
{
|
||||||
reportError(tok, Severity::warning, "charBitOp", "Warning - using char variable in bit operation");
|
reportError(tok,
|
||||||
|
Severity::warning,
|
||||||
|
"charBitOp",
|
||||||
|
"When using char variables in bit operations, sign extension can generate unexpected results.\n"
|
||||||
|
"When using char variables in bit operations, sign extension can generate unexpected results. For example:\n"
|
||||||
|
" char c = 0x80;\n"
|
||||||
|
" int i = 0 | c;\n"
|
||||||
|
" if (i & 0x8000)\n"
|
||||||
|
" printf(\"not expected\");\n"
|
||||||
|
"The 'not expected' will be printed on the screen.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckOther::variableScopeError(const Token *tok, const std::string &varname)
|
void CheckOther::variableScopeError(const Token *tok, const std::string &varname)
|
||||||
|
|
|
@ -41,6 +41,7 @@ private:
|
||||||
TEST_CASE(return1);
|
TEST_CASE(return1);
|
||||||
TEST_CASE(assignChar);
|
TEST_CASE(assignChar);
|
||||||
TEST_CASE(and03);
|
TEST_CASE(and03);
|
||||||
|
TEST_CASE(pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[])
|
void check(const char code[])
|
||||||
|
@ -76,20 +77,20 @@ private:
|
||||||
" char ch = 0x80;\n"
|
" char ch = 0x80;\n"
|
||||||
" buf[ch] = 0;\n"
|
" buf[ch] = 0;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (warning) Warning - using char variable as array index\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (warning) When using a char variable as array index, sign extension will mean buffer overflow.\n", errout.str());
|
||||||
|
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" signed char ch = 0x80;\n"
|
" signed char ch = 0x80;\n"
|
||||||
" buf[ch] = 0;\n"
|
" buf[ch] = 0;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (warning) Warning - using char variable as array index\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (warning) When using a char variable as array index, sign extension will mean buffer overflow.\n", errout.str());
|
||||||
|
|
||||||
check("void foo(char ch)\n"
|
check("void foo(char ch)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" buf[ch] = 0;\n"
|
" buf[ch] = 0;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (warning) Warning - using char variable as array index\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (warning) When using a char variable as array index, sign extension will mean buffer overflow.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,7 +102,7 @@ private:
|
||||||
" char ch;\n"
|
" char ch;\n"
|
||||||
" result = a | ch;\n"
|
" result = a | ch;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (warning) Warning - using char variable in bit operation\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (warning) When using char variables in bit operations, sign extension can generate unexpected results.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void bitop2()
|
void bitop2()
|
||||||
|
@ -145,6 +146,15 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pointer()
|
||||||
|
{
|
||||||
|
check("void f(char *p) {\n"
|
||||||
|
" int ret = 0;\n"
|
||||||
|
" ret |= *p;\n"
|
||||||
|
" return ret;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning) When using char variables in bit operations, sign extension can generate unexpected results.\n", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestCharVar)
|
REGISTER_TEST(TestCharVar)
|
||||||
|
|
Loading…
Reference in New Issue