2014-08-27 09:42:09 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2019-06-29 07:49:14 +02:00
* Copyright ( C ) 2007 - 2019 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"
2017-05-27 04:33:47 +02:00
# include "errorlogger.h"
# 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>
2017-11-27 12:55:20 +01:00
# include <stack>
2017-05-27 04:33:47 +02:00
# 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:
2016-07-16 21:21:24 +02:00
static const struct CWE CWE570 ( 570U ) ; // Expression is Always False
static const struct CWE CWE571 ( 571U ) ; // Expression is Always True
static const struct CWE CWE595 ( 595U ) ; // Comparison of Object References Instead of Object Contents
static const struct CWE CWE628 ( 628U ) ; // Function Call with Incorrectly Specified Arguments
static const struct CWE CWE665 ( 665U ) ; // Improper Initialization
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
static const struct 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 ( )
{
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 ;
const Token * str = tok - > getValueTokenMinStrSize ( ) ;
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
{
2015-06-05 14:34:13 +02:00
std : : list < const Token * > callstack ;
callstack . push_back ( tok ) ;
if ( strValue )
callstack . push_back ( strValue ) ;
std : : string errmsg ( " Modifying string literal " ) ;
if ( strValue ) {
std : : string s = strValue - > strValue ( ) ;
if ( s . size ( ) > 15U )
s = s . substr ( 0 , 13 ) + " .. " ;
errmsg + = " \" " + s + " \" " ;
}
2015-10-18 20:55:30 +02:00
errmsg + = " directly or indirectly is undefined behaviour. " ;
2015-06-05 14:34:13 +02:00
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
reportError ( callstack , Severity : : error , " stringLiteralWrite " , errmsg , CWE758 , false ) ;
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 ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2014-08-27 09:42:09 +02:00
return ;
2018-06-16 16:10:28 +02:00
for ( const Token * tok = mTokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
2016-05-25 15:30:49 +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|_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 )
{
const std : : size_t stringLen = 10 ;
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 " ) + " . "
2016-07-15 15:49:21 +02:00
" Therefore the comparison is unnecessary and looks suspicious. " , ( str1 = = str2 ) ? CWE571 : CWE570 , false ) ;
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. "
2016-07-15 15:49:21 +02:00
" This could be a logic bug. " , CWE571 , false ) ;
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 ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2014-08-27 09:42:09 +02:00
return ;
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 ;
// Pointer addition?
2018-06-16 16:10:28 +02:00
if ( varTok - > str ( ) = = " + " & & mTokenizer - > isC ( ) ) {
2018-05-29 13:24:48 +02:00
const Token * const tokens [ 2 ] = { varTok - > astOperand1 ( ) , varTok - > astOperand2 ( ) } ;
2014-08-27 09:42:09 +02:00
for ( int nr = 0 ; nr < 2 ; nr + + ) {
const Token * t = tokens [ nr ] ;
while ( t & & ( t - > str ( ) = = " . " | | t - > str ( ) = = " :: " ) )
t = t - > astOperand2 ( ) ;
if ( t & & t - > variable ( ) & & t - > variable ( ) - > isPointer ( ) )
varTok = t ;
}
}
if ( varTok - > str ( ) = = " * " ) {
2018-06-16 16:10:28 +02:00
if ( ! mTokenizer - > isC ( ) | | varTok - > astOperand2 ( ) ! = nullptr | | litTok - > tokType ( ) ! = Token : : eString )
2014-08-27 09:42:09 +02:00
continue ;
varTok = varTok - > astOperand1 ( ) ;
}
while ( varTok & & ( varTok - > str ( ) = = " . " | | varTok - > str ( ) = = " :: " ) )
varTok = varTok - > astOperand2 ( ) ;
if ( ! varTok | | ! varTok - > isName ( ) )
continue ;
const Variable * var = varTok - > variable ( ) ;
while ( Token : : Match ( varTok - > astParent ( ) , " [.*] " ) )
varTok = varTok - > astParent ( ) ;
const std : : string varname = varTok - > expressionString ( ) ;
2016-10-12 10:20:24 +02:00
const bool ischar ( litTok - > tokType ( ) = = Token : : eChar ) ;
2015-08-14 20:46:13 +02:00
if ( litTok - > tokType ( ) = = Token : : eString ) {
2018-06-16 16:10:28 +02:00
if ( mTokenizer - > isC ( ) | | ( var & & var - > isArrayOrPointer ( ) ) )
2019-04-06 06:54:38 +02:00
suspiciousStringCompareError ( tok , varname , litTok - > isLong ( ) ) ;
2016-10-09 11:39:20 +02:00
} else if ( ischar & & var & & var - > isPointer ( ) ) {
2014-08-27 09:42:09 +02:00
suspiciousStringCompareError_char ( tok , varname ) ;
}
}
}
}
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 " ,
2019-04-06 06:54:38 +02:00
" $symbol: " + var + " \n String literal compared with variable '$symbol'. Did you intend to use " + cmpFunc + " () instead? " , CWE595 , false ) ;
2014-08-27 09:42:09 +02:00
}
void CheckString : : suspiciousStringCompareError_char ( const Token * tok , const std : : string & var )
{
reportError ( tok , Severity : : warning , " charLiteralWithCharPtrCompare " ,
2018-04-09 06:43:48 +02:00
" $symbol: " + var + " \n Char literal compared with pointer '$symbol'. Did you intend to dereference it? " , CWE595 , false ) ;
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 ( )
{
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 " ;
reportError ( tok , Severity : : error , " strPlusChar " , " Unusual pointer arithmetic. A value of type ' " + charType + " ' is added to a string literal. " , CWE665 , 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 ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2014-08-27 09:42:09 +02:00
return ;
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)"
2018-07-25 16:14:43 +02:00
if ( ( endsWith ( tok - > str ( ) , " assert " , 6 ) | | endsWith ( tok - > str ( ) , " ASSERT " , 6 ) ) & &
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% ) " ) ) {
2018-04-04 21:51:31 +02:00
const MathLib : : biguint clen = MathLib : : toULongNumber ( 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 ) ) ;
}
}
2018-07-21 18:40:06 +02:00
} else if ( Token : : Match ( tok , " &&|%oror%|( %str%|%char% &&|%oror%|) " ) && !Token::Match(tok, " ( % str % | % char % ) " )) {
2014-08-27 09:42:09 +02:00
incorrectStringBooleanError ( tok - > next ( ) , tok - > strAt ( 1 ) ) ;
2018-08-08 19:04:10 +02:00
} else if ( Token : : Match ( tok , " if|while ( %str%|%char% ) " ) && !tok->tokAt(2)->getValue(0)) {
2014-08-27 09:42:09 +02:00
incorrectStringBooleanError ( tok - > tokAt ( 2 ) , tok - > strAt ( 2 ) ) ;
2019-04-06 06:54:38 +02:00
} else if ( tok - > str ( ) = = " ? " & & Token : : Match ( tok - > astOperand1 ( ) , " %str%|%char% " ) )
incorrectStringBooleanError ( tok - > astOperand1 ( ) , tok - > astOperand1 ( ) - > str ( ) ) ;
2014-08-27 09:42:09 +02:00
}
}
}
void CheckString : : incorrectStringCompareError ( const Token * tok , const std : : string & func , const std : : string & string )
{
2018-04-09 06:43:48 +02:00
reportError ( tok , Severity : : warning , " incorrectStringCompare " , " $symbol: " + func + " \n String literal " + string + " doesn't match length argument for $symbol(). " , CWE570 , false ) ;
2014-08-27 09:42:09 +02:00
}
void CheckString : : incorrectStringBooleanError ( const Token * tok , const std : : string & string )
{
2018-07-21 18:40:06 +02:00
const bool charLiteral = string [ 0 ] = = ' \' ' ;
2018-08-12 08:01:15 +02:00
const std : : string literalType = charLiteral ? " char " : " string " ;
const std : : string result = ( string = = " \' \\ 0 \' " ) ? " false " : " true " ;
2018-07-21 18:40:06 +02:00
reportError ( tok ,
Severity : : warning ,
charLiteral ? " incorrectCharBooleanError " : " incorrectStringBooleanError " ,
2018-08-12 08:01:15 +02:00
" Conversion of " + literalType + " literal " + string + " to bool always evaluates to " + result + ' . ' , CWE571 , false ) ;
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
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2017-11-27 12:55:20 +01:00
return ;
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 ;
std : : stack < const Token * > tokens ;
tokens . push ( tok ) ;
while ( ! tokens . empty ( ) ) {
const Token * const t = tokens . top ( ) ;
tokens . pop ( ) ;
if ( ! t )
continue ;
if ( t - > str ( ) = = " || " ) {
tokens . push ( t - > astOperand1 ( ) ) ;
tokens . push ( t - > astOperand2 ( ) ) ;
continue ;
}
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 ( ) ) ;
continue ;
}
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 ( ) ) ;
continue ;
}
if ( t - > str ( ) = = " ! " & & Token : : simpleMatch ( t - > astOperand1 ( ) , " ( " ) )
equals0 . push_back ( t - > astOperand1 ( ) ) ;
else if ( t - > str ( ) = = " ( " )
notEquals0 . push_back ( t ) ;
}
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 ( )
{
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 ] ;
if ( dest - > isCast ( ) )
dest = dest - > astOperand2 ( ) ? dest - > astOperand2 ( ) : dest - > astOperand1 ( ) ;
const Token * arg = args [ argnr ] ;
if ( ! arg - > valueType ( ) | | arg - > valueType ( ) - > pointer ! = 1 )
continue ;
if ( arg - > isCast ( ) )
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-04-06 06:54:38 +02:00
sprintfOverlappingDataError ( tok , args [ argnr ] , args [ argnr ] - > 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 "
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
" to sprintf() or snprintf(), the results are undefined. \" " , CWE628 , false ) ;
2014-08-27 09:42:09 +02:00
}