61 if (!tok->variable() || !tok->variable()->isPointer())
76 std::list<const Token*> callstack{ tok };
78 callstack.push_back(strValue);
80 std::string errmsg(
"Modifying string literal");
82 std::string s = strValue->
str();
85 s.replace(17, std::string::npos,
"..\"");
88 errmsg +=
" directly or indirectly is undefined behaviour.";
102 logChecker(
"CheckString::checkAlwaysTrueOrFalseStringCompare");
105 if (tok->isName() && tok->strAt(1) ==
"(" &&
Token::Match(tok,
"memcmp|strncmp|strcmp|stricmp|strverscmp|bcmp|strcmpi|strcasecmp|strncasecmp|strncasecmp_l|strcasecmp_l|wcsncasecmp|wcscasecmp|wmemcmp|wcscmp|wcscasecmp_l|wcsncasecmp_l|wcsncmp|_mbscmp|_mbscmp_l|_memicmp|_memicmp_l|_stricmp|_wcsicmp|_mbsicmp|_stricmp_l|_wcsicmp_l|_mbsicmp_l")) {
107 const std::string &str1 = tok->strAt(2);
108 const std::string &str2 = tok->strAt(4);
109 if (!tok->isExpandedMacro() && !tok->tokAt(2)->isExpandedMacro() && !tok->tokAt(4)->isExpandedMacro())
112 }
else if (
Token::Match(tok->tokAt(2),
"%name% , %name% ,|)")) {
113 const std::string &str1 = tok->strAt(2);
114 const std::string &str2 = tok->strAt(4);
118 }
else if (
Token::Match(tok->tokAt(2),
"%name% . c_str ( ) , %name% . c_str ( ) ,|)")) {
119 const std::string &str1 = tok->strAt(2);
120 const std::string &str2 = tok->strAt(8);
123 tok = tok->tokAt(13);
125 }
else if (tok->isName() &&
Token::Match(tok,
"QString :: compare ( %str% , %str% )")) {
126 const std::string &str1 = tok->strAt(4);
127 const std::string &str2 = tok->strAt(6);
130 }
else if (
Token::Match(tok,
"!!+ %str% ==|!= %str% !!+")) {
131 const std::string &str1 = tok->strAt(1);
132 const std::string &str2 = tok->strAt(3);
143 constexpr std::size_t stringLen = 10;
144 const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) +
"..");
145 const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) +
"..");
148 "Unnecessary comparison of static strings.\n"
149 "The compared strings, '" + string1 +
"' and '" + string2 +
"', are always " + (str1==str2?
"identical":
"unequal") +
". "
156 "Comparison of identical string variables.\n"
157 "The compared strings, '" + str1 +
"' and '" + str2 +
"', are identical. "
171 logChecker(
"CheckString::checkSuspiciousStringCompare");
176 if (!tok->isComparisonOp())
181 if (!varTok || !litTok)
184 std::swap(varTok, litTok);
196 if (varTok->
isC() || (varType && varType->
pointer))
207 const std::string cmpFunc = isLong ?
"wcscmp" :
"strcmp";
209 "$symbol:" + var +
"\nString literal compared with variable '$symbol'. Did you intend to use " + cmpFunc +
"() instead?",
CWE595,
Certainty::normal);
215 "$symbol:" + var +
"\nChar literal compared with pointer '$symbol'. Did you intend to dereference it?",
CWE595,
Certainty::normal);
234 if (tok->str() ==
"+") {
235 if (tok->astOperand1() && (tok->astOperand1()->tokType() ==
Token::eString)) {
236 if (tok->astOperand2() && (tok->astOperand2()->tokType() ==
Token::eChar ||
isChar(tok->astOperand2()->variable())))
246 std::string charType =
"char";
248 charType = tok->
astOperand2()->variable()->typeStartToken()->str();
250 charType =
"wchar_t";
257 while (parent && parent->isCast())
258 parent = parent->astParent();
261 if (parent->isExpandedMacro())
263 if (parent->isUnaryOp(
"!") || parent->isComparisonOp()) {
282 logChecker(
"CheckString::checkIncorrectStringCompare");
291 tok = tok->next()->link();
298 begin = begin->
link()->previous();
300 begin = begin->
tokAt(-2);
333 const std::string literalType = charLiteral ?
"char" :
"string";
337 charLiteral ?
"incorrectCharBooleanError" :
"incorrectStringBooleanError",
338 "Conversion of " + literalType +
" literal " +
string +
" to bool always evaluates to " + result +
'.',
CWE571,
Certainty::normal);
355 if (tok->str() !=
"||")
357 std::list<const Token *> equals0;
358 std::list<const Token *> notEquals0;
362 if (t->
str() ==
"||") {
363 return ChildrenToVisit::op1_and_op2;
365 if (t->
str() ==
"==") {
366 if (Token::simpleMatch(t->astOperand1(),
"(") && Token::simpleMatch(t->astOperand2(),
"0"))
367 equals0.push_back(t->astOperand1());
368 else if (Token::simpleMatch(t->astOperand2(),
"(") && Token::simpleMatch(t->astOperand1(),
"0"))
369 equals0.push_back(t->astOperand2());
370 return ChildrenToVisit::none;
372 if (t->
str() ==
"!=") {
373 if (Token::simpleMatch(t->astOperand1(),
"(") && Token::simpleMatch(t->astOperand2(),
"0"))
374 notEquals0.push_back(t->astOperand1());
375 else if (Token::simpleMatch(t->astOperand2(),
"(") && Token::simpleMatch(t->astOperand1(),
"0"))
376 notEquals0.push_back(t->astOperand2());
377 return ChildrenToVisit::none;
381 else if (t->
str() ==
"(")
382 notEquals0.push_back(t);
386 for (
const Token *eq0 : equals0) {
387 for (
const Token * ne0 : notEquals0) {
392 const std::vector<const Token *> args1 =
getArguments(eq0->previous());
393 const std::vector<const Token *> args2 =
getArguments(ne0->previous());
394 if (args1.size() != 2 || args2.size() != 2)
396 if (args1[1]->isLiteral() &&
397 args2[1]->isLiteral() &&
398 args1[1]->str() != args2[1]->str() &&
409 std::string eq0Expr(eq0 ? eq0->
expressionString() : std::string(
"strcmp(x,\"abc\")"));
410 if (eq0 && eq0->
astParent()->str() ==
"!")
411 eq0Expr =
"!" + eq0Expr;
415 const std::string ne0Expr = (ne0 ? ne0->
expressionString() : std::string(
"strcmp(x,\"def\")")) +
" != 0";
417 reportError(ne0,
Severity::warning,
"overlappingStrcmp",
"The expression '" + ne0Expr +
"' is suspicious. It overlaps '" + eq0Expr +
"'.");
426 logChecker(
"CheckString::sprintfOverlappingData");
434 const std::vector<const Token *> args =
getArguments(tok);
437 for (
unsigned int argnr = formatString + 1; argnr < args.size(); ++argnr) {
438 const Token *dest = args[0];
441 const Token *arg = args[argnr];
463 const std::string func = funcTok ? funcTok->
str() :
"s[n]printf";
466 "$symbol:" + varname +
"\n"
467 "Undefined behavior: Variable '$symbol' is used as parameter and destination in " + func +
"().\n" +
468 "The variable '$symbol' is used both as a parameter and as destination in " +
469 func +
"(). The origin and destination buffers overlap. Quote from glibc (C-library) "
470 "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): "
471 "\"If copying takes place between objects that overlap as a result of a call "
std::vector< const Token * > getArguments(const Token *ftok)
Get arguments (AST)
bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Settings &settings, bool pure, bool followVar, ErrorPath *errors)
const Token * getTokenArgumentFunction(const Token *tok, int &argn)
Return the token to the function and the argument number.
bool isUsedAsBool(const Token *const tok, const Settings &settings)
Is token used as boolean, that is to say cast to a bool, or used as a condition in a if/while/for.
void visitAstNodes(T *ast, const TFunc &visitor)
Visit AST nodes recursively.
static const CWE CWE571(571U)
static bool isMacroUsage(const Token *tok)
static const CWE CWE595(595U)
static const CWE CWE758(758U)
static const CWE CWE570(570U)
static const CWE CWE665(665U)
static bool isChar(const Variable *var)
static const CWE CWE628(628U)
Detect misusage of C-style strings and related standard functions.
void checkSuspiciousStringCompare()
Check for comparison of a string literal with a char* variable
void checkIncorrectStringCompare()
Check for using bad usage of strncmp and substr
void suspiciousStringCompareError(const Token *tok, const std::string &var, bool isLong)
void incorrectStringBooleanError(const Token *tok, const std::string &string)
void overlappingStrcmp()
Check for overlapping strcmp()
void incorrectStringCompareError(const Token *tok, const std::string &func, const std::string &string)
void strPlusCharError(const Token *tok)
void suspiciousStringCompareError_char(const Token *tok, const std::string &var)
void sprintfOverlappingData()
Check for overlapping source and destination passed to sprintf()
void stringLiteralWriteError(const Token *tok, const Token *strValue)
void alwaysTrueStringVariableCompareError(const Token *tok, const std::string &str1, const std::string &str2)
void overlappingStrcmpError(const Token *eq0, const Token *ne0)
void stringLiteralWrite()
undefined behaviour, writing string literal
void strPlusChar()
str plus char (unusual pointer arithmetic)
void alwaysTrueFalseStringCompareError(const Token *tok, const std::string &str1, const std::string &str2)
void sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname)
void checkAlwaysTrueOrFalseStringCompare()
Check for suspicious code that compares string literals for equality
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
const Settings *const mSettings
const Tokenizer *const mTokenizer
void logChecker(const char id[])
log checker
static biguint toBigUNumber(const std::string &str)
for conversion of numeric literals - for atoi-like conversions please use strToInt()
unsigned long long biguint
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
SimpleEnableGroup< Severity > severity
bool isEnabled(T flag) const
std::vector< const Scope * > functionScopes
Fast access to function scopes.
The token list that the TokenList generates is a linked-list of this class.
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
const Token * getValueTokenMinStrSize(const Settings &settings, MathLib::bigint *path=nullptr) const
static nonneg int getStrLength(const Token *tok)
const ValueType * valueType() const
const std::string & strAt(int index) const
void astOperand1(Token *tok)
void function(const Function *f)
Associate this token with given function.
std::string expressionString() const
const Token * tokAt(int index) const
Token::Type tokType() const
void astOperand2(Token *tok)
void link(Token *linkToToken)
Create link to given token.
const Token * linkAt(int index) const
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
void astParent(Token *tok)
const Token * tokens() const
const SymbolDatabase * getSymbolDatabase() const
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
Information about a member variable.
bool isArray() const
Is variable an array.
const Token * typeStartToken() const
Get type start token.
bool isPointer() const
Is pointer variable.
@ error
Programming error.
bool endsWith(const std::string &str, char c)
static bool isCharLiteral(const std::string &str)
static const char * bool_to_string(bool b)
static std::string getCharLiteral(const std::string &str)