2012-05-20 11:57:07 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2019-02-09 07:24:06 +01:00
* Copyright ( C ) 2007 - 2019 Cppcheck team .
2012-05-20 11:57:07 +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 "checkio.h"
2017-05-27 04:33:47 +02:00
# include "library.h"
# include "mathlib.h"
# include "settings.h"
2012-05-20 11:57:07 +02:00
# include "symboldatabase.h"
2017-05-27 04:33:47 +02:00
# include "token.h"
# include "tokenize.h"
2015-11-29 10:49:10 +01:00
# include "utils.h"
2017-05-27 04:33:47 +02:00
# include "valueflow.h"
2012-05-20 11:57:07 +02:00
# include <cctype>
2012-06-23 07:52:52 +02:00
# include <cstdlib>
2017-05-27 04:33:47 +02:00
# include <list>
# include <map>
# include <set>
# include <utility>
# include <vector>
2012-05-20 11:57:07 +02:00
//---------------------------------------------------------------------------
// Register CheckIO..
namespace {
CheckIO instance ;
}
2016-04-22 06:01:34 +02:00
// CVE ID used:
2016-08-01 13:01:01 +02:00
static const CWE CWE119 ( 119U ) ; // Improper Restriction of Operations within the Bounds of a Memory Buffer
2016-04-22 06:01:34 +02:00
static const CWE CWE398 ( 398U ) ; // Indicator of Poor Code Quality
2016-08-01 13:01:01 +02:00
static const CWE CWE664 ( 664U ) ; // Improper Control of a Resource Through its Lifetime
static const CWE CWE685 ( 685U ) ; // Function Call With Incorrect Number of Arguments
static const CWE CWE686 ( 686U ) ; // Function Call With Incorrect Argument Type
static const CWE CWE687 ( 687U ) ; // Function Call With Incorrectly Specified Argument Value
2016-08-25 19:17:07 +02:00
static const CWE CWE704 ( 704U ) ; // Incorrect Type Conversion or Cast
2016-08-01 13:01:01 +02:00
static const CWE CWE910 ( 910U ) ; // Use of Expired File Descriptor
2012-05-20 11:57:07 +02:00
//---------------------------------------------------------------------------
// std::cout << std::cout;
//---------------------------------------------------------------------------
void CheckIO : : checkCoutCerrMisusage ( )
{
2018-06-16 16:10:28 +02:00
if ( mTokenizer - > isC ( ) )
2014-09-01 17:01:05 +02:00
return ;
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-14 10:02:32 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok & & tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2014-05-22 20:25:54 +02:00
if ( Token : : Match ( tok , " std :: cout|cerr !!. " ) & & tok - > next ( ) - > astParent ( ) & & tok - > next ( ) - > astParent ( ) - > astOperand1 ( ) = = tok - > next ( ) ) {
const Token * tok2 = tok - > next ( ) ;
while ( tok2 - > astParent ( ) & & tok2 - > astParent ( ) - > str ( ) = = " << " ) {
tok2 = tok2 - > astParent ( ) ;
if ( tok2 - > astOperand2 ( ) & & Token : : Match ( tok2 - > astOperand2 ( ) - > previous ( ) , " std :: cout|cerr " ) )
coutCerrMisusageError ( tok , tok2 - > astOperand2 ( ) - > strAt ( 1 ) ) ;
}
}
2012-10-30 15:52:45 +01:00
}
2012-05-20 11:57:07 +02:00
}
}
void CheckIO : : coutCerrMisusageError ( const Token * tok , const std : : string & streamName )
{
2016-08-23 17:48:36 +02:00
reportError ( tok , Severity : : error , " coutCerrMisusage " , " Invalid usage of output stream: '<< std:: " + streamName + " '. " , CWE398 , false ) ;
2012-05-20 11:57:07 +02:00
}
//---------------------------------------------------------------------------
// fflush(stdin) <- fflush only applies to output streams in ANSI C
2012-07-21 15:54:04 +02:00
// fread(); fwrite(); <- consecutive read/write statements require repositioning in between
2012-05-22 14:30:22 +02:00
// fopen("","r"); fwrite(); <- write to read-only file (or vice versa)
// fclose(); fread(); <- Use closed file
2012-05-20 11:57:07 +02:00
//---------------------------------------------------------------------------
2014-03-17 11:02:03 +01:00
enum OpenMode { CLOSED , READ_MODE , WRITE_MODE , RW_MODE , UNKNOWN_OM } ;
2012-05-22 14:30:22 +02:00
static OpenMode getMode ( const std : : string & str )
2012-05-20 11:57:07 +02:00
{
2012-05-22 14:30:22 +02:00
if ( str . find ( ' + ' , 1 ) ! = std : : string : : npos )
return RW_MODE ;
else if ( str . find ( ' w ' ) ! = std : : string : : npos | | str . find ( ' a ' ) ! = std : : string : : npos )
return WRITE_MODE ;
else if ( str . find ( ' r ' ) ! = std : : string : : npos )
return READ_MODE ;
2014-03-17 11:02:03 +01:00
return UNKNOWN_OM ;
2012-05-22 14:30:22 +02:00
}
2012-05-22 15:43:40 +02:00
struct Filepointer {
OpenMode mode ;
2019-07-16 08:37:26 +02:00
nonneg int mode_indent ;
2012-05-22 15:43:40 +02:00
enum Operation { NONE , UNIMPORTANT , READ , WRITE , POSITIONING , OPEN , CLOSE , UNKNOWN_OP } lastOperation ;
2019-07-16 08:37:26 +02:00
nonneg int op_indent ;
2014-03-17 11:02:03 +01:00
enum AppendMode { UNKNOWN_AM , APPEND , APPEND_EX } ;
AppendMode append_mode ;
2014-11-13 21:39:14 +01:00
explicit Filepointer ( OpenMode mode_ = UNKNOWN_OM )
2014-11-20 14:20:09 +01:00
: mode ( mode_ ) , mode_indent ( 0 ) , lastOperation ( NONE ) , op_indent ( 0 ) , append_mode ( UNKNOWN_AM ) {
2012-05-22 15:43:40 +02:00
}
} ;
2015-06-20 22:26:51 +02:00
namespace {
2018-04-08 22:54:10 +02:00
const std : : set < std : : string > whitelist = { " clearerr " , " feof " , " ferror " , " fgetpos " , " ftell " , " setbuf " , " setvbuf " , " ungetc " , " ungetwc " } ;
2015-06-20 22:26:51 +02:00
}
2012-05-22 14:30:22 +02:00
void CheckIO : : checkFileUsage ( )
{
2018-06-16 16:10:28 +02:00
const bool windows = mSettings - > isWindowsPlatform ( ) ;
const bool printPortability = mSettings - > isEnabled ( Settings : : PORTABILITY ) ;
const bool printWarnings = mSettings - > isEnabled ( Settings : : WARNING ) ;
2012-05-22 14:30:22 +02:00
2019-07-16 08:37:26 +02:00
std : : map < int , Filepointer > filepointers ;
2012-05-22 14:30:22 +02:00
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-04-28 09:38:33 +02:00
for ( const Variable * var : symbolDatabase - > variableList ( ) ) {
2013-07-20 12:31:04 +02:00
if ( ! var | | ! var - > declarationId ( ) | | var - > isArray ( ) | | ! Token : : simpleMatch ( var - > typeStartToken ( ) , " FILE * " ) )
2012-05-22 14:30:22 +02:00
continue ;
2012-06-24 18:57:17 +02:00
if ( var - > isLocal ( ) ) {
if ( var - > nameToken ( ) - > strAt ( 1 ) = = " ( " ) // initialize by calling "ctor"
2014-03-17 11:02:03 +01:00
filepointers . insert ( std : : make_pair ( var - > declarationId ( ) , Filepointer ( UNKNOWN_OM ) ) ) ;
2012-06-24 18:57:17 +02:00
else
2013-07-20 12:31:04 +02:00
filepointers . insert ( std : : make_pair ( var - > declarationId ( ) , Filepointer ( CLOSED ) ) ) ;
2012-06-24 18:57:17 +02:00
} else {
2014-03-17 11:02:03 +01:00
filepointers . insert ( std : : make_pair ( var - > declarationId ( ) , Filepointer ( UNKNOWN_OM ) ) ) ;
2012-05-22 14:30:22 +02:00
// TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode
}
}
2018-04-28 09:38:33 +02:00
for ( const Scope * scope : symbolDatabase - > functionScopes ) {
2019-07-16 08:37:26 +02:00
int indent = 0 ;
2018-04-27 22:36:30 +02:00
for ( const Token * tok = scope - > bodyStart ; tok ! = scope - > bodyEnd ; tok = tok - > next ( ) ) {
2012-10-30 15:52:45 +01:00
if ( tok - > str ( ) = = " { " )
indent + + ;
else if ( tok - > str ( ) = = " } " ) {
indent - - ;
2019-07-16 08:37:26 +02:00
for ( std : : map < int , Filepointer > : : iterator i = filepointers . begin ( ) ; i ! = filepointers . end ( ) ; + + i ) {
2012-10-30 15:52:45 +01:00
if ( indent < i - > second . mode_indent ) {
i - > second . mode_indent = 0 ;
2014-03-17 11:02:03 +01:00
i - > second . mode = UNKNOWN_OM ;
2012-10-30 15:52:45 +01:00
}
if ( indent < i - > second . op_indent ) {
i - > second . op_indent = 0 ;
i - > second . lastOperation = Filepointer : : UNKNOWN_OP ;
}
2012-05-22 14:30:22 +02:00
}
2018-06-16 16:10:28 +02:00
} else if ( tok - > str ( ) = = " return " | | tok - > str ( ) = = " continue " | | tok - > str ( ) = = " break " | | mSettings - > library . isnoreturn ( tok ) ) { // Reset upon return, continue or break
2019-07-16 08:37:26 +02:00
for ( std : : map < int , Filepointer > : : iterator i = filepointers . begin ( ) ; i ! = filepointers . end ( ) ; + + i ) {
2013-02-15 17:30:43 +01:00
i - > second . mode_indent = 0 ;
2014-03-17 11:02:03 +01:00
i - > second . mode = UNKNOWN_OM ;
2013-02-15 17:30:43 +01:00
i - > second . op_indent = 0 ;
i - > second . lastOperation = Filepointer : : UNKNOWN_OP ;
}
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok , " %var% = " ) & &
2014-03-12 19:22:44 +01:00
( tok - > strAt ( 2 ) ! = " fopen " & & tok - > strAt ( 2 ) ! = " freopen " & & tok - > strAt ( 2 ) ! = " tmpfile " & &
( windows ? ( tok - > str ( ) ! = " _wfopen " & & tok - > str ( ) ! = " _wfreopen " ) : true ) ) ) {
2019-07-16 08:37:26 +02:00
std : : map < int , Filepointer > : : iterator i = filepointers . find ( tok - > varId ( ) ) ;
2012-10-30 15:52:45 +01:00
if ( i ! = filepointers . end ( ) ) {
2014-03-17 11:02:03 +01:00
i - > second . mode = UNKNOWN_OM ;
2012-05-22 14:30:22 +02:00
i - > second . lastOperation = Filepointer : : UNKNOWN_OP ;
}
2015-01-31 10:50:39 +01:00
} else if ( Token : : Match ( tok , " %name% ( " ) & & tok - > previous ( ) & & ( ! tok - > previous ( ) - > isName ( ) | | Token : : Match ( tok - > previous ( ) , " return|throw " ) ) ) {
2012-10-30 15:52:45 +01:00
std : : string mode ;
2017-07-28 15:20:43 +02:00
const Token * fileTok = nullptr ;
2012-10-30 15:52:45 +01:00
Filepointer : : Operation operation = Filepointer : : NONE ;
2014-03-12 19:22:44 +01:00
if ( ( tok - > str ( ) = = " fopen " | | tok - > str ( ) = = " freopen " | | tok - > str ( ) = = " tmpfile " | |
( windows & & ( tok - > str ( ) = = " _wfopen " | | tok - > str ( ) = = " _wfreopen " ) ) ) & &
tok - > strAt ( - 1 ) = = " = " ) {
2012-10-30 15:52:45 +01:00
if ( tok - > str ( ) ! = " tmpfile " ) {
const Token * modeTok = tok - > tokAt ( 2 ) - > nextArgument ( ) ;
2015-08-14 20:46:13 +02:00
if ( modeTok & & modeTok - > tokType ( ) = = Token : : eString )
2012-10-30 15:52:45 +01:00
mode = modeTok - > strValue ( ) ;
} else
mode = " wb+ " ;
fileTok = tok - > tokAt ( - 2 ) ;
operation = Filepointer : : OPEN ;
2015-01-31 10:50:39 +01:00
} else if ( windows & & Token : : Match ( tok , " fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %name% " ) ) {
2014-03-12 19:22:44 +01:00
const Token * modeTok = tok - > tokAt ( 2 ) - > nextArgument ( ) - > nextArgument ( ) ;
2015-08-14 20:46:13 +02:00
if ( modeTok & & modeTok - > tokType ( ) = = Token : : eString )
2014-03-12 19:22:44 +01:00
mode = modeTok - > strValue ( ) ;
fileTok = tok - > tokAt ( 3 ) ;
operation = Filepointer : : OPEN ;
} else if ( ( tok - > str ( ) = = " rewind " | | tok - > str ( ) = = " fseek " | | tok - > str ( ) = = " fsetpos " | | tok - > str ( ) = = " fflush " ) | |
( windows & & tok - > str ( ) = = " _fseeki64 " ) ) {
2017-09-28 22:41:29 +02:00
fileTok = tok - > tokAt ( 2 ) ;
if ( printPortability & & fileTok & & tok - > str ( ) = = " fflush " ) {
if ( fileTok - > str ( ) = = " stdin " )
fflushOnInputStreamError ( tok , fileTok - > str ( ) ) ;
else {
2018-05-29 13:24:48 +02:00
const Filepointer & f = filepointers [ fileTok - > varId ( ) ] ;
2017-09-28 22:41:29 +02:00
if ( f . mode = = READ_MODE )
2014-08-31 21:03:33 +02:00
fflushOnInputStreamError ( tok , fileTok - > str ( ) ) ;
}
2012-10-30 15:52:45 +01:00
}
2014-08-31 21:03:33 +02:00
operation = Filepointer : : POSITIONING ;
2014-03-12 19:22:44 +01:00
} else if ( tok - > str ( ) = = " fgetc " | | tok - > str ( ) = = " fgetwc " | |
tok - > str ( ) = = " fgets " | | tok - > str ( ) = = " fgetws " | | tok - > str ( ) = = " fread " | |
tok - > str ( ) = = " fscanf " | | tok - > str ( ) = = " fwscanf " | | tok - > str ( ) = = " getc " | |
( windows & & ( tok - > str ( ) = = " fscanf_s " | | tok - > str ( ) = = " fwscanf_s " ) ) ) {
if ( tok - > str ( ) . find ( " scanf " ) ! = std : : string : : npos )
2012-10-30 15:52:45 +01:00
fileTok = tok - > tokAt ( 2 ) ;
else
fileTok = tok - > linkAt ( 1 ) - > previous ( ) ;
operation = Filepointer : : READ ;
2014-03-12 19:22:44 +01:00
} else if ( tok - > str ( ) = = " fputc " | | tok - > str ( ) = = " fputwc " | |
tok - > str ( ) = = " fputs " | | tok - > str ( ) = = " fputws " | | tok - > str ( ) = = " fwrite " | |
tok - > str ( ) = = " fprintf " | | tok - > str ( ) = = " fwprintf " | | tok - > str ( ) = = " putcc " | |
( windows & & ( tok - > str ( ) = = " fprintf_s " | | tok - > str ( ) = = " fwprintf_s " ) ) ) {
if ( tok - > str ( ) . find ( " printf " ) ! = std : : string : : npos )
2012-10-30 15:52:45 +01:00
fileTok = tok - > tokAt ( 2 ) ;
else
fileTok = tok - > linkAt ( 1 ) - > previous ( ) ;
operation = Filepointer : : WRITE ;
} else if ( tok - > str ( ) = = " fclose " ) {
2012-05-22 14:30:22 +02:00
fileTok = tok - > tokAt ( 2 ) ;
2012-10-30 15:52:45 +01:00
operation = Filepointer : : CLOSE ;
} else if ( whitelist . find ( tok - > str ( ) ) ! = whitelist . end ( ) ) {
2012-05-22 14:30:22 +02:00
fileTok = tok - > tokAt ( 2 ) ;
2014-03-12 19:22:44 +01:00
if ( ( tok - > str ( ) = = " ungetc " | | tok - > str ( ) = = " ungetwc " ) & & fileTok )
2012-10-30 15:52:45 +01:00
fileTok = fileTok - > nextArgument ( ) ;
operation = Filepointer : : UNIMPORTANT ;
2018-06-16 16:10:28 +02:00
} else if ( ! Token : : Match ( tok , " if|for|while|catch|switch " ) & & ! mSettings - > library . isFunctionConst ( tok - > str ( ) , true ) ) {
2012-10-30 15:52:45 +01:00
const Token * const end2 = tok - > linkAt ( 1 ) ;
2015-01-27 21:52:52 +01:00
if ( scope - > functionOf & & scope - > functionOf - > isClassOrStruct ( ) & & ! scope - > function - > isStatic ( ) & & ( ( tok - > strAt ( - 1 ) ! = " :: " & & tok - > strAt ( - 1 ) ! = " . " ) | | tok - > strAt ( - 2 ) = = " this " ) ) {
if ( ! tok - > function ( ) | | ( tok - > function ( ) - > nestedIn & & tok - > function ( ) - > nestedIn - > isClassOrStruct ( ) ) ) {
2019-07-16 08:37:26 +02:00
for ( std : : map < int , Filepointer > : : iterator i = filepointers . begin ( ) ; i ! = filepointers . end ( ) ; + + i ) {
2015-01-27 21:52:52 +01:00
const Variable * var = symbolDatabase - > getVariableFromVarId ( i - > first ) ;
if ( ! var | | ! ( var - > isLocal ( ) | | var - > isGlobal ( ) | | var - > isStatic ( ) ) ) {
i - > second . mode = UNKNOWN_OM ;
i - > second . mode_indent = 0 ;
i - > second . op_indent = indent ;
i - > second . lastOperation = Filepointer : : UNKNOWN_OP ;
}
}
continue ;
}
}
2012-10-30 15:52:45 +01:00
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 ! = end2 ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > varId ( ) & & filepointers . find ( tok2 - > varId ( ) ) ! = filepointers . end ( ) ) {
fileTok = tok2 ;
operation = Filepointer : : UNKNOWN_OP ; // Assume that repositioning was last operation and that the file is opened now
break ;
}
2012-05-22 14:30:22 +02:00
}
}
2015-01-31 10:50:39 +01:00
while ( Token : : Match ( fileTok , " %name% . " ) )
2013-03-15 06:42:46 +01:00
fileTok = fileTok - > tokAt ( 2 ) ;
2015-07-20 22:40:19 +02:00
if ( ! fileTok | | ! fileTok - > varId ( ) | | fileTok - > strAt ( 1 ) = = " [ " )
2012-10-30 15:52:45 +01:00
continue ;
2012-05-22 14:30:22 +02:00
2012-10-30 15:52:45 +01:00
if ( filepointers . find ( fileTok - > varId ( ) ) = = filepointers . end ( ) ) { // function call indicates: Its a File
2014-03-17 11:02:03 +01:00
filepointers . insert ( std : : make_pair ( fileTok - > varId ( ) , Filepointer ( UNKNOWN_OM ) ) ) ;
2012-10-30 15:52:45 +01:00
}
Filepointer & f = filepointers [ fileTok - > varId ( ) ] ;
switch ( operation ) {
case Filepointer : : OPEN :
f . mode = getMode ( mode ) ;
2014-03-17 11:02:03 +01:00
if ( mode . find ( ' a ' ) ! = std : : string : : npos ) {
if ( f . mode = = RW_MODE )
f . append_mode = Filepointer : : APPEND_EX ;
else
f . append_mode = Filepointer : : APPEND ;
2015-10-13 15:31:58 +02:00
} else
f . append_mode = Filepointer : : UNKNOWN_AM ;
2012-10-30 15:52:45 +01:00
f . mode_indent = indent ;
break ;
case Filepointer : : POSITIONING :
if ( f . mode = = CLOSED )
useClosedFileError ( tok ) ;
2015-04-07 07:07:08 +02:00
else if ( f . append_mode = = Filepointer : : APPEND & & tok - > str ( ) ! = " fflush " & & printWarnings )
2014-03-17 11:02:03 +01:00
seekOnAppendedFileError ( tok ) ;
2012-10-30 15:52:45 +01:00
break ;
case Filepointer : : READ :
if ( f . mode = = CLOSED )
useClosedFileError ( tok ) ;
else if ( f . mode = = WRITE_MODE )
readWriteOnlyFileError ( tok ) ;
else if ( f . lastOperation = = Filepointer : : WRITE )
ioWithoutPositioningError ( tok ) ;
break ;
case Filepointer : : WRITE :
if ( f . mode = = CLOSED )
useClosedFileError ( tok ) ;
else if ( f . mode = = READ_MODE )
writeReadOnlyFileError ( tok ) ;
else if ( f . lastOperation = = Filepointer : : READ )
ioWithoutPositioningError ( tok ) ;
break ;
case Filepointer : : CLOSE :
if ( f . mode = = CLOSED )
useClosedFileError ( tok ) ;
else
f . mode = CLOSED ;
f . mode_indent = indent ;
break ;
case Filepointer : : UNIMPORTANT :
if ( f . mode = = CLOSED )
useClosedFileError ( tok ) ;
break ;
case Filepointer : : UNKNOWN_OP :
2014-03-17 11:02:03 +01:00
f . mode = UNKNOWN_OM ;
2012-10-30 15:52:45 +01:00
f . mode_indent = 0 ;
break ;
default :
break ;
}
if ( operation ! = Filepointer : : NONE & & operation ! = Filepointer : : UNIMPORTANT ) {
f . op_indent = indent ;
f . lastOperation = operation ;
}
2012-05-22 14:30:22 +02:00
}
}
2019-07-16 08:37:26 +02:00
for ( std : : map < int , Filepointer > : : iterator i = filepointers . begin ( ) ; i ! = filepointers . end ( ) ; + + i ) {
2012-12-08 09:26:10 +01:00
i - > second . op_indent = 0 ;
2014-03-17 11:02:03 +01:00
i - > second . mode = UNKNOWN_OM ;
2012-12-08 09:26:10 +01:00
i - > second . lastOperation = Filepointer : : UNKNOWN_OP ;
}
2012-05-20 11:57:07 +02:00
}
}
void CheckIO : : fflushOnInputStreamError ( const Token * tok , const std : : string & varname )
{
2014-08-31 20:56:05 +02:00
reportError ( tok , Severity : : portability ,
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
2016-04-12 19:29:40 +02:00
" fflushOnInputStream " , " fflush() called on input stream ' " + varname + " ' may result in undefined behaviour on non-linux systems. " , CWE398 , false ) ;
2012-05-22 14:30:22 +02:00
}
void CheckIO : : ioWithoutPositioningError ( const Token * tok )
{
reportError ( tok , Severity : : error ,
2016-01-25 20:01:48 +01:00
" IOWithoutPositioning " , " Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour. " , CWE664 , false ) ;
2012-05-20 11:57:07 +02:00
}
2012-05-22 14:30:22 +02:00
void CheckIO : : readWriteOnlyFileError ( const Token * tok )
{
reportError ( tok , Severity : : error ,
2016-01-25 20:01:48 +01:00
" readWriteOnlyFile " , " Read operation on a file that was opened only for writing. " , CWE664 , false ) ;
2012-05-22 14:30:22 +02:00
}
void CheckIO : : writeReadOnlyFileError ( const Token * tok )
{
reportError ( tok , Severity : : error ,
2016-01-25 20:01:48 +01:00
" writeReadOnlyFile " , " Write operation on a file that was opened only for reading. " , CWE664 , false ) ;
2012-05-22 14:30:22 +02:00
}
void CheckIO : : useClosedFileError ( const Token * tok )
{
reportError ( tok , Severity : : error ,
2016-01-25 20:01:48 +01:00
" useClosedFile " , " Used file that is not opened. " , CWE910 , false ) ;
2012-05-22 14:30:22 +02:00
}
2014-03-17 11:02:03 +01:00
void CheckIO : : seekOnAppendedFileError ( const Token * tok )
{
reportError ( tok , Severity : : warning ,
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
2016-04-12 19:29:40 +02:00
" seekOnAppendedFile " , " Repositioning operation performed on a file opened in append mode has no effect. " , CWE398 , false ) ;
2014-03-17 11:02:03 +01:00
}
2012-05-22 14:30:22 +02:00
2012-05-20 11:57:07 +02:00
//---------------------------------------------------------------------------
// scanf without field width limits can crash with huge input data
//---------------------------------------------------------------------------
void CheckIO : : invalidScanf ( )
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2012-05-20 11:57:07 +02:00
return ;
2012-07-07 13:34:37 +02:00
2018-06-16 16:10:28 +02:00
const SymbolDatabase * const symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
2018-07-14 10:02:32 +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-02-16 11:47:52 +01:00
const Token * formatToken = nullptr ;
2012-10-30 15:52:45 +01:00
if ( Token : : Match ( tok , " scanf|vscanf ( %str% , " ) )
formatToken = tok - > tokAt ( 2 ) ;
else if ( Token : : Match ( tok , " sscanf|vsscanf|fscanf|vfscanf ( " ) ) {
const Token * nextArg = tok - > tokAt ( 2 ) - > nextArgument ( ) ;
2015-08-14 20:46:13 +02:00
if ( nextArg & & nextArg - > tokType ( ) = = Token : : eString )
2012-10-30 15:52:45 +01:00
formatToken = nextArg ;
else
continue ;
} else
2012-05-20 11:57:07 +02:00
continue ;
2012-10-30 15:52:45 +01:00
bool format = false ;
2012-05-20 11:57:07 +02:00
2013-11-09 16:07:28 +01:00
// scan the string backwards, so we do not need to keep states
2012-10-30 15:52:45 +01:00
const std : : string & formatstr ( formatToken - > str ( ) ) ;
2014-07-07 21:25:30 +02:00
for ( std : : size_t i = 1 ; i < formatstr . length ( ) ; i + + ) {
2012-10-30 15:52:45 +01:00
if ( formatstr [ i ] = = ' % ' )
format = ! format ;
2012-05-20 11:57:07 +02:00
2012-10-30 15:52:45 +01:00
else if ( ! format )
continue ;
2012-05-20 11:57:07 +02:00
2012-10-30 15:52:45 +01:00
else if ( std : : isdigit ( formatstr [ i ] ) | | formatstr [ i ] = = ' * ' ) {
format = false ;
}
2012-05-20 11:57:07 +02:00
2014-03-18 21:41:47 +01:00
else if ( std : : isalpha ( ( unsigned char ) formatstr [ i ] ) | | formatstr [ i ] = = ' [ ' ) {
2015-08-14 08:03:46 +02:00
if ( formatstr [ i ] = = ' s ' | | formatstr [ i ] = = ' [ ' | | formatstr [ i ] = = ' S ' | | ( formatstr [ i ] = = ' l ' & & formatstr [ i + 1 ] = = ' s ' ) ) // #3490 - field width limits are only necessary for string input
invalidScanfError ( tok ) ;
2012-10-30 15:52:45 +01:00
format = false ;
}
2012-05-20 11:57:07 +02:00
}
}
}
}
2015-08-14 08:03:46 +02:00
void CheckIO : : invalidScanfError ( const Token * tok )
2012-05-20 11:57:07 +02:00
{
2018-04-04 21:51:31 +02:00
const std : : string fname = ( tok ? tok - > str ( ) : std : : string ( " scanf " ) ) ;
2015-08-14 12:50:45 +02:00
reportError ( tok , Severity : : warning ,
2016-05-26 21:10:50 +02:00
" invalidscanf " , fname + " () without field width limits can crash with huge input data. \n " +
fname + " () without field width limits can crash with huge input data. Add a field width "
2016-08-01 18:42:27 +02:00
" specifier to fix this problem. \n "
2015-08-14 12:50:45 +02:00
" \n "
" Sample program that can crash: \n "
" \n "
" #include <stdio.h> \n "
" int main() \n "
" { \n "
" char c[5]; \n "
" scanf( \" %s \" , c); \n "
" return 0; \n "
" } \n "
" \n "
" Typing in 5 or more characters may make the program crash. The correct usage "
" here is 'scanf( \" %4s \" , c);', as the maximum field width does not include the "
" terminating null byte. \n "
" Source: http://linux.die.net/man/3/scanf \n "
CWE mapping of invalidscanf, invalidScanfArgType_s, invalidScanfArgType_int, invalidScanfArgType_float, invalidPrintfArgType_s, invalidPrintfArgType_n, invalidPrintfArgType_p, invalidPrintfArgType_int, invalidPrintfArgType_uint, invalidPrintfArgType_sint, invalidPrintfArgType_float, wrongPrintfScanfParameterPositionError
2016-07-31 11:49:36 +02:00
" Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c " ,
2016-08-01 13:01:01 +02:00
CWE119 , false ) ;
2012-05-20 11:57:07 +02:00
}
//---------------------------------------------------------------------------
// printf("%u", "xyz"); // Wrong argument type
// printf("%u%s", 1); // Too few arguments
// printf("", 1); // Too much arguments
//---------------------------------------------------------------------------
2013-10-04 17:30:55 +02:00
2019-07-16 08:37:26 +02:00
static bool findFormat ( nonneg int arg , const Token * firstArg ,
2013-10-04 17:30:55 +02:00
const Token * * formatStringTok , const Token * * formatArgTok )
{
const Token * argTok = firstArg ;
2019-07-16 08:37:26 +02:00
for ( int i = 0 ; i < arg & & argTok ; + + i )
2013-10-04 17:30:55 +02:00
argTok = argTok - > nextArgument ( ) ;
if ( Token : : Match ( argTok , " %str% [,)] " ) ) {
* formatArgTok = argTok - > nextArgument ( ) ;
* formatStringTok = argTok ;
return true ;
} else if ( Token : : Match ( argTok , " %var% [,)] " ) & &
2015-01-31 10:50:39 +01:00
argTok - > variable ( ) & &
Token : : Match ( argTok - > variable ( ) - > typeStartToken ( ) , " char|wchar_t " ) & &
( argTok - > variable ( ) - > isPointer ( ) | |
( argTok - > variable ( ) - > dimensions ( ) . size ( ) = = 1 & &
argTok - > variable ( ) - > dimensionKnown ( 0 ) & &
argTok - > variable ( ) - > dimension ( 0 ) ! = 0 ) ) ) {
2013-10-04 17:30:55 +02:00
* formatArgTok = argTok - > nextArgument ( ) ;
2017-10-02 17:03:55 +02:00
if ( ! argTok - > values ( ) . empty ( ) ) {
2018-12-17 06:07:34 +01:00
std : : list < ValueFlow : : Value > : : const_iterator value = std : : find_if (
argTok - > values ( ) . begin ( ) , argTok - > values ( ) . end ( ) , std : : mem_fn ( & ValueFlow : : Value : : isTokValue ) ) ;
if ( value ! = argTok - > values ( ) . end ( ) & & value - > isTokValue ( ) & & value - > tokvalue & &
value - > tokvalue - > tokType ( ) = = Token : : eString ) {
* formatStringTok = value - > tokvalue ;
2017-10-02 17:03:55 +02:00
}
}
2013-10-04 17:30:55 +02:00
return true ;
}
return false ;
}
2014-05-27 19:15:15 +02:00
// Utility function returning whether iToTest equals iTypename or iOptionalPrefix+iTypename
2015-01-05 01:20:33 +01:00
static inline bool typesMatch ( const std : : string & iToTest , const std : : string & iTypename , const std : : string & iOptionalPrefix = " std:: " )
2014-05-27 19:15:15 +02:00
{
return ( iToTest = = iTypename ) | | ( iToTest = = iOptionalPrefix + iTypename ) ;
}
2012-05-20 11:57:07 +02:00
void CheckIO : : checkWrongPrintfScanfArguments ( )
{
2018-06-16 16:10:28 +02:00
const SymbolDatabase * symbolDatabase = mTokenizer - > getSymbolDatabase ( ) ;
const bool isWindows = mSettings - > isWindowsPlatform ( ) ;
2012-05-20 11:57:07 +02:00
2018-07-14 10:02:32 +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 ( ) ) {
2012-10-30 15:52:45 +01:00
if ( ! tok - > isName ( ) ) continue ;
2017-07-28 15:20:43 +02:00
const Token * argListTok = nullptr ; // Points to first va_list argument
const Token * formatStringTok = nullptr ; // Points to format string token
2012-10-30 15:52:45 +01:00
2014-01-12 12:44:24 +01:00
bool scan = false ;
bool scanf_s = false ;
int formatStringArgNo = - 1 ;
2018-06-16 16:10:28 +02:00
if ( tok - > strAt ( 1 ) = = " ( " & & mSettings - > library . formatstr_function ( tok ) ) {
formatStringArgNo = mSettings - > library . formatstr_argno ( tok ) ;
scan = mSettings - > library . formatstr_scan ( tok ) ;
scanf_s = mSettings - > library . formatstr_secure ( tok ) ;
2013-07-22 20:21:45 +02:00
}
2014-01-12 12:44:24 +01:00
if ( formatStringArgNo > = 0 ) {
// formatstring found in library. Find format string and first argument belonging to format string.
2019-07-16 08:37:26 +02:00
if ( ! findFormat ( formatStringArgNo , tok - > tokAt ( 2 ) , & formatStringTok , & argListTok ) )
2014-01-12 12:44:24 +01:00
continue ;
2017-09-28 16:59:55 +02:00
} else if ( Token : : simpleMatch ( tok , " swprintf ( " ) ) {
if ( Token : : Match ( tok - > tokAt ( 2 ) - > nextArgument ( ) , " %str% " ) ) {
// Find third parameter and format string
if ( ! findFormat ( 1 , tok - > tokAt ( 2 ) , & formatStringTok , & argListTok ) )
continue ;
} else {
// Find fourth parameter and format string
if ( ! findFormat ( 2 , tok - > tokAt ( 2 ) , & formatStringTok , & argListTok ) )
continue ;
}
2015-04-10 14:18:52 +02:00
} else if ( isWindows & & Token : : Match ( tok , " sprintf_s|swprintf_s ( " ) ) {
2013-10-04 17:30:55 +02:00
// template <size_t size> int sprintf_s(char (&buffer)[size], const char *format, ...);
if ( findFormat ( 1 , tok - > tokAt ( 2 ) , & formatStringTok , & argListTok ) ) {
if ( ! formatStringTok )
continue ;
}
// int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...);
else if ( findFormat ( 2 , tok - > tokAt ( 2 ) , & formatStringTok , & argListTok ) ) {
if ( ! formatStringTok )
continue ;
2012-10-30 15:52:45 +01:00
}
2015-04-10 14:18:52 +02:00
} else if ( isWindows & & Token : : Match ( tok , " _snprintf_s|_snwprintf_s ( " ) ) {
2013-10-04 17:30:55 +02:00
// template <size_t size> int _snprintf_s(char (&buffer)[size], size_t count, const char *format, ...);
if ( findFormat ( 2 , tok - > tokAt ( 2 ) , & formatStringTok , & argListTok ) ) {
if ( ! formatStringTok )
continue ;
2013-09-30 19:55:21 +02:00
}
2013-10-04 17:30:55 +02:00
// int _snprintf_s(char *buffer, size_t sizeOfBuffer, size_t count, const char *format, ...);
else if ( findFormat ( 3 , tok - > tokAt ( 2 ) , & formatStringTok , & argListTok ) ) {
if ( ! formatStringTok )
continue ;
2013-09-30 19:55:21 +02:00
}
2012-05-20 11:57:07 +02:00
} else {
continue ;
}
2015-08-07 16:16:41 +02:00
if ( ! formatStringTok )
2013-10-05 07:47:52 +02:00
continue ;
2013-10-04 17:30:55 +02:00
2015-10-10 20:08:15 +02:00
checkFormatString ( tok , formatStringTok , argListTok , scan , scanf_s ) ;
}
}
}
void CheckIO : : checkFormatString ( const Token * const tok ,
const Token * const formatStringTok ,
const Token * argListTok ,
const bool scan ,
const bool scanf_s )
{
2018-06-16 16:10:28 +02:00
const bool printWarning = mSettings - > isEnabled ( Settings : : WARNING ) ;
2015-10-10 20:08:15 +02:00
const std : : string & formatString = formatStringTok - > str ( ) ;
// Count format string parameters..
2019-07-16 08:37:26 +02:00
int numFormat = 0 ;
int numSecure = 0 ;
2015-10-10 20:08:15 +02:00
bool percent = false ;
const Token * argListTok2 = argListTok ;
2019-07-16 08:37:26 +02:00
std : : set < int > parameterPositionsUsed ;
2015-10-10 20:08:15 +02:00
for ( std : : string : : const_iterator i = formatString . begin ( ) ; i ! = formatString . end ( ) ; + + i ) {
if ( * i = = ' % ' ) {
percent = ! percent ;
} else if ( percent & & * i = = ' [ ' ) {
while ( i ! = formatString . end ( ) ) {
if ( * i = = ' ] ' ) {
numFormat + + ;
if ( argListTok )
argListTok = argListTok - > nextArgument ( ) ;
percent = false ;
break ;
}
+ + i ;
}
if ( scanf_s ) {
numSecure + + ;
if ( argListTok ) {
argListTok = argListTok - > nextArgument ( ) ;
}
}
if ( i = = formatString . end ( ) )
break ;
} else if ( percent ) {
percent = false ;
bool _continue = false ;
bool skip = false ;
std : : string width ;
2019-07-16 08:37:26 +02:00
int parameterPosition = 0 ;
2015-10-10 20:08:15 +02:00
bool hasParameterPosition = false ;
while ( i ! = formatString . end ( ) & & * i ! = ' [ ' & & ! std : : isalpha ( ( unsigned char ) * i ) ) {
if ( * i = = ' * ' ) {
skip = true ;
if ( scan )
_continue = true ;
else {
numFormat + + ;
if ( argListTok )
argListTok = argListTok - > nextArgument ( ) ;
}
} else if ( std : : isdigit ( * i ) ) {
width + = * i ;
} else if ( * i = = ' $ ' ) {
2019-07-16 08:37:26 +02:00
parameterPosition = std : : atoi ( width . c_str ( ) ) ;
2015-10-10 20:08:15 +02:00
hasParameterPosition = true ;
width . clear ( ) ;
}
+ + i ;
}
if ( i ! = formatString . end ( ) & & * i = = ' [ ' ) {
while ( i ! = formatString . end ( ) ) {
if ( * i = = ' ] ' ) {
if ( ! skip ) {
2012-05-20 11:57:07 +02:00
numFormat + + ;
if ( argListTok )
argListTok = argListTok - > nextArgument ( ) ;
2013-10-01 05:49:44 +02:00
}
2012-10-30 15:52:45 +01:00
break ;
}
2015-10-10 20:08:15 +02:00
+ + i ;
}
if ( scanf_s & & ! skip ) {
numSecure + + ;
if ( argListTok ) {
argListTok = argListTok - > nextArgument ( ) ;
2013-10-01 05:49:44 +02:00
}
2015-10-10 20:08:15 +02:00
}
_continue = true ;
}
if ( i = = formatString . end ( ) )
break ;
if ( _continue )
continue ;
2013-07-28 16:13:16 +02:00
2015-10-10 20:08:15 +02:00
if ( scan | | * i ! = ' m ' ) { // %m is a non-standard extension that requires no parameter on print functions.
+ + numFormat ;
2012-05-20 11:57:07 +02:00
2015-10-10 20:08:15 +02:00
// Handle parameter positions (POSIX extension) - Ticket #4900
if ( hasParameterPosition ) {
if ( parameterPositionsUsed . find ( parameterPosition ) = = parameterPositionsUsed . end ( ) )
parameterPositionsUsed . insert ( parameterPosition ) ;
else // Parameter already referenced, hence don't consider it a new format
- - numFormat ;
}
2012-10-30 15:52:45 +01:00
2015-10-10 20:08:15 +02:00
// Perform type checks
2018-06-16 16:10:28 +02:00
ArgumentInfo argInfo ( argListTok , mSettings , mTokenizer - > isCPP ( ) ) ;
2015-10-10 20:08:15 +02:00
2018-06-16 16:10:28 +02:00
if ( argInfo . typeToken & & ! argInfo . isLibraryType ( mSettings ) ) {
2015-10-10 20:08:15 +02:00
if ( scan ) {
std : : string specifier ;
bool done = false ;
while ( ! done ) {
switch ( * i ) {
case ' s ' :
specifier + = * i ;
if ( argInfo . variableInfo & & argInfo . isKnownType ( ) & & argInfo . variableInfo - > isArray ( ) & & ( argInfo . variableInfo - > dimensions ( ) . size ( ) = = 1 ) & & argInfo . variableInfo - > dimensions ( ) [ 0 ] . known ) {
if ( ! width . empty ( ) ) {
2018-04-04 21:51:31 +02:00
const int numWidth = std : : atoi ( width . c_str ( ) ) ;
2015-10-10 20:08:15 +02:00
if ( numWidth ! = ( argInfo . variableInfo - > dimension ( 0 ) - 1 ) )
2016-05-22 14:00:26 +02:00
invalidScanfFormatWidthError ( tok , numFormat , numWidth , argInfo . variableInfo , ' s ' ) ;
2015-10-10 20:08:15 +02:00
}
}
if ( argListTok & & argListTok - > tokType ( ) ! = Token : : eString & &
argInfo . isKnownType ( ) & & argInfo . isArrayOrPointer ( ) & &
( ! Token : : Match ( argInfo . typeToken , " char|wchar_t " ) | |
argInfo . typeToken - > strAt ( - 1 ) = = " const " ) ) {
if ( ! ( argInfo . isArrayOrPointer ( ) & & argInfo . element & & ! argInfo . typeToken - > isStandardType ( ) ) )
invalidScanfArgTypeError_s ( tok , numFormat , specifier , & argInfo ) ;
}
if ( scanf_s ) {
numSecure + + ;
if ( argListTok ) {
argListTok = argListTok - > nextArgument ( ) ;
}
}
done = true ;
break ;
case ' c ' :
2016-05-22 14:00:26 +02:00
if ( argInfo . variableInfo & & argInfo . isKnownType ( ) & & argInfo . variableInfo - > isArray ( ) & & ( argInfo . variableInfo - > dimensions ( ) . size ( ) = = 1 ) & & argInfo . variableInfo - > dimensions ( ) [ 0 ] . known ) {
if ( ! width . empty ( ) ) {
2018-04-04 21:51:31 +02:00
const int numWidth = std : : atoi ( width . c_str ( ) ) ;
2016-05-22 14:00:26 +02:00
if ( numWidth > argInfo . variableInfo - > dimension ( 0 ) )
invalidScanfFormatWidthError ( tok , numFormat , numWidth , argInfo . variableInfo , ' c ' ) ;
}
}
2015-10-10 20:08:15 +02:00
if ( scanf_s ) {
numSecure + + ;
if ( argListTok ) {
argListTok = argListTok - > nextArgument ( ) ;
}
}
done = true ;
break ;
case ' x ' :
case ' X ' :
2017-11-09 14:35:09 +01:00
case ' u ' :
2015-10-10 20:08:15 +02:00
case ' o ' :
specifier + = * i ;
if ( argInfo . typeToken - > tokType ( ) = = Token : : eString )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
else if ( argInfo . isKnownType ( ) ) {
if ( ! Token : : Match ( argInfo . typeToken , " char|short|int|long " ) ) {
if ( argInfo . typeToken - > isStandardType ( ) | | ! argInfo . element )
2013-09-22 06:56:31 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
2017-10-25 20:22:17 +02:00
} else if ( ! argInfo . typeToken - > isUnsigned ( ) | |
! argInfo . isArrayOrPointer ( ) | |
2015-10-10 20:08:15 +02:00
argInfo . typeToken - > strAt ( - 1 ) = = " const " ) {
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
} else {
switch ( specifier [ 0 ] ) {
case ' h ' :
if ( specifier [ 1 ] = = ' h ' ) {
if ( argInfo . typeToken - > str ( ) ! = " char " )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
} else if ( argInfo . typeToken - > str ( ) ! = " short " )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
break ;
case ' l ' :
if ( specifier [ 1 ] = = ' l ' ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
2017-11-09 09:28:07 +01:00
typesMatch ( argInfo . typeToken - > originalName ( ) , " uintmax_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
} else if ( argInfo . typeToken - > str ( ) ! = " long " | | argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
2017-11-09 13:38:48 +01:00
typesMatch ( argInfo . typeToken - > originalName ( ) , " uintmax_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
break ;
case ' I ' :
if ( specifier . find ( " I64 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
2013-10-27 10:48:49 +01:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
2015-10-10 20:08:15 +02:00
} else if ( specifier . find ( " I32 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " int " | | argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
2017-11-08 13:06:51 +01:00
} else if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
break ;
case ' j ' :
2017-11-20 20:13:20 +01:00
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " uintmax_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
break ;
case ' z ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
break ;
case ' t ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
break ;
case ' L ' :
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
2013-09-22 06:56:31 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
2017-11-08 21:52:49 +01:00
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
2017-11-17 21:58:09 +01:00
typesMatch ( argInfo . typeToken - > originalName ( ) , " uintmax_t " ) )
2017-11-08 21:52:49 +01:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
2015-10-10 20:08:15 +02:00
break ;
default :
2017-11-07 22:53:18 +01:00
if ( argInfo . typeToken - > str ( ) ! = " int " )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
2017-11-09 13:38:48 +01:00
typesMatch ( argInfo . typeToken - > originalName ( ) , " uintmax_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , true ) ;
break ;
2013-09-22 06:56:31 +02:00
}
2015-10-10 20:08:15 +02:00
}
}
done = true ;
break ;
case ' n ' :
case ' d ' :
case ' i ' :
specifier + = * i ;
if ( argInfo . typeToken - > tokType ( ) = = Token : : eString )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
else if ( argInfo . isKnownType ( ) ) {
if ( ! Token : : Match ( argInfo . typeToken , " char|short|int|long " ) ) {
if ( argInfo . typeToken - > isStandardType ( ) | | ! argInfo . element )
2013-09-22 06:56:31 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
2015-10-10 20:08:15 +02:00
} else if ( argInfo . typeToken - > isUnsigned ( ) | |
! argInfo . isArrayOrPointer ( ) | |
argInfo . typeToken - > strAt ( - 1 ) = = " const " ) {
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
} else {
switch ( specifier [ 0 ] ) {
case ' h ' :
if ( specifier [ 1 ] = = ' h ' ) {
if ( argInfo . typeToken - > str ( ) ! = " char " )
2013-10-27 10:48:49 +01:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
2015-10-10 20:08:15 +02:00
} else if ( argInfo . typeToken - > str ( ) ! = " short " )
2013-09-22 06:56:31 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
2015-10-10 20:08:15 +02:00
break ;
case ' l ' :
if ( specifier [ 1 ] = = ' l ' ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
2017-11-20 21:07:37 +01:00
typesMatch ( argInfo . typeToken - > originalName ( ) , " intmax_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
} else if ( argInfo . typeToken - > str ( ) ! = " long " | | argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
2017-11-20 21:07:37 +01:00
typesMatch ( argInfo . typeToken - > originalName ( ) , " intmax_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
break ;
case ' I ' :
if ( specifier . find ( " I64 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
} else if ( specifier . find ( " I32 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " int " | | argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
} else if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
break ;
case ' j ' :
2017-11-20 20:31:13 +01:00
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " intmax_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
break ;
case ' z ' :
2017-11-08 12:22:03 +01:00
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ssize_t " ) )
2015-10-10 20:08:15 +02:00
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
break ;
case ' t ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
break ;
case ' L ' :
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
break ;
default :
if ( argInfo . typeToken - > str ( ) ! = " int " )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
argInfo . typeToken - > originalName ( ) = = " intmax_t " )
invalidScanfArgTypeError_int ( tok , numFormat , specifier , & argInfo , false ) ;
break ;
2013-09-22 06:56:31 +02:00
}
2015-10-10 20:08:15 +02:00
}
}
done = true ;
break ;
case ' e ' :
case ' E ' :
case ' f ' :
case ' g ' :
case ' G ' :
case ' a ' :
specifier + = * i ;
if ( argInfo . typeToken - > tokType ( ) = = Token : : eString )
invalidScanfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
else if ( argInfo . isKnownType ( ) ) {
if ( ! Token : : Match ( argInfo . typeToken , " float|double " ) ) {
if ( argInfo . typeToken - > isStandardType ( ) )
2013-09-22 06:56:31 +02:00
invalidScanfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( ! argInfo . isArrayOrPointer ( ) | |
argInfo . typeToken - > strAt ( - 1 ) = = " const " ) {
invalidScanfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
} else {
switch ( specifier [ 0 ] ) {
case ' l ' :
2017-11-07 20:57:44 +01:00
if ( argInfo . typeToken - > str ( ) ! = " double " | | argInfo . typeToken - > isLong ( ) )
2013-09-22 06:56:31 +02:00
invalidScanfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
case ' L ' :
if ( argInfo . typeToken - > str ( ) ! = " double " | | ! argInfo . typeToken - > isLong ( ) )
invalidScanfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
break ;
default :
if ( argInfo . typeToken - > str ( ) ! = " float " )
invalidScanfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
break ;
2013-09-22 06:56:31 +02:00
}
2012-10-30 15:52:45 +01:00
}
2012-06-23 07:52:52 +02:00
}
2015-10-10 20:08:15 +02:00
done = true ;
break ;
case ' I ' :
if ( ( i + 1 ! = formatString . end ( ) & & * ( i + 1 ) = = ' 6 ' & &
i + 2 ! = formatString . end ( ) & & * ( i + 2 ) = = ' 4 ' ) | |
( i + 1 ! = formatString . end ( ) & & * ( i + 1 ) = = ' 3 ' & &
i + 2 ! = formatString . end ( ) & & * ( i + 2 ) = = ' 2 ' ) ) {
specifier + = * i + + ;
specifier + = * i + + ;
if ( ( i + 1 ) ! = formatString . end ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
specifier + = * i ;
invalidLengthModifierError ( tok , numFormat , specifier ) ;
2013-07-21 08:35:01 +02:00
done = true ;
2015-10-10 20:08:15 +02:00
} else {
specifier + = * i + + ;
}
} else {
if ( ( i + 1 ) ! = formatString . end ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
2013-08-10 12:33:55 +02:00
specifier + = * i ;
2015-10-10 20:08:15 +02:00
invalidLengthModifierError ( tok , numFormat , specifier ) ;
done = true ;
} else {
specifier + = * i + + ;
}
}
break ;
case ' h ' :
case ' l ' :
if ( i + 1 ! = formatString . end ( ) & & * ( i + 1 ) = = * i )
specifier + = * i + + ;
2015-12-17 15:40:54 +01:00
// fallthrough
2015-10-10 20:08:15 +02:00
case ' j ' :
case ' q ' :
case ' t ' :
case ' z ' :
case ' L ' :
// Expect an alphabetical character after these specifiers
if ( i ! = formatString . end ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
specifier + = * i ;
invalidLengthModifierError ( tok , numFormat , specifier ) ;
done = true ;
} else {
specifier + = * i + + ;
}
break ;
default :
done = true ;
break ;
}
}
2017-11-03 10:39:57 +01:00
} else if ( printWarning ) {
2015-10-10 20:08:15 +02:00
std : : string specifier ;
bool done = false ;
while ( ! done ) {
switch ( * i ) {
case ' s ' :
if ( argListTok - > tokType ( ) ! = Token : : eString & &
argInfo . isKnownType ( ) & & ! argInfo . isArrayOrPointer ( ) ) {
if ( ! Token : : Match ( argInfo . typeToken , " char|wchar_t " ) ) {
if ( ! ( ! argInfo . isArrayOrPointer ( ) & & argInfo . element ) )
invalidPrintfArgTypeError_s ( tok , numFormat , & argInfo ) ;
}
}
done = true ;
break ;
case ' n ' :
if ( ( argInfo . isKnownType ( ) & & ( ! argInfo . isArrayOrPointer ( ) | | argInfo . typeToken - > strAt ( - 1 ) = = " const " ) ) | | argListTok - > tokType ( ) = = Token : : eString )
invalidPrintfArgTypeError_n ( tok , numFormat , & argInfo ) ;
done = true ;
break ;
case ' c ' :
case ' x ' :
case ' X ' :
case ' o ' :
specifier + = * i ;
if ( argInfo . typeToken - > tokType ( ) = = Token : : eString )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2017-11-07 20:14:29 +01:00
else if ( argInfo . isArrayOrPointer ( ) & & ! argInfo . element ) {
// use %p on pointers and arrays
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2017-11-07 20:14:29 +01:00
} else if ( argInfo . isKnownType ( ) ) {
if ( ! Token : : Match ( argInfo . typeToken , " bool|short|long|int|char|wchar_t " ) ) {
2015-10-10 20:08:15 +02:00
if ( ! ( ! argInfo . isArrayOrPointer ( ) & & argInfo . element ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else {
switch ( specifier [ 0 ] ) {
2016-08-24 12:47:11 +02:00
case ' h ' :
if ( specifier [ 1 ] = = ' h ' ) {
2017-11-21 20:14:48 +01:00
if ( ! ( argInfo . typeToken - > str ( ) = = " char " & & argInfo . typeToken - > isUnsigned ( ) ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2017-11-21 20:17:02 +01:00
} else if ( ! ( argInfo . typeToken - > str ( ) = = " short " & & argInfo . typeToken - > isUnsigned ( ) ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2016-08-24 12:47:11 +02:00
break ;
2015-10-10 20:08:15 +02:00
case ' l ' :
if ( specifier [ 1 ] = = ' l ' ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2017-11-20 20:56:33 +01:00
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
argInfo . typeToken - > originalName ( ) = = " uintmax_t " )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( argInfo . typeToken - > str ( ) ! = " long " | | argInfo . typeToken - > isLong ( ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2017-11-20 20:56:33 +01:00
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
2017-11-27 23:32:20 +01:00
argInfo . typeToken - > originalName ( ) = = " uintmax_t " )
2017-11-20 20:56:33 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
case ' j ' :
2017-11-17 22:00:02 +01:00
if ( argInfo . typeToken - > originalName ( ) ! = " uintmax_t " )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
case ' z ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
case ' t ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
case ' I ' :
if ( specifier . find ( " I64 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( specifier . find ( " I32 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " int " | | argInfo . typeToken - > isLong ( ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( ! ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
argInfo . typeToken - > originalName ( ) = = " WPARAM " | |
argInfo . typeToken - > originalName ( ) = = " UINT_PTR " | |
argInfo . typeToken - > originalName ( ) = = " LONG_PTR " | |
argInfo . typeToken - > originalName ( ) = = " LPARAM " | |
argInfo . typeToken - > originalName ( ) = = " LRESULT " ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
default :
if ( ! Token : : Match ( argInfo . typeToken , " bool|char|short|wchar_t|int " ) )
2017-11-14 19:46:17 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
2013-07-21 08:35:01 +02:00
}
2015-10-10 20:08:15 +02:00
}
}
done = true ;
break ;
case ' d ' :
case ' i ' :
specifier + = * i ;
if ( argInfo . typeToken - > tokType ( ) = = Token : : eString ) {
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
2017-11-07 20:14:29 +01:00
} else if ( argInfo . isArrayOrPointer ( ) & & ! argInfo . element ) {
// use %p on pointers and arrays
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( argInfo . isKnownType ( ) ) {
2017-11-07 20:14:29 +01:00
if ( argInfo . typeToken - > isUnsigned ( ) & & ! Token : : Match ( argInfo . typeToken , " char|short " ) ) {
2015-10-10 20:08:15 +02:00
if ( ! ( ! argInfo . isArrayOrPointer ( ) & & argInfo . element ) )
2013-09-05 01:46:58 +02:00
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( ! Token : : Match ( argInfo . typeToken , " bool|char|short|int|long " ) ) {
if ( ! ( ! argInfo . isArrayOrPointer ( ) & & argInfo . element ) )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
} else {
switch ( specifier [ 0 ] ) {
2016-08-24 12:47:11 +02:00
case ' h ' :
if ( specifier [ 1 ] = = ' h ' ) {
if ( ! ( argInfo . typeToken - > str ( ) = = " char " & & ! argInfo . typeToken - > isUnsigned ( ) ) )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
} else if ( ! ( argInfo . typeToken - > str ( ) = = " short " & & ! argInfo . typeToken - > isUnsigned ( ) ) )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2015-10-10 20:08:15 +02:00
case ' l ' :
if ( specifier [ 1 ] = = ' l ' ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
argInfo . typeToken - > originalName ( ) = = " intmax_t " )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
} else if ( argInfo . typeToken - > str ( ) ! = " long " | | argInfo . typeToken - > isLong ( ) )
2013-09-08 20:21:00 +02:00
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
argInfo . typeToken - > originalName ( ) = = " intmax_t " )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
break ;
case ' j ' :
if ( argInfo . typeToken - > originalName ( ) ! = " intmax_t " )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
break ;
case ' t ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
break ;
case ' I ' :
if ( specifier . find ( " I64 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
2013-10-27 10:48:49 +01:00
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( specifier . find ( " I32 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " int " | | argInfo . typeToken - > isLong ( ) )
2013-10-27 10:48:49 +01:00
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
break ;
case ' z ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ssize_t " ) )
2017-10-27 21:21:18 +02:00
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
2016-07-16 10:33:46 +02:00
case ' L ' :
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2015-10-10 20:08:15 +02:00
default :
if ( ! Token : : Match ( argInfo . typeToken , " bool|char|short|int " ) )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) | |
argInfo . typeToken - > originalName ( ) = = " intmax_t " )
invalidPrintfArgTypeError_sint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2013-07-21 08:35:01 +02:00
}
2015-10-10 20:08:15 +02:00
}
}
done = true ;
break ;
case ' u ' :
specifier + = * i ;
if ( argInfo . typeToken - > tokType ( ) = = Token : : eString ) {
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2017-11-07 20:14:29 +01:00
} else if ( argInfo . isArrayOrPointer ( ) & & ! argInfo . element ) {
// use %p on pointers and arrays
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( argInfo . isKnownType ( ) ) {
2017-11-07 20:14:29 +01:00
if ( ! argInfo . typeToken - > isUnsigned ( ) & & ! Token : : Match ( argInfo . typeToken , " bool|_Bool " ) ) {
2015-10-10 20:08:15 +02:00
if ( ! ( ! argInfo . isArrayOrPointer ( ) & & argInfo . element ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
} else if ( ! Token : : Match ( argInfo . typeToken , " bool|char|short|long|int " ) ) {
if ( ! ( ! argInfo . isArrayOrPointer ( ) & & argInfo . element ) )
2013-09-05 01:46:58 +02:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else {
switch ( specifier [ 0 ] ) {
2016-08-24 12:47:11 +02:00
case ' h ' :
if ( specifier [ 1 ] = = ' h ' ) {
if ( ! ( argInfo . typeToken - > str ( ) = = " char " & & argInfo . typeToken - > isUnsigned ( ) ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
} else if ( ! ( argInfo . typeToken - > str ( ) = = " short " & & argInfo . typeToken - > isUnsigned ( ) ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2015-10-10 20:08:15 +02:00
case ' l ' :
if ( specifier [ 1 ] = = ' l ' ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
argInfo . typeToken - > originalName ( ) = = " uintmax_t " )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
} else if ( argInfo . typeToken - > str ( ) ! = " long " | | argInfo . typeToken - > isLong ( ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
argInfo . typeToken - > originalName ( ) = = " uintmax_t " )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
break ;
case ' j ' :
if ( argInfo . typeToken - > originalName ( ) ! = " uintmax_t " )
2013-09-05 01:46:58 +02:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
break ;
case ' z ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2017-11-15 20:40:16 +01:00
case ' t ' :
if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " ptrdiff_t " ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2015-10-10 20:08:15 +02:00
case ' I ' :
if ( specifier . find ( " I64 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
2013-10-27 10:48:49 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( specifier . find ( " I32 " ) ! = std : : string : : npos ) {
if ( argInfo . typeToken - > str ( ) ! = " int " | | argInfo . typeToken - > isLong ( ) )
2013-10-27 10:48:49 +01:00
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( ! typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2016-07-16 10:33:46 +02:00
case ' L ' :
if ( argInfo . typeToken - > str ( ) ! = " long " | | ! argInfo . typeToken - > isLong ( ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2015-10-10 20:08:15 +02:00
default :
if ( ! Token : : Match ( argInfo . typeToken , " bool|char|short|int " ) )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
else if ( typesMatch ( argInfo . typeToken - > originalName ( ) , " size_t " ) | |
argInfo . typeToken - > originalName ( ) = = " intmax_t " )
invalidPrintfArgTypeError_uint ( tok , numFormat , specifier , & argInfo ) ;
break ;
2012-10-30 15:52:45 +01:00
}
2015-10-10 20:08:15 +02:00
}
}
done = true ;
break ;
case ' p ' :
if ( argInfo . typeToken - > tokType ( ) = = Token : : eString )
2017-10-08 10:14:10 +02:00
; // string literals are passed as pointers to literal start, okay
2015-10-10 20:08:15 +02:00
else if ( argInfo . isKnownType ( ) & & ! argInfo . isArrayOrPointer ( ) )
invalidPrintfArgTypeError_p ( tok , numFormat , & argInfo ) ;
done = true ;
break ;
case ' e ' :
case ' E ' :
case ' f ' :
case ' g ' :
case ' G ' :
specifier + = * i ;
if ( argInfo . typeToken - > tokType ( ) = = Token : : eString )
invalidPrintfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
2017-11-07 20:14:29 +01:00
else if ( argInfo . isArrayOrPointer ( ) & & ! argInfo . element ) {
// use %p on pointers and arrays
invalidPrintfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
} else if ( argInfo . isKnownType ( ) ) {
if ( ! Token : : Match ( argInfo . typeToken , " float|double " ) ) {
2015-10-10 20:08:15 +02:00
if ( ! ( ! argInfo . isArrayOrPointer ( ) & & argInfo . element ) )
2013-09-05 01:46:58 +02:00
invalidPrintfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
2015-10-10 20:08:15 +02:00
} else if ( ( specifier [ 0 ] = = ' L ' & & ( ! argInfo . typeToken - > isLong ( ) | | argInfo . typeToken - > str ( ) ! = " double " ) ) | |
( specifier [ 0 ] ! = ' L ' & & argInfo . typeToken - > isLong ( ) ) )
invalidPrintfArgTypeError_float ( tok , numFormat , specifier , & argInfo ) ;
}
done = true ;
break ;
case ' h ' : // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
2018-01-30 08:43:15 +01:00
case ' l ' : { // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int)
2015-10-10 20:08:15 +02:00
// If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
2018-01-30 08:43:15 +01:00
const bool isSecondCharAvailable = ( ( i + 1 ) ! = formatString . end ( ) ) ;
if ( i ! = formatString . end ( ) & & isSecondCharAvailable & & * ( i + 1 ) = = * i ) {
2018-04-08 08:12:56 +02:00
if ( ! isalpha ( * ( i + 2 ) ) ) {
std : : string modifier ;
modifier + = * i ;
modifier + = * ( i + 1 ) ;
invalidLengthModifierError ( tok , numFormat , modifier ) ;
2015-10-10 20:08:15 +02:00
done = true ;
2018-04-08 08:12:56 +02:00
} else {
specifier = * i + + ;
specifier + = * i + + ;
2015-10-10 20:08:15 +02:00
}
} else {
if ( i ! = formatString . end ( ) ) {
2018-01-30 08:43:15 +01:00
if ( ( i + 1 ) ! = formatString . end ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
2015-10-10 20:08:15 +02:00
std : : string modifier ;
modifier + = * i ;
invalidLengthModifierError ( tok , numFormat , modifier ) ;
2013-07-22 08:28:29 +02:00
done = true ;
} else {
2015-10-10 20:08:15 +02:00
specifier = * i + + ;
2012-10-30 15:52:45 +01:00
}
2015-10-10 20:08:15 +02:00
} else {
2013-07-21 08:35:01 +02:00
done = true ;
2012-10-21 08:50:29 +02:00
}
}
2018-01-30 08:43:15 +01:00
}
break ;
2015-10-10 20:08:15 +02:00
case ' I ' : // Microsoft extension: I for size_t and ptrdiff_t, I32 for __int32, and I64 for __int64
if ( ( * ( i + 1 ) = = ' 3 ' & & * ( i + 2 ) = = ' 2 ' ) | |
( * ( i + 1 ) = = ' 6 ' & & * ( i + 2 ) = = ' 4 ' ) ) {
specifier + = * i + + ;
specifier + = * i + + ;
}
2015-12-17 15:40:54 +01:00
// fallthrough
2015-10-10 20:08:15 +02:00
case ' j ' : // intmax_t or uintmax_t
case ' z ' : // size_t
case ' t ' : // ptrdiff_t
case ' L ' : // long double
// Expect an alphabetical character after these specifiers
if ( i ! = formatString . end ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
specifier + = * i ;
invalidLengthModifierError ( tok , numFormat , specifier ) ;
done = true ;
} else {
specifier + = * i + + ;
}
break ;
default :
done = true ;
break ;
2012-05-20 11:57:07 +02:00
}
}
2012-10-30 15:52:45 +01:00
}
2012-05-20 11:57:07 +02:00
}
2015-10-10 20:08:15 +02:00
if ( argListTok )
argListTok = argListTok - > nextArgument ( ) ; // Find next argument
2012-10-30 15:52:45 +01:00
}
2015-10-10 20:08:15 +02:00
}
}
2013-07-28 16:13:16 +02:00
2015-10-10 20:08:15 +02:00
// Count printf/scanf parameters..
2019-07-16 08:37:26 +02:00
int numFunction = 0 ;
2015-10-10 20:08:15 +02:00
while ( argListTok2 ) {
numFunction + + ;
argListTok2 = argListTok2 - > nextArgument ( ) ; // Find next argument
}
2012-05-20 11:57:07 +02:00
2015-10-10 20:08:15 +02:00
if ( printWarning ) {
// Check that all parameter positions reference an actual parameter
2019-07-16 08:37:26 +02:00
for ( int i : parameterPositionsUsed ) {
2018-07-14 10:02:32 +02:00
if ( ( i = = 0 ) | | ( i > numFormat ) )
wrongPrintfScanfPosixParameterPositionError ( tok , tok - > str ( ) , i , numFormat ) ;
2012-10-30 15:52:45 +01:00
}
2012-05-20 11:57:07 +02:00
}
2015-10-10 20:08:15 +02:00
// Mismatching number of parameters => warning
if ( ( numFormat + numSecure ) ! = numFunction )
wrongPrintfScanfArgumentsError ( tok , tok - > originalName ( ) . empty ( ) ? tok - > str ( ) : tok - > originalName ( ) , numFormat + numSecure , numFunction ) ;
2012-05-20 11:57:07 +02:00
}
2013-08-24 07:25:50 +02:00
// We currently only support string literals, variables, and functions.
2013-08-24 22:34:52 +02:00
/// @todo add non-string literals, and generic expressions
2013-08-20 06:16:31 +02:00
2017-04-01 09:31:27 +02:00
CheckIO : : ArgumentInfo : : ArgumentInfo ( const Token * arg , const Settings * settings , bool _isCPP )
2015-06-17 21:25:15 +02:00
: variableInfo ( nullptr )
, typeToken ( nullptr )
, functionInfo ( nullptr )
2015-08-15 19:46:31 +02:00
, tempToken ( nullptr )
2013-09-03 05:50:19 +02:00
, element ( false )
2013-09-08 20:21:00 +02:00
, _template ( false )
2013-09-22 06:56:31 +02:00
, address ( false )
2015-06-17 21:25:15 +02:00
, isCPP ( _isCPP )
2013-08-20 06:16:31 +02:00
{
2017-04-01 09:31:27 +02:00
if ( ! arg )
2015-10-05 19:04:29 +02:00
return ;
2015-10-05 10:12:30 +02:00
// Use AST type info
// TODO: This is a bailout so that old code is used in simple cases. Remove the old code and always use the AST type.
2017-04-01 09:31:27 +02:00
if ( ! Token : : Match ( arg , " %str% ,|) " ) & & ! ( Token : : Match ( arg , " %var% " ) & & arg - > variable ( ) & & arg - > variable ( ) - > isArray ( ) ) ) {
const Token * top = arg ;
while ( top - > astParent ( ) & & top - > astParent ( ) - > str ( ) ! = " , " & & top - > astParent ( ) ! = arg - > previous ( ) )
2016-08-14 17:19:14 +02:00
top = top - > astParent ( ) ;
const ValueType * valuetype = top - > argumentType ( ) ;
2015-10-05 19:59:15 +02:00
if ( valuetype & & valuetype - > type > = ValueType : : Type : : BOOL ) {
2018-05-25 07:15:05 +02:00
typeToken = tempToken = new Token ( ) ;
2017-03-01 22:33:31 +01:00
if ( valuetype - > pointer & & valuetype - > constness & 1 ) {
2015-10-07 19:08:26 +02:00
tempToken - > str ( " const " ) ;
tempToken - > insertToken ( " a " ) ;
tempToken = tempToken - > next ( ) ;
}
2016-05-28 15:36:13 +02:00
if ( valuetype - > type = = ValueType : : BOOL )
2015-10-05 19:59:15 +02:00
tempToken - > str ( " bool " ) ;
else if ( valuetype - > type = = ValueType : : CHAR )
tempToken - > str ( " char " ) ;
else if ( valuetype - > type = = ValueType : : SHORT )
tempToken - > str ( " short " ) ;
2019-05-01 16:34:28 +02:00
else if ( valuetype - > type = = ValueType : : WCHAR_T )
tempToken - > str ( " wchar_t " ) ;
2015-10-05 19:59:15 +02:00
else if ( valuetype - > type = = ValueType : : INT )
2015-10-05 10:12:30 +02:00
tempToken - > str ( " int " ) ;
else if ( valuetype - > type = = ValueType : : LONG )
tempToken - > str ( " long " ) ;
2015-10-05 19:20:42 +02:00
else if ( valuetype - > type = = ValueType : : LONGLONG ) {
2015-10-05 10:12:30 +02:00
tempToken - > str ( " long " ) ;
2015-10-05 19:20:42 +02:00
tempToken - > isLong ( true ) ;
} else if ( valuetype - > type = = ValueType : : FLOAT )
2015-10-05 10:12:30 +02:00
tempToken - > str ( " float " ) ;
else if ( valuetype - > type = = ValueType : : DOUBLE )
tempToken - > str ( " double " ) ;
2015-10-08 19:52:24 +02:00
else if ( valuetype - > type = = ValueType : : LONGDOUBLE ) {
tempToken - > str ( " double " ) ;
tempToken - > isLong ( true ) ;
}
2015-10-05 10:12:30 +02:00
if ( valuetype - > isIntegral ( ) ) {
if ( valuetype - > sign = = ValueType : : Sign : : UNSIGNED )
tempToken - > isUnsigned ( true ) ;
else if ( valuetype - > sign = = ValueType : : Sign : : SIGNED )
tempToken - > isSigned ( true ) ;
}
2015-10-07 20:24:17 +02:00
if ( ! valuetype - > originalTypeName . empty ( ) )
tempToken - > originalName ( valuetype - > originalTypeName ) ;
2019-07-16 08:37:26 +02:00
for ( int p = 0 ; p < valuetype - > pointer ; p + + )
2015-10-05 19:59:15 +02:00
tempToken - > insertToken ( " * " ) ;
2015-10-07 19:08:26 +02:00
tempToken = const_cast < Token * > ( typeToken ) ;
2015-10-05 10:12:30 +02:00
return ;
}
}
2015-10-05 19:04:29 +02:00
2017-04-01 09:31:27 +02:00
if ( arg - > tokType ( ) = = Token : : eString ) {
typeToken = arg ;
2015-10-05 19:04:29 +02:00
return ;
2017-04-01 09:31:27 +02:00
} else if ( arg - > str ( ) = = " & " | | arg - > tokType ( ) = = Token : : eVariable | |
arg - > tokType ( ) = = Token : : eFunction | | Token : : Match ( arg , " %type% :: " ) | |
( Token : : Match ( arg , " static_cast|reinterpret_cast|const_cast < " ) & &
Token : : simpleMatch ( arg - > linkAt ( 1 ) , " > ( " ) & &
Token : : Match ( arg - > linkAt ( 1 ) - > linkAt ( 1 ) , " ) ,|) " ) ) ) {
if ( Token : : Match ( arg , " static_cast|reinterpret_cast|const_cast " ) ) {
typeToken = arg - > tokAt ( 2 ) ;
2015-10-05 19:04:29 +02:00
while ( typeToken - > str ( ) = = " const " | | typeToken - > str ( ) = = " extern " )
typeToken = typeToken - > next ( ) ;
2013-09-03 05:50:19 +02:00
return ;
2015-10-05 19:04:29 +02:00
}
2017-04-01 09:31:27 +02:00
if ( arg - > str ( ) = = " & " ) {
2015-10-05 19:04:29 +02:00
address = true ;
2017-04-01 09:31:27 +02:00
arg = arg - > next ( ) ;
2015-10-05 19:04:29 +02:00
}
2017-04-01 09:31:27 +02:00
while ( Token : : Match ( arg , " %type% :: " ) )
arg = arg - > tokAt ( 2 ) ;
if ( ! arg | | ! ( arg - > tokType ( ) = = Token : : eVariable | | arg - > tokType ( ) = = Token : : eFunction ) )
2015-10-05 19:04:29 +02:00
return ;
const Token * varTok = nullptr ;
2017-04-01 09:31:27 +02:00
const Token * tok1 = arg - > next ( ) ;
2015-10-05 19:04:29 +02:00
for ( ; tok1 ; tok1 = tok1 - > next ( ) ) {
if ( tok1 - > str ( ) = = " , " | | tok1 - > str ( ) = = " ) " ) {
if ( tok1 - > previous ( ) - > str ( ) = = " ] " ) {
varTok = tok1 - > linkAt ( - 1 ) - > previous ( ) ;
if ( varTok - > str ( ) = = " ) " & & varTok - > link ( ) - > previous ( ) - > tokType ( ) = = Token : : eFunction ) {
const Function * function = varTok - > link ( ) - > previous ( ) - > function ( ) ;
2016-04-22 06:02:54 +02:00
if ( function & & function - > retType & & function - > retType - > isEnumType ( ) ) {
if ( function - > retType - > classScope - > enumType )
typeToken = function - > retType - > classScope - > enumType ;
else {
2018-05-25 07:15:05 +02:00
tempToken = new Token ( ) ;
2016-04-22 06:02:54 +02:00
tempToken - > fileIndex ( tok1 - > fileIndex ( ) ) ;
tempToken - > linenr ( tok1 - > linenr ( ) ) ;
tempToken - > str ( " int " ) ;
typeToken = tempToken ;
}
} else if ( function & & function - > retDef ) {
2013-09-03 05:50:19 +02:00
typeToken = function - > retDef ;
2014-07-28 09:16:35 +02:00
while ( typeToken - > str ( ) = = " const " | | typeToken - > str ( ) = = " extern " )
2013-09-22 06:56:31 +02:00
typeToken = typeToken - > next ( ) ;
2013-09-03 05:50:19 +02:00
functionInfo = function ;
2015-10-05 19:04:29 +02:00
element = true ;
2014-08-18 14:02:35 +02:00
}
return ;
2013-09-03 05:50:19 +02:00
}
2015-10-05 19:04:29 +02:00
} else if ( tok1 - > previous ( ) - > str ( ) = = " ) " & & tok1 - > linkAt ( - 1 ) - > previous ( ) - > tokType ( ) = = Token : : eFunction ) {
const Function * function = tok1 - > linkAt ( - 1 ) - > previous ( ) - > function ( ) ;
2016-04-22 06:02:54 +02:00
if ( function & & function - > retType & & function - > retType - > isEnumType ( ) ) {
if ( function - > retType - > classScope - > enumType )
typeToken = function - > retType - > classScope - > enumType ;
else {
2018-05-25 07:15:05 +02:00
tempToken = new Token ( ) ;
2016-04-22 06:02:54 +02:00
tempToken - > fileIndex ( tok1 - > fileIndex ( ) ) ;
tempToken - > linenr ( tok1 - > linenr ( ) ) ;
tempToken - > str ( " int " ) ;
typeToken = tempToken ;
}
} else if ( function & & function - > retDef ) {
2015-10-05 19:04:29 +02:00
typeToken = function - > retDef ;
while ( typeToken - > str ( ) = = " const " | | typeToken - > str ( ) = = " extern " )
typeToken = typeToken - > next ( ) ;
functionInfo = function ;
element = false ;
}
2013-11-08 12:44:05 +01:00
return ;
2015-10-05 19:04:29 +02:00
} else
2013-11-08 12:44:05 +01:00
varTok = tok1 - > previous ( ) ;
2015-10-05 19:04:29 +02:00
break ;
} else if ( tok1 - > str ( ) = = " ( " | | tok1 - > str ( ) = = " { " || tok1->str() == " [ " )
tok1 = tok1 - > link ( ) ;
else if ( tok1 - > link ( ) & & tok1 - > str ( ) = = " < " )
tok1 = tok1 - > link ( ) ;
// check for some common well known functions
else if ( isCPP & & ( ( Token : : Match ( tok1 - > previous ( ) , " %var% . size|empty|c_str ( ) [,)] " ) & & isStdContainer ( tok1 - > previous ( ) ) ) | |
( Token : : Match ( tok1 - > previous ( ) , " ] . size|empty|c_str ( ) [,)] " ) & & isStdContainer ( tok1 - > previous ( ) - > link ( ) - > previous ( ) ) ) ) ) {
2018-05-25 07:15:05 +02:00
tempToken = new Token ( ) ;
2015-10-05 19:04:29 +02:00
tempToken - > fileIndex ( tok1 - > fileIndex ( ) ) ;
tempToken - > linenr ( tok1 - > linenr ( ) ) ;
if ( tok1 - > next ( ) - > str ( ) = = " size " ) {
// size_t is platform dependent
if ( settings - > sizeof_size_t = = 8 ) {
tempToken - > str ( " long " ) ;
if ( settings - > sizeof_long ! = 8 )
tempToken - > isLong ( true ) ;
} else if ( settings - > sizeof_size_t = = 4 ) {
if ( settings - > sizeof_long = = 4 ) {
tempToken - > str ( " long " ) ;
} else {
tempToken - > str ( " int " ) ;
}
2013-11-08 12:44:05 +01:00
}
2015-10-05 19:04:29 +02:00
tempToken - > originalName ( " size_t " ) ;
tempToken - > isUnsigned ( true ) ;
} else if ( tok1 - > next ( ) - > str ( ) = = " empty " ) {
tempToken - > str ( " bool " ) ;
} else if ( tok1 - > next ( ) - > str ( ) = = " c_str " ) {
tempToken - > str ( " const " ) ;
tempToken - > insertToken ( " * " ) ;
if ( typeToken - > strAt ( 2 ) = = " string " )
tempToken - > insertToken ( " char " ) ;
else
tempToken - > insertToken ( " wchar_t " ) ;
}
typeToken = tempToken ;
return ;
2013-08-23 05:35:57 +02:00
}
2013-08-20 06:16:31 +02:00
2015-10-05 19:04:29 +02:00
// check for std::vector::at() and std::string::at()
else if ( Token : : Match ( tok1 - > previous ( ) , " %var% . at ( " ) & &
Token : : Match ( tok1 - > linkAt ( 2 ) , " ) [,)] " ) ) {
varTok = tok1 - > previous ( ) ;
2013-09-03 05:50:19 +02:00
variableInfo = varTok - > variable ( ) ;
2013-08-30 05:41:59 +02:00
2015-10-05 19:04:29 +02:00
if ( ! variableInfo | | ! isStdVectorOrString ( ) ) {
2017-08-09 20:00:26 +02:00
variableInfo = nullptr ;
typeToken = nullptr ;
2013-09-03 05:50:19 +02:00
}
2013-08-30 05:41:59 +02:00
2013-09-03 05:50:19 +02:00
return ;
2015-10-05 19:04:29 +02:00
} else if ( ! ( tok1 - > str ( ) = = " . " | | tok1 - > tokType ( ) = = Token : : eVariable | | tok1 - > tokType ( ) = = Token : : eFunction ) )
return ;
}
if ( varTok ) {
variableInfo = varTok - > variable ( ) ;
element = tok1 - > previous ( ) - > str ( ) = = " ] " ;
// look for std::vector operator [] and use template type as return type
if ( variableInfo ) {
if ( element & & isStdVectorOrString ( ) ) { // isStdVectorOrString sets type token if true
element = false ; // not really an array element
2016-04-22 06:02:54 +02:00
} else if ( variableInfo - > isEnumType ( ) ) {
if ( variableInfo - > type ( ) & & variableInfo - > type ( ) - > classScope & & variableInfo - > type ( ) - > classScope - > enumType )
typeToken = variableInfo - > type ( ) - > classScope - > enumType ;
else {
2018-05-25 07:15:05 +02:00
tempToken = new Token ( ) ;
2016-04-22 06:02:54 +02:00
tempToken - > fileIndex ( tok1 - > fileIndex ( ) ) ;
tempToken - > linenr ( tok1 - > linenr ( ) ) ;
tempToken - > str ( " int " ) ;
typeToken = tempToken ;
}
2015-10-05 19:04:29 +02:00
} else
typeToken = variableInfo - > typeStartToken ( ) ;
2013-08-23 05:35:57 +02:00
}
2015-10-05 19:04:29 +02:00
return ;
2013-08-23 05:35:57 +02:00
}
2013-08-20 06:16:31 +02:00
}
2013-09-03 05:50:19 +02:00
}
2013-11-01 05:42:37 +01:00
CheckIO : : ArgumentInfo : : ~ ArgumentInfo ( )
{
if ( tempToken ) {
while ( tempToken - > next ( ) )
tempToken - > deleteNext ( ) ;
delete tempToken ;
}
}
2015-06-10 21:14:17 +02:00
namespace {
2018-04-08 22:54:10 +02:00
const std : : set < std : : string > stl_vector = { " array " , " vector " } ;
const std : : set < std : : string > stl_string = { " string " , " u16string " , " u32string " , " wstring " } ;
2015-06-10 21:14:17 +02:00
}
2015-05-17 20:02:41 +02:00
2013-09-08 20:21:00 +02:00
bool CheckIO : : ArgumentInfo : : isStdVectorOrString ( )
2013-09-07 07:20:06 +02:00
{
2015-06-17 09:09:23 +02:00
if ( ! isCPP )
return false ;
2014-01-30 05:26:48 +01:00
if ( variableInfo - > isStlType ( stl_vector ) ) {
2013-09-07 07:20:06 +02:00
typeToken = variableInfo - > typeStartToken ( ) - > tokAt ( 4 ) ;
2013-09-08 20:21:00 +02:00
_template = true ;
return true ;
2014-01-30 05:26:48 +01:00
} else if ( variableInfo - > isStlType ( stl_string ) ) {
2018-05-25 07:15:05 +02:00
tempToken = new Token ( ) ;
2013-09-08 20:21:00 +02:00
tempToken - > fileIndex ( variableInfo - > typeStartToken ( ) - > fileIndex ( ) ) ;
tempToken - > linenr ( variableInfo - > typeStartToken ( ) - > linenr ( ) ) ;
if ( variableInfo - > typeStartToken ( ) - > strAt ( 2 ) = = " string " )
tempToken - > str ( " char " ) ;
else
tempToken - > str ( " wchar_t " ) ;
typeToken = tempToken ;
2013-09-07 07:20:06 +02:00
return true ;
} else if ( variableInfo - > type ( ) & & ! variableInfo - > type ( ) - > derivedFrom . empty ( ) ) {
2015-09-15 14:34:12 +02:00
const std : : vector < Type : : BaseInfo > & derivedFrom = variableInfo - > type ( ) - > derivedFrom ;
2019-09-19 20:29:33 +02:00
for ( const Type : : BaseInfo & i : derivedFrom ) {
const Token * nameTok = i . nameTok ;
2015-09-15 14:34:12 +02:00
if ( Token : : Match ( nameTok , " std :: vector|array < " ) ) {
typeToken = nameTok - > tokAt ( 4 ) ;
2013-09-08 20:21:00 +02:00
_template = true ;
return true ;
2015-09-15 14:34:12 +02:00
} else if ( Token : : Match ( nameTok , " std :: string|wstring " ) ) {
2018-05-25 07:15:05 +02:00
tempToken = new Token ( ) ;
2013-09-08 20:21:00 +02:00
tempToken - > fileIndex ( variableInfo - > typeStartToken ( ) - > fileIndex ( ) ) ;
tempToken - > linenr ( variableInfo - > typeStartToken ( ) - > linenr ( ) ) ;
2015-09-15 14:34:12 +02:00
if ( nameTok - > strAt ( 2 ) = = " string " )
2013-09-08 20:21:00 +02:00
tempToken - > str ( " char " ) ;
else
tempToken - > str ( " wchar_t " ) ;
typeToken = tempToken ;
2013-09-07 07:20:06 +02:00
return true ;
}
}
2014-10-20 06:33:36 +02:00
} else if ( variableInfo - > type ( ) ) {
const Scope * classScope = variableInfo - > type ( ) - > classScope ;
if ( classScope ) {
2018-07-14 10:02:32 +02:00
for ( const Function & func : classScope - > functionList ) {
if ( func . name ( ) = = " operator[] " ) {
if ( Token : : Match ( func . retDef , " %type% & " ) ) {
typeToken = func . retDef ;
2014-10-20 06:33:36 +02:00
return true ;
}
}
}
}
2013-09-07 07:20:06 +02:00
}
return false ;
}
2018-07-14 10:02:32 +02:00
static const std : : set < std : : string > stl_container = {
" array " , " bitset " , " deque " , " forward_list " ,
" hash_map " , " hash_multimap " , " hash_set " ,
" list " , " map " , " multimap " , " multiset " ,
" priority_queue " , " queue " , " set " , " stack " ,
" unordered_map " , " unordered_multimap " , " unordered_multiset " , " unordered_set " , " vector "
} ;
2015-06-10 21:14:17 +02:00
bool CheckIO : : ArgumentInfo : : isStdContainer ( const Token * tok )
{
2015-06-17 09:09:23 +02:00
if ( ! isCPP )
return false ;
2013-09-07 07:20:06 +02:00
if ( tok & & tok - > variable ( ) ) {
2014-07-04 08:56:43 +02:00
const Variable * variable = tok - > variable ( ) ;
if ( variable - > isStlType ( stl_container ) ) {
typeToken = variable - > typeStartToken ( ) - > tokAt ( 4 ) ;
2013-09-08 20:21:00 +02:00
return true ;
2014-07-04 08:56:43 +02:00
} else if ( variable - > isStlType ( stl_string ) ) {
typeToken = variable - > typeStartToken ( ) ;
2013-09-07 07:20:06 +02:00
return true ;
2014-07-04 08:56:43 +02:00
} else if ( variable - > type ( ) & & ! variable - > type ( ) - > derivedFrom . empty ( ) ) {
2018-07-14 10:02:32 +02:00
for ( const Type : : BaseInfo & baseInfo : variable - > type ( ) - > derivedFrom ) {
const Token * nameTok = baseInfo . nameTok ;
2014-07-04 08:56:43 +02:00
if ( Token : : Match ( nameTok , " std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset < " ) ) {
typeToken = nameTok - > tokAt ( 4 ) ;
2013-09-08 20:21:00 +02:00
return true ;
2014-07-04 08:56:43 +02:00
} else if ( Token : : Match ( nameTok , " std :: string|wstring " ) ) {
typeToken = nameTok ;
2013-09-07 07:20:06 +02:00
return true ;
}
}
}
}
return false ;
}
2013-09-08 20:21:00 +02:00
bool CheckIO : : ArgumentInfo : : isArrayOrPointer ( ) const
{
2013-09-22 06:56:31 +02:00
if ( address )
return true ;
else if ( variableInfo & & ! _template ) {
2013-09-08 20:21:00 +02:00
return variableInfo - > isArrayOrPointer ( ) ;
} else {
const Token * tok = typeToken ;
2016-11-26 17:08:36 +01:00
while ( Token : : Match ( tok , " const|struct " ) )
2013-09-08 20:21:00 +02:00
tok = tok - > next ( ) ;
if ( tok & & tok - > strAt ( 1 ) = = " * " )
return true ;
}
return false ;
}
2013-09-03 05:50:19 +02:00
bool CheckIO : : ArgumentInfo : : isComplexType ( ) const
{
if ( variableInfo - > type ( ) )
return ( true ) ;
const Token * varTypeTok = typeToken ;
if ( varTypeTok - > str ( ) = = " std " )
varTypeTok = varTypeTok - > tokAt ( 2 ) ;
2014-09-05 12:03:08 +02:00
return ( ( variableInfo - > isStlStringType ( ) | | ( varTypeTok - > strAt ( 1 ) = = " < " & & varTypeTok - > linkAt ( 1 ) & & varTypeTok - > linkAt ( 1 ) - > strAt ( 1 ) ! = " :: " ) ) & & ! variableInfo - > isArrayOrPointer ( ) ) ;
2013-09-03 05:50:19 +02:00
}
bool CheckIO : : ArgumentInfo : : isKnownType ( ) const
{
if ( variableInfo )
return ( typeToken - > isStandardType ( ) | | typeToken - > next ( ) - > isStandardType ( ) | | isComplexType ( ) ) ;
else if ( functionInfo )
2014-07-28 09:16:35 +02:00
return ( typeToken - > isStandardType ( ) | | functionInfo - > retType | | Token : : Match ( typeToken , " std :: string|wstring " ) ) ;
2013-08-20 06:16:31 +02:00
2013-09-24 06:43:03 +02:00
return typeToken - > isStandardType ( ) | | Token : : Match ( typeToken , " std :: string|wstring " ) ;
2013-08-20 06:16:31 +02:00
}
2014-06-12 07:01:44 +02:00
bool CheckIO : : ArgumentInfo : : isLibraryType ( const Settings * settings ) const
{
return typeToken & & typeToken - > isStandardType ( ) & & settings - > library . podtype ( typeToken - > str ( ) ) ;
}
2012-05-20 11:57:07 +02:00
void CheckIO : : wrongPrintfScanfArgumentsError ( const Token * tok ,
const std : : string & functionName ,
2019-07-16 08:37:26 +02:00
nonneg int numFormat ,
nonneg int numFunction )
2012-05-20 11:57:07 +02:00
{
2018-04-05 15:55:57 +02:00
const Severity : : SeverityType severity = numFormat > numFunction ? Severity : : error : Severity : : warning ;
2018-06-16 16:10:28 +02:00
if ( severity ! = Severity : : error & & ! mSettings - > isEnabled ( Settings : : WARNING ) )
2012-05-20 11:57:07 +02:00
return ;
std : : ostringstream errmsg ;
errmsg < < functionName
2013-09-30 19:55:21 +02:00
< < " format string requires "
2012-05-20 11:57:07 +02:00
< < numFormat
2013-09-30 19:55:21 +02:00
< < " parameter " < < ( numFormat ! = 1 ? " s " : " " ) < < " but "
2012-05-20 11:57:07 +02:00
< < ( numFormat > numFunction ? " only " : " " )
< < numFunction
2013-09-30 19:55:21 +02:00
< < ( numFunction ! = 1 ? " are " : " is " )
< < " given. " ;
2012-05-20 11:57:07 +02:00
2016-01-25 20:01:48 +01:00
reportError ( tok , severity , " wrongPrintfScanfArgNum " , errmsg . str ( ) , CWE685 , false ) ;
2012-05-20 11:57:07 +02:00
}
2013-07-28 15:00:28 +02:00
void CheckIO : : wrongPrintfScanfPosixParameterPositionError ( const Token * tok , const std : : string & functionName ,
2019-07-16 08:37:26 +02:00
nonneg int index , nonneg int numFunction )
2013-07-28 15:00:28 +02:00
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2015-01-06 15:08:25 +01:00
return ;
2013-07-28 15:00:28 +02:00
std : : ostringstream errmsg ;
errmsg < < functionName < < " : " ;
2013-07-28 16:13:16 +02:00
if ( index = = 0 ) {
2013-07-28 15:00:28 +02:00
errmsg < < " parameter positions start at 1, not 0 " ;
} else {
errmsg < < " referencing parameter " < < index < < " while " < < numFunction < < " arguments given " ;
}
CWE mapping of invalidscanf, invalidScanfArgType_s, invalidScanfArgType_int, invalidScanfArgType_float, invalidPrintfArgType_s, invalidPrintfArgType_n, invalidPrintfArgType_p, invalidPrintfArgType_int, invalidPrintfArgType_uint, invalidPrintfArgType_sint, invalidPrintfArgType_float, wrongPrintfScanfParameterPositionError
2016-07-31 11:49:36 +02:00
reportError ( tok , Severity : : warning , " wrongPrintfScanfParameterPositionError " , errmsg . str ( ) , CWE685 , false ) ;
2013-07-28 15:00:28 +02:00
}
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidScanfArgTypeError_s ( const Token * tok , nonneg int numFormat , const std : : string & specifier , const ArgumentInfo * argInfo )
2012-05-20 11:57:07 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2012-05-20 11:57:07 +02:00
std : : ostringstream errmsg ;
2013-09-22 06:56:31 +02:00
errmsg < < " % " < < specifier < < " in format string (no. " < < numFormat < < " ) requires a \' " ;
if ( specifier [ 0 ] = = ' s ' )
errmsg < < " char " ;
else if ( specifier [ 0 ] = = ' S ' )
errmsg < < " wchar_t " ;
errmsg < < " * \' but the argument type is " ;
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidScanfArgType_s " , errmsg . str ( ) , CWE686 , false ) ;
2012-05-20 11:57:07 +02:00
}
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidScanfArgTypeError_int ( const Token * tok , nonneg int numFormat , const std : : string & specifier , const ArgumentInfo * argInfo , bool isUnsigned )
2013-09-22 06:56:31 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2013-09-22 06:56:31 +02:00
std : : ostringstream errmsg ;
errmsg < < " % " < < specifier < < " in format string (no. " < < numFormat < < " ) requires \' " ;
if ( specifier [ 0 ] = = ' h ' ) {
if ( specifier [ 1 ] = = ' h ' )
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " char " ;
else
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " short " ;
} else if ( specifier [ 0 ] = = ' l ' ) {
if ( specifier [ 1 ] = = ' l ' )
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " long long " ;
else
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " long " ;
2013-10-03 06:37:40 +02:00
} else if ( specifier . find ( " I32 " ) ! = std : : string : : npos ) {
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " __int32 " ;
} else if ( specifier . find ( " I64 " ) ! = std : : string : : npos ) {
2013-09-22 06:56:31 +02:00
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " __int64 " ;
2013-10-03 06:37:40 +02:00
} else if ( specifier [ 0 ] = = ' I ' ) {
errmsg < < ( isUnsigned ? " size_t " : " ptrdiff_t " ) ;
2013-09-22 06:56:31 +02:00
} else if ( specifier [ 0 ] = = ' j ' ) {
if ( isUnsigned )
errmsg < < " uintmax_t " ;
else
errmsg < < " intmax_t " ;
} else if ( specifier [ 0 ] = = ' z ' ) {
2014-01-14 06:09:13 +01:00
if ( specifier [ 1 ] = = ' d ' )
2017-10-24 21:01:23 +02:00
errmsg < < " ssize_t " ;
2014-01-14 06:09:13 +01:00
else
errmsg < < " size_t " ;
2013-09-22 06:56:31 +02:00
} else if ( specifier [ 0 ] = = ' t ' ) {
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " ptrdiff_t " ;
} else if ( specifier [ 0 ] = = ' L ' ) {
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " long long " ;
} else {
errmsg < < ( isUnsigned ? " unsigned " : " " ) < < " int " ;
}
errmsg < < " * \' but the argument type is " ;
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidScanfArgType_int " , errmsg . str ( ) , CWE686 , false ) ;
2013-09-22 06:56:31 +02:00
}
2019-07-16 09:29:57 +02:00
void CheckIO : : invalidScanfArgTypeError_float ( const Token * tok , nonneg int numFormat , const std : : string & specifier , const ArgumentInfo * argInfo )
2013-09-22 06:56:31 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2013-09-22 06:56:31 +02:00
std : : ostringstream errmsg ;
errmsg < < " % " < < specifier < < " in format string (no. " < < numFormat < < " ) requires \' " ;
if ( specifier [ 0 ] = = ' l ' & & specifier [ 1 ] ! = ' l ' )
errmsg < < " double " ;
else if ( specifier [ 0 ] = = ' L ' )
errmsg < < " long double " ;
else
errmsg < < " float " ;
errmsg < < " * \' but the argument type is " ;
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidScanfArgType_float " , errmsg . str ( ) , CWE686 , false ) ;
2013-09-22 06:56:31 +02:00
}
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidPrintfArgTypeError_s ( const Token * tok , nonneg int numFormat , const ArgumentInfo * argInfo )
2012-05-20 11:57:07 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2012-05-20 11:57:07 +02:00
std : : ostringstream errmsg ;
2013-10-27 10:48:49 +01:00
errmsg < < " %s in format string (no. " < < numFormat < < " ) requires \' char * \' but the argument type is " ;
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidPrintfArgType_s " , errmsg . str ( ) , CWE686 , false ) ;
2012-05-20 11:57:07 +02:00
}
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidPrintfArgTypeError_n ( const Token * tok , nonneg int numFormat , const ArgumentInfo * argInfo )
2012-05-20 11:57:07 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2012-05-20 11:57:07 +02:00
std : : ostringstream errmsg ;
2013-09-22 06:56:31 +02:00
errmsg < < " %n in format string (no. " < < numFormat < < " ) requires \' int * \' but the argument type is " ;
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidPrintfArgType_n " , errmsg . str ( ) , CWE686 , false ) ;
2012-05-20 11:57:07 +02:00
}
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidPrintfArgTypeError_p ( const Token * tok , nonneg int numFormat , const ArgumentInfo * argInfo )
2012-05-20 11:57:07 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2012-05-20 11:57:07 +02:00
std : : ostringstream errmsg ;
2013-09-05 01:46:58 +02:00
errmsg < < " %p in format string (no. " < < numFormat < < " ) requires an address but the argument type is " ;
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidPrintfArgType_p " , errmsg . str ( ) , CWE686 , false ) ;
2012-05-20 11:57:07 +02:00
}
2013-09-29 10:42:47 +02:00
static void printfFormatType ( std : : ostream & os , const std : : string & specifier , bool isUnsigned )
{
os < < " \' " ;
if ( specifier [ 0 ] = = ' l ' ) {
if ( specifier [ 1 ] = = ' l ' )
os < < ( isUnsigned ? " unsigned " : " " ) < < " long long " ;
else
os < < ( isUnsigned ? " unsigned " : " " ) < < " long " ;
2016-08-24 12:47:11 +02:00
} else if ( specifier [ 0 ] = = ' h ' ) {
if ( specifier [ 1 ] = = ' h ' )
os < < ( isUnsigned ? " unsigned " : " " ) < < " char " ;
else
os < < ( isUnsigned ? " unsigned " : " " ) < < " short " ;
2013-09-29 10:42:47 +02:00
} else if ( specifier . find ( " I32 " ) ! = std : : string : : npos ) {
os < < ( isUnsigned ? " unsigned " : " " ) < < " __int32 " ;
} else if ( specifier . find ( " I64 " ) ! = std : : string : : npos ) {
os < < ( isUnsigned ? " unsigned " : " " ) < < " __int64 " ;
} else if ( specifier [ 0 ] = = ' I ' ) {
os < < ( isUnsigned ? " size_t " : " ptrdiff_t " ) ;
} else if ( specifier [ 0 ] = = ' j ' ) {
if ( isUnsigned )
os < < " uintmax_t " ;
else
os < < " intmax_t " ;
} else if ( specifier [ 0 ] = = ' z ' ) {
if ( specifier [ 1 ] = = ' d ' )
os < < " ssize_t " ;
else
os < < " size_t " ;
} else if ( specifier [ 0 ] = = ' t ' ) {
os < < ( isUnsigned ? " unsigned " : " " ) < < " ptrdiff_t " ;
} else if ( specifier [ 0 ] = = ' L ' ) {
os < < ( isUnsigned ? " unsigned " : " " ) < < " long long " ;
} else {
os < < ( isUnsigned ? " unsigned " : " " ) < < " int " ;
}
os < < " \' " ;
}
2017-11-14 19:46:17 +01:00
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidPrintfArgTypeError_uint ( const Token * tok , nonneg int numFormat , const std : : string & specifier , const ArgumentInfo * argInfo )
2012-07-11 19:46:35 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2012-07-11 19:46:35 +02:00
std : : ostringstream errmsg ;
2013-09-29 10:42:47 +02:00
errmsg < < " % " < < specifier < < " in format string (no. " < < numFormat < < " ) requires " ;
printfFormatType ( errmsg , specifier , true ) ;
errmsg < < " but the argument type is " ;
2013-09-05 01:46:58 +02:00
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidPrintfArgType_uint " , errmsg . str ( ) , CWE686 , false ) ;
2012-07-11 19:46:35 +02:00
}
2013-09-29 10:42:47 +02:00
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidPrintfArgTypeError_sint ( const Token * tok , nonneg int numFormat , const std : : string & specifier , const ArgumentInfo * argInfo )
2012-07-11 19:46:35 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2012-07-11 19:46:35 +02:00
std : : ostringstream errmsg ;
2013-09-29 10:42:47 +02:00
errmsg < < " % " < < specifier < < " in format string (no. " < < numFormat < < " ) requires " ;
printfFormatType ( errmsg , specifier , false ) ;
errmsg < < " but the argument type is " ;
2013-09-05 01:46:58 +02:00
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidPrintfArgType_sint " , errmsg . str ( ) , CWE686 , false ) ;
2012-07-11 19:46:35 +02:00
}
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidPrintfArgTypeError_float ( const Token * tok , nonneg int numFormat , const std : : string & specifier , const ArgumentInfo * argInfo )
2012-05-20 11:57:07 +02:00
{
2016-09-11 17:05:01 +02:00
const Severity : : SeverityType severity = getSeverity ( argInfo ) ;
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( severity ) )
2015-01-06 15:08:25 +01:00
return ;
2012-05-20 11:57:07 +02:00
std : : ostringstream errmsg ;
2013-09-29 10:42:47 +02:00
errmsg < < " % " < < specifier < < " in format string (no. " < < numFormat < < " ) requires \' " ;
2013-09-15 16:38:45 +02:00
if ( specifier [ 0 ] = = ' L ' )
errmsg < < " long " ;
errmsg < < " double \' but the argument type is " ;
2013-09-05 01:46:58 +02:00
argumentType ( errmsg , argInfo ) ;
errmsg < < " . " ;
2016-09-11 17:05:01 +02:00
reportError ( tok , severity , " invalidPrintfArgType_float " , errmsg . str ( ) , CWE686 , false ) ;
}
2017-02-27 13:22:10 +01:00
Severity : : SeverityType CheckIO : : getSeverity ( const CheckIO : : ArgumentInfo * argInfo )
2016-09-11 17:05:01 +02:00
{
return ( argInfo & & argInfo - > typeToken & & ! argInfo - > typeToken - > originalName ( ) . empty ( ) ) ? Severity : : portability : Severity : : warning ;
2013-09-05 01:46:58 +02:00
}
void CheckIO : : argumentType ( std : : ostream & os , const ArgumentInfo * argInfo )
{
if ( argInfo ) {
os < < " \' " ;
const Token * type = argInfo - > typeToken ;
2015-08-14 20:46:13 +02:00
if ( type - > tokType ( ) = = Token : : eString ) {
2013-08-31 06:26:39 +02:00
if ( type - > isLong ( ) )
2013-09-05 01:46:58 +02:00
os < < " const wchar_t * " ;
2013-08-31 06:26:39 +02:00
else
2013-09-05 01:46:58 +02:00
os < < " const char * " ;
2013-08-31 06:26:39 +02:00
} else {
if ( type - > originalName ( ) . empty ( ) ) {
2013-09-22 06:56:31 +02:00
if ( type - > strAt ( - 1 ) = = " const " )
os < < " const " ;
2013-09-08 20:21:00 +02:00
while ( Token : : Match ( type , " const|struct " ) ) {
os < < type - > str ( ) < < " " ;
2013-09-03 05:50:19 +02:00
type = type - > next ( ) ;
}
2013-10-27 10:48:49 +01:00
while ( Token : : Match ( type , " %any% :: " ) ) {
os < < type - > str ( ) < < " :: " ;
type = type - > tokAt ( 2 ) ;
}
2014-09-29 14:50:00 +02:00
type - > stringify ( os , false , true , false ) ;
2013-10-05 16:47:06 +02:00
if ( type - > strAt ( 1 ) = = " * " & & ! argInfo - > element )
2013-09-08 20:21:00 +02:00
os < < " * " ;
else if ( argInfo - > variableInfo & & ! argInfo - > element & & argInfo - > variableInfo - > isArray ( ) )
2013-09-05 01:46:58 +02:00
os < < " * " ;
2013-10-27 10:48:49 +01:00
else if ( type - > strAt ( 1 ) = = " * " & & argInfo - > variableInfo & & argInfo - > element & & argInfo - > variableInfo - > isArray ( ) )
os < < " * " ;
2013-09-22 06:56:31 +02:00
if ( argInfo - > address )
os < < " * " ;
2013-08-31 06:26:39 +02:00
} else {
2017-10-26 22:42:38 +02:00
if ( type - > isUnsigned ( ) ) {
2017-10-26 22:47:11 +02:00
if ( type - > originalName ( ) = = " __int64 " | | type - > originalName ( ) = = " __int32 " | | type - > originalName ( ) = = " ptrdiff_t " )
2017-10-26 22:42:38 +02:00
os < < " unsigned " ;
}
2013-09-22 06:56:31 +02:00
os < < type - > originalName ( ) ;
if ( type - > strAt ( 1 ) = = " * " | | argInfo - > address )
os < < " * " ;
os < < " {aka " ;
2014-09-29 14:50:00 +02:00
type - > stringify ( os , false , true , false ) ;
2013-09-22 06:56:31 +02:00
if ( type - > strAt ( 1 ) = = " * " | | argInfo - > address )
2013-09-05 01:46:58 +02:00
os < < " * " ;
os < < " } " ;
2013-08-31 06:26:39 +02:00
}
}
2013-09-05 01:46:58 +02:00
os < < " \' " ;
2013-08-31 06:26:39 +02:00
} else
2013-09-05 01:46:58 +02:00
os < < " Unknown " ;
2012-05-20 11:57:07 +02:00
}
2013-09-05 01:46:58 +02:00
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidLengthModifierError ( const Token * tok , nonneg int numFormat , const std : : string & modifier )
2012-10-21 08:50:29 +02:00
{
2018-06-16 16:10:28 +02:00
if ( ! mSettings - > isEnabled ( Settings : : WARNING ) )
2015-01-06 15:08:25 +01:00
return ;
2012-10-21 08:50:29 +02:00
std : : ostringstream errmsg ;
errmsg < < " ' " < < modifier < < " ' in format string (no. " < < numFormat < < " ) is a length modifier and cannot be used without a conversion specifier. " ;
2016-08-24 16:37:14 +02:00
reportError ( tok , Severity : : warning , " invalidLengthModifierError " , errmsg . str ( ) , CWE704 , false ) ;
2012-10-21 08:50:29 +02:00
}
2019-07-16 08:37:26 +02:00
void CheckIO : : invalidScanfFormatWidthError ( const Token * tok , nonneg int numFormat , int width , const Variable * var , char c )
2012-06-23 07:52:52 +02:00
{
2014-09-01 09:33:58 +02:00
MathLib : : bigint arrlen = 0 ;
std : : string varname ;
2012-06-23 07:52:52 +02:00
if ( var ) {
2014-09-01 09:33:58 +02:00
arrlen = var - > dimension ( 0 ) ;
varname = var - > name ( ) ;
}
2012-06-23 07:52:52 +02:00
2014-09-01 09:33:58 +02:00
std : : ostringstream errmsg ;
if ( arrlen > width ) {
2018-12-20 21:14:02 +01:00
if ( tok ! = nullptr & & ( ! mSettings - > inconclusive | | ! mSettings - > isEnabled ( Settings : : WARNING ) ) )
2014-09-01 09:33:58 +02:00
return ;
errmsg < < " Width " < < width < < " given in format string (no. " < < numFormat < < " ) is smaller than destination buffer "
< < " ' " < < varname < < " [ " < < arrlen < < " ]'. " ;
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : warning , " invalidScanfFormatWidth_smaller " , errmsg . str ( ) , CWE ( 0U ) , true ) ;
2014-09-01 09:33:58 +02:00
} else {
errmsg < < " Width " < < width < < " given in format string (no. " < < numFormat < < " ) is larger than destination buffer ' "
2016-05-22 14:00:26 +02:00
< < varname < < " [ " < < arrlen < < " ]', use % " < < ( c = = ' c ' ? arrlen : ( arrlen - 1 ) ) < < c < < " to prevent overflowing it. " ;
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " invalidScanfFormatWidth " , errmsg . str ( ) , CWE687 , false ) ;
2014-09-01 09:33:58 +02:00
}
2012-06-23 07:52:52 +02:00
}