* #4241: Check for address of single character passed as string Add a check that address of a single character is not passed as argument to argument marked as strings (using strz). The check does not warn if the address of a character with known value '\0'. Since ValueFlow currently does not handle global constants (see #7597), do not warn if the variable is global to avoid FPs when the address of a global variable assigned to '\0' is passed to a function expecting a string. Remove comment in docs saying strz is unused. * Change asdf to Hello world * Add test of address to first element in string * Add error reporting function to getErrorMessages * Fix strings in test
This commit is contained in:
parent
0a30768b59
commit
613dc19b68
|
@ -125,6 +125,17 @@ void CheckFunctions::invalidFunctionUsage()
|
||||||
else if (!mSettings->library.isIntArgValid(functionToken, argnr, 1))
|
else if (!mSettings->library.isIntArgValid(functionToken, argnr, 1))
|
||||||
invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr));
|
invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mSettings->library.isargstrz(functionToken, argnr)) {
|
||||||
|
if (Token::Match(argtok, "& %var%") && argtok->next() && argtok->next()->valueType()) {
|
||||||
|
const ValueType * valueType = argtok->next()->valueType();
|
||||||
|
const Variable * variable = argtok->next()->variable();
|
||||||
|
if (valueType->type == ValueType::Type::CHAR && !variable->isGlobal() &&
|
||||||
|
(!argtok->next()->hasKnownValue() || argtok->next()->getValue(0) == nullptr)) {
|
||||||
|
invalidFunctionArgStrError(argtok, functionToken->str(), argnr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,6 +178,14 @@ void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::st
|
||||||
reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, false);
|
reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, unsigned int argnr)
|
||||||
|
{
|
||||||
|
std::ostringstream errmsg;
|
||||||
|
errmsg << "$symbol:" << functionName << '\n';
|
||||||
|
errmsg << "Invalid $symbol() argument nr " << argnr << ". A nul-terminated string is required.";
|
||||||
|
reportError(tok, Severity::error, "invalidFunctionArgStr", errmsg.str(), CWE628, false);
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Check for ignored return values.
|
// Check for ignored return values.
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
|
@ -110,6 +110,7 @@ public:
|
||||||
private:
|
private:
|
||||||
void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr);
|
void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr);
|
||||||
void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr);
|
void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr);
|
||||||
|
void invalidFunctionArgStrError(const Token *tok, const std::string &functionName, unsigned int argnr);
|
||||||
void ignoredReturnValueError(const Token* tok, const std::string& function);
|
void ignoredReturnValueError(const Token* tok, const std::string& function);
|
||||||
void mathfunctionCallWarning(const Token *tok, const unsigned int numParam = 1);
|
void mathfunctionCallWarning(const Token *tok, const unsigned int numParam = 1);
|
||||||
void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp);
|
void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp);
|
||||||
|
@ -126,6 +127,7 @@ private:
|
||||||
|
|
||||||
c.invalidFunctionArgError(nullptr, "func_name", 1, nullptr,"1:4");
|
c.invalidFunctionArgError(nullptr, "func_name", 1, nullptr,"1:4");
|
||||||
c.invalidFunctionArgBoolError(nullptr, "func_name", 1);
|
c.invalidFunctionArgBoolError(nullptr, "func_name", 1);
|
||||||
|
c.invalidFunctionArgStrError(nullptr, "func_name", 1);
|
||||||
c.ignoredReturnValueError(nullptr, "malloc");
|
c.ignoredReturnValueError(nullptr, "malloc");
|
||||||
c.mathfunctionCallWarning(nullptr);
|
c.mathfunctionCallWarning(nullptr);
|
||||||
c.mathfunctionCallWarning(nullptr, "1 - erf(x)", "erfc(x)");
|
c.mathfunctionCallWarning(nullptr, "1 - erf(x)", "erfc(x)");
|
||||||
|
|
|
@ -1586,8 +1586,7 @@ Checking minsize.c...
|
||||||
<section>
|
<section>
|
||||||
<title>strz</title>
|
<title>strz</title>
|
||||||
|
|
||||||
<para>This setting is not used by Cppcheck currently. But with this
|
<para>With this you can say that an argument must be a zero-terminated
|
||||||
you can say that an argument must be a zero-terminated
|
|
||||||
string.</para>
|
string.</para>
|
||||||
|
|
||||||
<para><programlisting><?xml version="1.0"?>
|
<para><programlisting><?xml version="1.0"?>
|
||||||
|
|
|
@ -3797,6 +3797,24 @@ void invalidFunctionArg(char c)
|
||||||
(void)toupper(255);
|
(void)toupper(255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void invalidFunctionArgString(char c)
|
||||||
|
{
|
||||||
|
/* cppcheck-suppress invalidFunctionArgStr */
|
||||||
|
(void)atoi(&c);
|
||||||
|
char x = 'x';
|
||||||
|
/* cppcheck-suppress invalidFunctionArgStr */
|
||||||
|
(void)strlen(&x);
|
||||||
|
|
||||||
|
char y = '\0';
|
||||||
|
(void)strlen(&y);
|
||||||
|
|
||||||
|
// #5225
|
||||||
|
char str[80] = "hello worl";
|
||||||
|
char d='d';
|
||||||
|
/* cppcheck-suppress invalidFunctionArgStr */
|
||||||
|
(void)strcat(str,&d);
|
||||||
|
}
|
||||||
|
|
||||||
void ignoredReturnValue_abs(int i)
|
void ignoredReturnValue_abs(int i)
|
||||||
{
|
{
|
||||||
// cppcheck-suppress ignoredReturnValue
|
// cppcheck-suppress ignoredReturnValue
|
||||||
|
|
|
@ -62,6 +62,7 @@ private:
|
||||||
|
|
||||||
// Invalid function usage
|
// Invalid function usage
|
||||||
TEST_CASE(invalidFunctionUsage1);
|
TEST_CASE(invalidFunctionUsage1);
|
||||||
|
TEST_CASE(invalidFunctionUsageStrings);
|
||||||
|
|
||||||
// Math function usage
|
// Math function usage
|
||||||
TEST_CASE(mathfunctionCall_fmod);
|
TEST_CASE(mathfunctionCall_fmod);
|
||||||
|
@ -447,6 +448,97 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void invalidFunctionUsageStrings() {
|
||||||
|
check("size_t f() { char x = 'x'; return strlen(&x); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("size_t f() { return strlen(&x); }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("size_t f(char x) { return strlen(&x); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("size_t f() { char x = '\\0'; return strlen(&x); }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("size_t f() {\n"
|
||||||
|
" char x;\n"
|
||||||
|
" if (y)\n"
|
||||||
|
" x = '\\0';\n"
|
||||||
|
" else\n"
|
||||||
|
" x = 'a';\n"
|
||||||
|
" return strlen(&x);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("int f() { char x = '\\0'; return strcmp(\"Hello world\", &x); }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("int f() { char x = 'x'; return strcmp(\"Hello world\", &x); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("size_t f(char x) { char * y = &x; return strlen(y) }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("size_t f(char x) { char * y = &x; char *z = y; return strlen(z) }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("size_t f() { char x = 'x'; char * y = &x; char *z = y; return strlen(z) }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("size_t f() { char x = '\\0'; char * y = &x; char *z = y; return strlen(z) }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("size_t f() { char x[] = \"Hello world\"; return strlen(x) }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("size_t f(char x[]) { return strlen(x) }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("int f(char x, char y) { return strcmp(&x, &y); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 1. A nul-terminated string is required.\n"
|
||||||
|
"[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("size_t f() { char x[] = \"Hello world\"; return strlen(&x[0]) }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct S {\n"
|
||||||
|
" char x;\n"
|
||||||
|
"};\n"
|
||||||
|
"size_t f() {\n"
|
||||||
|
" S s1 = {0};\n"
|
||||||
|
" S s2;\n;"
|
||||||
|
" s2.x = 'x';\n"
|
||||||
|
" size_t l1 = strlen(&s1.x);\n"
|
||||||
|
" size_t l2 = strlen(&s2.x);\n"
|
||||||
|
" return l1 + l2;\n"
|
||||||
|
"}\n");
|
||||||
|
TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str());
|
||||||
|
|
||||||
|
check("const char x = 'x'; size_t f() { return strlen(&x); }");
|
||||||
|
TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout.str());
|
||||||
|
|
||||||
|
check("const char x = 'x'; size_t f() { char y = x; return strlen(&y); }");
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout.str());
|
||||||
|
|
||||||
|
check("const char x = '\\0'; size_t f() { return strlen(&x); }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("const char x = '\\0'; size_t f() { char y = x; return strlen(&y); }");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// #5225
|
||||||
|
check("int main(void)\n"
|
||||||
|
"{\n"
|
||||||
|
" char str[80] = \"hello worl\";\n"
|
||||||
|
" char d = 'd';\n"
|
||||||
|
" strcat(str, &d);\n"
|
||||||
|
" puts(str);\n"
|
||||||
|
" return 0;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:5]: (error) Invalid strcat() argument nr 2. A nul-terminated string is required.\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void mathfunctionCall_sqrt() {
|
void mathfunctionCall_sqrt() {
|
||||||
// sqrt, sqrtf, sqrtl
|
// sqrt, sqrtf, sqrtl
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
|
|
Loading…
Reference in New Issue