2014-08-27 09:42:09 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-09-04 19:44:27 +02:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2014-08-27 09:42:09 +02:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
# include "checkstring.h"
2017-05-27 04:33:47 +02:00
2017-04-23 10:51:31 +02:00
# include "astutils.h"
2022-01-27 19:03:20 +01:00
# include "errortypes.h"
2017-05-27 04:33:47 +02:00
# include "mathlib.h"
# include "settings.h"
# include "symboldatabase.h"
# include "token.h"
# include "tokenize.h"
2017-04-23 10:17:35 +02:00
# include "utils.h"
2014-08-27 09:42:09 +02:00
2017-05-27 04:33:47 +02:00
# include <cstddef>
# include <list>
# include <vector>
# include <utility>
2014-08-27 09:42:09 +02:00
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace {
CheckString instance ;
}
Mapped error ids stlBoundaries, stlcstr, useAutoPointerContainer, useAutoPointerArray, sprintfOverlappingData, strPlusChar, shiftTooManyBits, integerOverflow, uninitstring, uninitdata, uninitvar, uninitStructMember, deadpointer, va_start_referencePassed, va_end_missing, va_list_usedBeforeStarted, va_start_subsequentCalls to their CWEs.
2016-02-03 13:53:23 +01:00
// CWE ids used:
2023-10-08 09:10:17 +02:00
static const CWE CWE570 ( 570U ) ; // Expression is Always False
static const CWE CWE571 ( 571U ) ; // Expression is Always True
static const CWE CWE595 ( 595U ) ; // Comparison of Object References Instead of Object Contents
static const CWE CWE628 ( 628U ) ; // Function Call with Incorrectly Specified Arguments
static const CWE CWE665 ( 665U ) ; // Improper Initialization
static const CWE CWE758 ( 758U ) ; // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
2014-08-27 09:42:09 +02:00
2015-05-03 10:44:40 +02:00
//---------------------------------------------------------------------------
// Writing string literal is UB
//---------------------------------------------------------------------------
void CheckString : : stringLiteralWrite ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckString::stringLiteralWrite " ) ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-13 16:40:15 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2015-05-10 10:34:13 +02:00
if ( ! tok - > variable ( ) | | ! tok - > variable ( ) - > isPointer ( ) )
continue ;
Set correct type and size of string and char literals (#2275)
* Set correct type and size of string and char literals
Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.
When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.
Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.
Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.
Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).
* Add comment and update string length
2019-10-20 07:11:57 +02:00
const Token * str = tok - > getValueTokenMinStrSize ( mSettings ) ;
2015-05-10 10:34:13 +02:00
if ( ! str )
continue ;
if ( Token : : Match ( tok , " %var% [ " ) & & Token : : simpleMatch ( tok - > linkAt ( 1 ) , " ] = " ) )
2015-06-05 14:34:13 +02:00
stringLiteralWriteError ( tok , str ) ;
2015-05-10 10:34:13 +02:00
else if ( Token : : Match ( tok - > previous ( ) , " * %var% = " ) )
2015-06-05 14:34:13 +02:00
stringLiteralWriteError ( tok , str ) ;
2015-05-10 10:34:13 +02:00
}
2015-05-03 10:44:40 +02:00
}
}
2015-06-05 14:34:13 +02:00
void CheckString : : stringLiteralWriteError ( const Token * tok , const Token * strValue )
2015-05-03 10:44:40 +02:00
{
2023-10-15 14:51:12 +02:00
std : : list < const Token * > callstack { tok } ;
2015-06-05 14:34:13 +02:00
if ( strValue )
callstack . push_back ( strValue ) ;
std : : string errmsg ( " Modifying string literal " ) ;
if ( strValue ) {
Set correct type and size of string and char literals (#2275)
* Set correct type and size of string and char literals
Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.
When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.
Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.
Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.
Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).
* Add comment and update string length
2019-10-20 07:11:57 +02:00
std : : string s = strValue - > str ( ) ;
// 20 is an arbitrary value, the max string length shown in a warning message
if ( s . size ( ) > 20U )
2022-07-11 23:07:37 +02:00
s . replace ( 17 , std : : string : : npos , " .. \" " ) ;
Set correct type and size of string and char literals (#2275)
* Set correct type and size of string and char literals
Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.
When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.
Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.
Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.
Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).
* Add comment and update string length
2019-10-20 07:11:57 +02:00
errmsg + = " " + s ;
2015-06-05 14:34:13 +02:00
}
2015-10-18 20:55:30 +02:00
errmsg + = " directly or indirectly is undefined behaviour. " ;
2015-06-05 14:34:13 +02:00
2021-02-24 22:00:06 +01:00
reportError ( callstack , Severity : : error , " stringLiteralWrite " , errmsg , CWE758 , Certainty : : normal ) ;
2015-05-03 10:44:40 +02:00
}
2014-08-27 09:42:09 +02:00
//---------------------------------------------------------------------------
// Check for string comparison involving two static strings.
// if(strcmp("00FF00","00FF00")==0) // <- statement is always true
//---------------------------------------------------------------------------
void CheckString : : checkAlwaysTrueOrFalseStringCompare ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2014-08-27 09:42:09 +02:00
return ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckString::checkAlwaysTrueOrFalseStringCompare " ) ; // warning
2018-06-16 16:10:28 +02:00
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2022-05-25 13:35:39 +02:00
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 " ) ) {
2015-01-07 08:30:05 +01:00
if ( Token : : Match ( tok - > tokAt ( 2 ) , " %str% , %str% ,|) " ) ) {
2014-08-27 09:42:09 +02:00
const std : : string & str1 = tok - > strAt ( 2 ) ;
const std : : string & str2 = tok - > strAt ( 4 ) ;
2016-01-13 20:32:40 +01:00
if ( ! tok - > isExpandedMacro ( ) & & ! tok - > tokAt ( 2 ) - > isExpandedMacro ( ) & & ! tok - > tokAt ( 4 ) - > isExpandedMacro ( ) )
alwaysTrueFalseStringCompareError ( tok , str1 , str2 ) ;
2014-08-27 09:42:09 +02:00
tok = tok - > tokAt ( 5 ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok - > tokAt ( 2 ) , " %name% , %name% ,|) " ) ) {
2014-08-27 09:42:09 +02:00
const std : : string & str1 = tok - > strAt ( 2 ) ;
const std : : string & str2 = tok - > strAt ( 4 ) ;
if ( str1 = = str2 )
alwaysTrueStringVariableCompareError ( tok , str1 , str2 ) ;
tok = tok - > tokAt ( 5 ) ;
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok - > tokAt ( 2 ) , " %name% . c_str ( ) , %name% . c_str ( ) ,|) " ) ) {
2014-08-27 09:42:09 +02:00
const std : : string & str1 = tok - > strAt ( 2 ) ;
const std : : string & str2 = tok - > strAt ( 8 ) ;
if ( str1 = = str2 )
alwaysTrueStringVariableCompareError ( tok , str1 , str2 ) ;
tok = tok - > tokAt ( 13 ) ;
}
2015-09-07 17:35:15 +02:00
} else if ( tok - > isName ( ) & & Token : : Match ( tok , " QString :: compare ( %str% , %str% ) " ) ) {
2014-08-27 09:42:09 +02:00
const std : : string & str1 = tok - > strAt ( 4 ) ;
const std : : string & str2 = tok - > strAt ( 6 ) ;
alwaysTrueFalseStringCompareError ( tok , str1 , str2 ) ;
tok = tok - > tokAt ( 7 ) ;
} else if ( Token : : Match ( tok , " !!+ %str% ==|!= %str% !!+ " ) ) {
const std : : string & str1 = tok - > strAt ( 1 ) ;
const std : : string & str2 = tok - > strAt ( 3 ) ;
alwaysTrueFalseStringCompareError ( tok , str1 , str2 ) ;
tok = tok - > tokAt ( 5 ) ;
}
2015-05-29 20:20:05 +02:00
if ( ! tok )
break ;
2014-08-27 09:42:09 +02:00
}
}
void CheckString : : alwaysTrueFalseStringCompareError ( const Token * tok , const std : : string & str1 , const std : : string & str2 )
{
2023-11-03 09:55:44 +01:00
constexpr std : : size_t stringLen = 10 ;
2014-08-27 09:42:09 +02:00
const std : : string string1 = ( str1 . size ( ) < stringLen ) ? str1 : ( str1 . substr ( 0 , stringLen - 2 ) + " .. " ) ;
const std : : string string2 = ( str2 . size ( ) < stringLen ) ? str2 : ( str2 . substr ( 0 , stringLen - 2 ) + " .. " ) ;
reportError ( tok , Severity : : warning , " staticStringCompare " ,
" Unnecessary comparison of static strings. \n "
" The compared strings, ' " + string1 + " ' and ' " + string2 + " ', are always " + ( str1 = = str2 ? " identical " : " unequal " ) + " . "
2021-02-24 22:00:06 +01:00
" Therefore the comparison is unnecessary and looks suspicious. " , ( str1 = = str2 ) ? CWE571 : CWE570 , Certainty : : normal ) ;
2014-08-27 09:42:09 +02:00
}
void CheckString : : alwaysTrueStringVariableCompareError ( const Token * tok , const std : : string & str1 , const std : : string & str2 )
{
reportError ( tok , Severity : : warning , " stringCompare " ,
" Comparison of identical string variables. \n "
" The compared strings, ' " + str1 + " ' and ' " + str2 + " ', are identical. "
2021-02-24 22:00:06 +01:00
" This could be a logic bug. " , CWE571 , Certainty : : normal ) ;
2014-08-27 09:42:09 +02:00
}
//-----------------------------------------------------------------------------
// Detect "str == '\0'" where "*str == '\0'" is correct.
// Comparing char* with each other instead of using strcmp()
//-----------------------------------------------------------------------------
void CheckString : : checkSuspiciousStringCompare ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2014-08-27 09:42:09 +02:00
return ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckString::checkSuspiciousStringCompare " ) ; // warning
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-13 16:40:15 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2018-11-09 06:30:41 +01:00
if ( ! tok - > isComparisonOp ( ) )
2014-08-27 09:42:09 +02:00
continue ;
const Token * varTok = tok - > astOperand1 ( ) ;
const Token * litTok = tok - > astOperand2 ( ) ;
if ( ! varTok | | ! litTok ) // <- failed to create AST for comparison
continue ;
2016-10-12 10:20:24 +02:00
if ( Token : : Match ( varTok , " %char%|%num%|%str% " ) )
2014-08-27 09:42:09 +02:00
std : : swap ( varTok , litTok ) ;
2016-10-09 11:39:20 +02:00
else if ( ! Token : : Match ( litTok , " %char%|%num%|%str% " ) )
2014-08-27 09:42:09 +02:00
continue ;
2021-02-20 13:29:14 +01:00
if ( varTok - > isLiteral ( ) )
2020-06-06 17:47:30 +02:00
continue ;
2021-02-20 13:29:14 +01:00
const ValueType * varType = varTok - > valueType ( ) ;
if ( mTokenizer - > isCPP ( ) & & ( ! varType | | ! varType - > isIntegral ( ) ) )
2014-08-27 09:42:09 +02:00
continue ;
2015-08-14 20:46:13 +02:00
if ( litTok - > tokType ( ) = = Token : : eString ) {
2021-02-20 13:29:14 +01:00
if ( mTokenizer - > isC ( ) | | ( varType & & varType - > pointer ) )
suspiciousStringCompareError ( tok , varTok - > expressionString ( ) , litTok - > isLong ( ) ) ;
} else if ( litTok - > tokType ( ) = = Token : : eChar & & varType & & varType - > pointer ) {
suspiciousStringCompareError_char ( tok , varTok - > expressionString ( ) ) ;
2014-08-27 09:42:09 +02:00
}
}
}
}
2019-04-06 06:54:38 +02:00
void CheckString : : suspiciousStringCompareError ( const Token * tok , const std : : string & var , bool isLong )
2014-08-27 09:42:09 +02:00
{
2019-04-06 06:54:38 +02:00
const std : : string cmpFunc = isLong ? " wcscmp " : " strcmp " ;
2014-08-27 09:42:09 +02:00
reportError ( tok , Severity : : warning , " literalWithCharPtrCompare " ,
2021-02-24 22:00:06 +01:00
" $symbol: " + var + " \n String literal compared with variable '$symbol'. Did you intend to use " + cmpFunc + " () instead? " , CWE595 , Certainty : : normal ) ;
2014-08-27 09:42:09 +02:00
}
void CheckString : : suspiciousStringCompareError_char ( const Token * tok , const std : : string & var )
{
reportError ( tok , Severity : : warning , " charLiteralWithCharPtrCompare " ,
2021-02-24 22:00:06 +01:00
" $symbol: " + var + " \n Char literal compared with pointer '$symbol'. Did you intend to dereference it? " , CWE595 , Certainty : : normal ) ;
2014-08-27 09:42:09 +02:00
}
//---------------------------------------------------------------------------
// Adding C-string and char with operator+
//---------------------------------------------------------------------------
static bool isChar ( const Variable * var )
{
2019-04-06 06:54:38 +02:00
return ( var & & ! var - > isPointer ( ) & & ! var - > isArray ( ) & & ( var - > typeStartToken ( ) - > str ( ) = = " char " | | var - > typeStartToken ( ) - > str ( ) = = " wchar_t " ) ) ;
2014-08-27 09:42:09 +02:00
}
void CheckString : : strPlusChar ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckString::strPlusChar " ) ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-13 16:40:15 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2014-08-27 09:42:09 +02:00
if ( tok - > str ( ) = = " + " ) {
2015-08-14 20:46:13 +02:00
if ( tok - > astOperand1 ( ) & & ( tok - > astOperand1 ( ) - > tokType ( ) = = Token : : eString ) ) { // string literal...
if ( tok - > astOperand2 ( ) & & ( tok - > astOperand2 ( ) - > tokType ( ) = = Token : : eChar | | isChar ( tok - > astOperand2 ( ) - > variable ( ) ) ) ) // added to char variable or char constant
2014-08-27 09:42:09 +02:00
strPlusCharError ( tok ) ;
}
}
}
}
}
void CheckString : : strPlusCharError ( const Token * tok )
{
2019-04-06 06:54:38 +02:00
std : : string charType = " char " ;
if ( tok & & tok - > astOperand2 ( ) & & tok - > astOperand2 ( ) - > variable ( ) )
charType = tok - > astOperand2 ( ) - > variable ( ) - > typeStartToken ( ) - > str ( ) ;
else if ( tok & & tok - > astOperand2 ( ) & & tok - > astOperand2 ( ) - > tokType ( ) = = Token : : eChar & & tok - > astOperand2 ( ) - > isLong ( ) )
charType = " wchar_t " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " strPlusChar " , " Unusual pointer arithmetic. A value of type ' " + charType + " ' is added to a string literal. " , CWE665 , Certainty : : normal ) ;
2014-08-27 09:42:09 +02:00
}
2023-08-23 18:06:41 +02:00
static bool isMacroUsage ( const Token * tok )
{
if ( const Token * parent = tok - > astParent ( ) ) {
2023-09-12 10:05:51 +02:00
while ( parent & & parent - > isCast ( ) )
parent = parent - > astParent ( ) ;
if ( ! parent )
return false ;
2023-08-23 18:06:41 +02:00
if ( parent - > isExpandedMacro ( ) )
return true ;
2023-09-12 10:05:51 +02:00
if ( parent - > isUnaryOp ( " ! " ) | | parent - > isComparisonOp ( ) ) {
2023-08-23 18:06:41 +02:00
int argn { } ;
const Token * ftok = getTokenArgumentFunction ( parent , argn ) ;
if ( ftok & & ! ftok - > function ( ) )
return true ;
}
}
return false ;
}
2014-08-27 09:42:09 +02:00
//---------------------------------------------------------------------------
// Implicit casts of string literals to bool
// Comparing string literal with strlen() with wrong length
//---------------------------------------------------------------------------
void CheckString : : checkIncorrectStringCompare ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2014-08-27 09:42:09 +02:00
return ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckString::checkIncorrectStringCompare " ) ; // warning
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-13 16:40:15 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2014-08-27 09:42:09 +02:00
// skip "assert(str && ..)" and "assert(.. && str)"
2021-09-28 20:34:21 +02:00
if ( ( endsWith ( tok - > str ( ) , " assert " ) | | endsWith ( tok - > str ( ) , " ASSERT " ) ) & &
2018-07-25 16:14:43 +02:00
Token : : Match ( tok , " %name% ( " ) & &
2014-12-09 23:04:14 +01:00
( Token : : Match ( tok - > tokAt ( 2 ) , " %str% && " ) | | Token : : Match ( tok - > next ( ) - > link ( ) - > tokAt ( - 2 ) , " && %str% ) " ) ) )
2014-08-27 09:42:09 +02:00
tok = tok - > next ( ) - > link ( ) ;
if ( Token : : simpleMatch ( tok , " . substr ( " ) & & Token : : Match ( tok - > tokAt ( 3 ) - > nextArgument ( ) , " %num% ) " ) ) {
2023-10-05 19:21:42 +02:00
const MathLib : : biguint clen = MathLib : : toBigUNumber ( tok - > linkAt ( 2 ) - > strAt ( - 1 ) ) ;
2014-08-27 09:42:09 +02:00
const Token * begin = tok - > previous ( ) ;
for ( ; ; ) { // Find start of statement
while ( begin - > link ( ) & & Token : : Match ( begin , " ]|)|> " ) )
begin = begin - > link ( ) - > previous ( ) ;
if ( Token : : Match ( begin - > previous ( ) , " .|:: " ) )
begin = begin - > tokAt ( - 2 ) ;
else
break ;
}
begin = begin - > previous ( ) ;
const Token * end = tok - > linkAt ( 2 ) - > next ( ) ;
if ( Token : : Match ( begin - > previous ( ) , " %str% ==|!= " ) & & begin - > strAt ( - 2 ) ! = " + " ) {
2018-04-04 21:51:31 +02:00
const std : : size_t slen = Token : : getStrLength ( begin - > previous ( ) ) ;
2015-11-28 10:11:07 +01:00
if ( clen ! = slen ) {
2014-08-27 09:42:09 +02:00
incorrectStringCompareError ( tok - > next ( ) , " substr " , begin - > strAt ( - 1 ) ) ;
}
} else if ( Token : : Match ( end , " ==|!= %str% !!+ " ) ) {
2018-04-04 21:51:31 +02:00
const std : : size_t slen = Token : : getStrLength ( end - > next ( ) ) ;
2015-11-28 10:11:07 +01:00
if ( clen ! = slen ) {
2014-08-27 09:42:09 +02:00
incorrectStringCompareError ( tok - > next ( ) , " substr " , end - > strAt ( 1 ) ) ;
}
}
2023-08-22 19:33:24 +02:00
} else if ( Token : : Match ( tok , " %str%|%char% " ) & &
2023-08-23 18:06:41 +02:00
isUsedAsBool ( tok ) & &
! isMacroUsage ( tok ) )
2023-08-18 10:32:52 +02:00
incorrectStringBooleanError ( tok , tok - > str ( ) ) ;
2014-08-27 09:42:09 +02:00
}
}
}
void CheckString : : incorrectStringCompareError ( const Token * tok , const std : : string & func , const std : : string & string )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " incorrectStringCompare " , " $symbol: " + func + " \n String literal " + string + " doesn't match length argument for $symbol(). " , CWE570 , Certainty : : normal ) ;
2014-08-27 09:42:09 +02:00
}
void CheckString : : incorrectStringBooleanError ( const Token * tok , const std : : string & string )
{
2019-10-16 11:41:33 +02:00
const bool charLiteral = isCharLiteral ( string ) ;
2018-08-12 08:01:15 +02:00
const std : : string literalType = charLiteral ? " char " : " string " ;
2023-09-11 11:34:22 +02:00
const std : : string result = bool_to_string ( getCharLiteral ( string ) ! = " \\ 0 " ) ;
2018-07-21 18:40:06 +02:00
reportError ( tok ,
Severity : : warning ,
charLiteral ? " incorrectCharBooleanError " : " incorrectStringBooleanError " ,
2021-02-24 22:00:06 +01:00
" Conversion of " + literalType + " literal " + string + " to bool always evaluates to " + result + ' . ' , CWE571 , Certainty : : normal ) ;
2014-08-27 09:42:09 +02:00
}
2017-11-27 12:55:20 +01:00
//---------------------------------------------------------------------------
// always true: strcmp(str,"a")==0 || strcmp(str,"b")
// TODO: Library configuration for string comparison functions
//---------------------------------------------------------------------------
2017-12-13 15:28:50 +01:00
void CheckString : : overlappingStrcmp ( )
2017-11-27 12:55:20 +01:00
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : warning ) )
2017-11-27 12:55:20 +01:00
return ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckString::overlappingStrcmp " ) ; // warning
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-13 16:40:15 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2017-11-27 23:32:20 +01:00
if ( tok - > str ( ) ! = " || " )
continue ;
std : : list < const Token * > equals0 ;
std : : list < const Token * > notEquals0 ;
2020-07-09 23:08:04 +02:00
visitAstNodes ( tok , [ & ] ( const Token * t ) {
2017-11-27 23:32:20 +01:00
if ( ! t )
2020-07-09 23:08:04 +02:00
return ChildrenToVisit : : none ;
2017-11-27 23:32:20 +01:00
if ( t - > str ( ) = = " || " ) {
2020-07-09 23:08:04 +02:00
return ChildrenToVisit : : op1_and_op2 ;
2017-11-27 23:32:20 +01:00
}
if ( t - > str ( ) = = " == " ) {
if ( Token : : simpleMatch ( t - > astOperand1 ( ) , " ( " ) & & Token : : simpleMatch ( t - > astOperand2 ( ) , " 0 " ) )
equals0 . push_back ( t - > astOperand1 ( ) ) ;
else if ( Token : : simpleMatch ( t - > astOperand2 ( ) , " ( " ) & & Token : : simpleMatch ( t - > astOperand1 ( ) , " 0 " ) )
equals0 . push_back ( t - > astOperand2 ( ) ) ;
2020-07-09 23:08:04 +02:00
return ChildrenToVisit : : none ;
2017-11-27 23:32:20 +01:00
}
if ( t - > str ( ) = = " != " ) {
if ( Token : : simpleMatch ( t - > astOperand1 ( ) , " ( " ) & & Token : : simpleMatch ( t - > astOperand2 ( ) , " 0 " ) )
notEquals0 . push_back ( t - > astOperand1 ( ) ) ;
else if ( Token : : simpleMatch ( t - > astOperand2 ( ) , " ( " ) & & Token : : simpleMatch ( t - > astOperand1 ( ) , " 0 " ) )
notEquals0 . push_back ( t - > astOperand2 ( ) ) ;
2020-07-09 23:08:04 +02:00
return ChildrenToVisit : : none ;
2017-11-27 23:32:20 +01:00
}
if ( t - > str ( ) = = " ! " & & Token : : simpleMatch ( t - > astOperand1 ( ) , " ( " ) )
equals0 . push_back ( t - > astOperand1 ( ) ) ;
else if ( t - > str ( ) = = " ( " )
notEquals0 . push_back ( t ) ;
2020-07-09 23:08:04 +02:00
return ChildrenToVisit : : none ;
} ) ;
2017-11-27 23:32:20 +01:00
2018-07-13 16:40:15 +02:00
for ( const Token * eq0 : equals0 ) {
for ( const Token * ne0 : notEquals0 ) {
2019-04-06 06:54:38 +02:00
if ( ! Token : : Match ( eq0 - > previous ( ) , " strcmp|wcscmp ( " ) )
2017-11-27 23:32:20 +01:00
continue ;
2019-04-06 06:54:38 +02:00
if ( ! Token : : Match ( ne0 - > previous ( ) , " strcmp|wcscmp ( " ) )
2017-11-27 23:32:20 +01:00
continue ;
2018-07-13 16:40:15 +02:00
const std : : vector < const Token * > args1 = getArguments ( eq0 - > previous ( ) ) ;
const std : : vector < const Token * > args2 = getArguments ( ne0 - > previous ( ) ) ;
2017-11-27 23:32:20 +01:00
if ( args1 . size ( ) ! = 2 | | args2 . size ( ) ! = 2 )
continue ;
if ( args1 [ 1 ] - > isLiteral ( ) & &
args2 [ 1 ] - > isLiteral ( ) & &
args1 [ 1 ] - > str ( ) ! = args2 [ 1 ] - > str ( ) & &
2018-09-28 08:38:24 +02:00
isSameExpression ( mTokenizer - > isCPP ( ) , true , args1 [ 0 ] , args2 [ 0 ] , mSettings - > library , true , false ) )
2018-07-13 16:40:15 +02:00
overlappingStrcmpError ( eq0 , ne0 ) ;
2017-11-27 23:32:20 +01:00
}
}
}
}
2017-11-27 12:55:20 +01:00
}
2017-12-13 15:28:50 +01:00
void CheckString : : overlappingStrcmpError ( const Token * eq0 , const Token * ne0 )
2017-11-27 12:55:20 +01:00
{
2017-11-27 23:32:20 +01:00
std : : string eq0Expr ( eq0 ? eq0 - > expressionString ( ) : std : : string ( " strcmp(x, \" abc \" ) " ) ) ;
if ( eq0 & & eq0 - > astParent ( ) - > str ( ) = = " ! " )
eq0Expr = " ! " + eq0Expr ;
else
eq0Expr + = " == 0 " ;
2017-12-11 22:10:00 +01:00
2017-12-13 15:28:50 +01:00
const std : : string ne0Expr = ( ne0 ? ne0 - > expressionString ( ) : std : : string ( " strcmp(x, \" def \" ) " ) ) + " != 0 " ;
2017-12-11 22:10:00 +01:00
2017-12-13 15:28:50 +01:00
reportError ( ne0 , Severity : : warning , " overlappingStrcmp " , " The expression ' " + ne0Expr + " ' is suspicious. It overlaps ' " + eq0Expr + " '. " ) ;
2017-11-27 12:55:20 +01:00
}
2014-08-27 09:42:09 +02:00
//---------------------------------------------------------------------------
// Overlapping source and destination passed to sprintf().
2017-04-23 10:51:31 +02:00
// TODO: Library configuration for overlapping arguments
2014-08-27 09:42:09 +02:00
//---------------------------------------------------------------------------
void CheckString : : sprintfOverlappingData ( )
{
2023-08-29 12:00:52 +02:00
logChecker ( " CheckString::sprintfOverlappingData " ) ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-13 16:40:15 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart - > next ( ) ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2017-04-23 10:51:31 +02:00
if ( ! Token : : Match ( tok , " sprintf|snprintf|swprintf ( " ) )
2014-08-27 09:42:09 +02:00
continue ;
2017-04-23 10:51:31 +02:00
const std : : vector < const Token * > args = getArguments ( tok ) ;
2017-04-23 12:14:14 +02:00
const int formatString = Token : : simpleMatch ( tok , " sprintf " ) ? 1 : 2 ;
2017-04-23 10:51:31 +02:00
for ( unsigned int argnr = formatString + 1 ; argnr < args . size ( ) ; + + argnr ) {
2019-02-25 09:23:03 +01:00
const Token * dest = args [ 0 ] ;
2019-07-05 12:27:39 +02:00
while ( dest - > isCast ( ) )
2019-02-25 09:23:03 +01:00
dest = dest - > astOperand2 ( ) ? dest - > astOperand2 ( ) : dest - > astOperand1 ( ) ;
const Token * arg = args [ argnr ] ;
if ( ! arg - > valueType ( ) | | arg - > valueType ( ) - > pointer ! = 1 )
continue ;
2019-07-05 12:27:39 +02:00
while ( arg - > isCast ( ) )
2019-02-25 09:23:03 +01:00
arg = arg - > astOperand2 ( ) ? arg - > astOperand2 ( ) : arg - > astOperand1 ( ) ;
2018-06-16 16:10:28 +02:00
const bool same = isSameExpression ( mTokenizer - > isCPP ( ) ,
2018-04-04 21:51:31 +02:00
false ,
2019-02-25 09:23:03 +01:00
dest ,
arg ,
2018-06-16 16:10:28 +02:00
mSettings - > library ,
2018-09-28 08:38:24 +02:00
true ,
false ) ;
2017-04-23 10:51:31 +02:00
if ( same ) {
2019-07-05 12:27:39 +02:00
sprintfOverlappingDataError ( tok , args [ argnr ] , arg - > expressionString ( ) ) ;
2014-10-31 11:40:42 +01:00
}
2017-04-23 10:51:31 +02:00
}
2014-10-31 11:40:42 +01:00
}
2014-08-27 09:42:09 +02:00
}
}
2019-04-06 06:54:38 +02:00
void CheckString : : sprintfOverlappingDataError ( const Token * funcTok , const Token * tok , const std : : string & varname )
2014-08-27 09:42:09 +02:00
{
2019-04-06 06:54:38 +02:00
const std : : string func = funcTok ? funcTok - > str ( ) : " s[n]printf " ;
2014-08-27 09:42:09 +02:00
reportError ( tok , Severity : : error , " sprintfOverlappingData " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + varname + " \n "
2019-04-06 06:54:38 +02:00
" Undefined behavior: Variable '$symbol' is used as parameter and destination in " + func + " (). \n " +
" The variable '$symbol' is used both as a parameter and as destination in " +
func + " (). The origin and destination buffers overlap. Quote from glibc (C-library) "
2014-08-27 09:42:09 +02:00
" documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): "
" \" If copying takes place between objects that overlap as a result of a call "
2021-02-24 22:00:06 +01:00
" to sprintf() or snprintf(), the results are undefined. \" " , CWE628 , Certainty : : normal ) ;
2014-08-27 09:42:09 +02:00
}