2012-05-20 11:57:07 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 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"
2023-12-04 11:19:49 +01:00
# include "astutils.h"
2023-10-16 12:57:49 +02:00
# include "errortypes.h"
2017-05-27 04:33:47 +02:00
# include "library.h"
# include "mathlib.h"
2023-04-08 16:08:47 +02:00
# include "platform.h"
2017-05-27 04:33:47 +02:00
# 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"
2023-04-30 07:33:19 +02:00
# include "utils.h"
2023-01-26 22:23:22 +01:00
# include "vfvalue.h"
2012-05-20 11:57:07 +02:00
2022-01-27 19:03:20 +01:00
# include <algorithm>
2012-05-20 11:57:07 +02:00
# include <cctype>
2012-06-23 07:52:52 +02:00
# include <cstdlib>
2022-01-27 19:03:20 +01:00
# include <functional>
2017-05-27 04:33:47 +02:00
# include <list>
# include <map>
# include <set>
2022-09-16 07:15:49 +02:00
# include <sstream> // IWYU pragma: keep
2022-01-27 19:03:20 +01:00
# include <unordered_set>
2017-05-27 04:33:47 +02:00
# 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 ;
2023-08-29 12:00:52 +02:00
logChecker ( " CheckIO::checkCoutCerrMisusage " ) ; // c
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 )
{
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " coutCerrMisusage " , " Invalid usage of output stream: '<< std:: " + streamName + " '. " , CWE398 , Certainty : : normal ) ;
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
//---------------------------------------------------------------------------
2021-02-05 08:17:57 +01:00
enum class 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 )
2021-02-05 08:17:57 +01:00
return OpenMode : : RW_MODE ;
2023-06-20 18:43:21 +02:00
if ( str . find ( ' w ' ) ! = std : : string : : npos | | str . find ( ' a ' ) ! = std : : string : : npos )
2021-02-05 08:17:57 +01:00
return OpenMode : : WRITE_MODE ;
2023-06-20 18:43:21 +02:00
if ( str . find ( ' r ' ) ! = std : : string : : npos )
2021-02-05 08:17:57 +01:00
return OpenMode : : READ_MODE ;
return OpenMode : : UNKNOWN_OM ;
2012-05-22 14:30:22 +02:00
}
2012-05-22 15:43:40 +02:00
2015-06-20 22:26:51 +02:00
namespace {
2023-11-16 15:49:41 +01:00
struct Filepointer {
OpenMode mode ;
nonneg int mode_indent { } ;
enum class Operation { NONE , UNIMPORTANT , READ , WRITE , POSITIONING , OPEN , CLOSE , UNKNOWN_OP } lastOperation = Operation : : NONE ;
nonneg int op_indent { } ;
enum class AppendMode { UNKNOWN_AM , APPEND , APPEND_EX } ;
AppendMode append_mode = AppendMode : : UNKNOWN_AM ;
std : : string filename ;
explicit Filepointer ( OpenMode mode_ = OpenMode : : UNKNOWN_OM )
: mode ( mode_ ) { }
} ;
2021-01-16 13:52:09 +01:00
const std : : unordered_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 ( )
{
2023-03-03 18:36:27 +01:00
const bool windows = mSettings - > platform . isWindows ( ) ;
2021-02-24 22:00:06 +01:00
const bool printPortability = mSettings - > severity . isEnabled ( Severity : : portability ) ;
const bool printWarnings = mSettings - > severity . isEnabled ( Severity : : 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
2023-08-29 12:00:52 +02:00
logChecker ( " CheckIO::checkFileUsage " ) ;
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"
2021-02-05 08:17:57 +01:00
filepointers . insert ( std : : make_pair ( var - > declarationId ( ) , Filepointer ( OpenMode : : UNKNOWN_OM ) ) ) ;
2012-06-24 18:57:17 +02:00
else
2021-02-05 08:17:57 +01:00
filepointers . insert ( std : : make_pair ( var - > declarationId ( ) , Filepointer ( OpenMode : : CLOSED ) ) ) ;
2012-06-24 18:57:17 +02:00
} else {
2021-02-05 08:17:57 +01:00
filepointers . insert ( std : : make_pair ( var - > declarationId ( ) , Filepointer ( OpenMode : : 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 ( ) ) {
2023-12-04 11:19:49 +01:00
if ( Token : : Match ( tok , " %name% ( " ) & & isUnevaluated ( tok ) ) {
tok = tok - > linkAt ( 1 ) ;
continue ;
}
2012-10-30 15:52:45 +01:00
if ( tok - > str ( ) = = " { " )
indent + + ;
else if ( tok - > str ( ) = = " } " ) {
indent - - ;
2020-09-21 19:30:47 +02:00
for ( std : : pair < const int , Filepointer > & filepointer : filepointers ) {
if ( indent < filepointer . second . mode_indent ) {
filepointer . second . mode_indent = 0 ;
2021-02-05 08:17:57 +01:00
filepointer . second . mode = OpenMode : : UNKNOWN_OM ;
2012-10-30 15:52:45 +01:00
}
2020-09-21 19:30:47 +02:00
if ( indent < filepointer . second . op_indent ) {
filepointer . second . op_indent = 0 ;
2021-02-05 08:17:57 +01:00
filepointer . second . lastOperation = Filepointer : : Operation : : UNKNOWN_OP ;
2012-10-30 15:52:45 +01:00
}
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
2020-09-21 19:30:47 +02:00
for ( std : : pair < const int , Filepointer > & filepointer : filepointers ) {
filepointer . second . mode_indent = 0 ;
2021-02-05 08:17:57 +01:00
filepointer . second . mode = OpenMode : : UNKNOWN_OM ;
2020-09-21 19:30:47 +02:00
filepointer . second . op_indent = 0 ;
2021-02-05 08:17:57 +01:00
filepointer . second . lastOperation = Filepointer : : Operation : : UNKNOWN_OP ;
2013-02-15 17:30:43 +01:00
}
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 ) ) ) {
2022-10-02 07:12:40 +02:00
const std : : map < int , Filepointer > : : iterator i = filepointers . find ( tok - > varId ( ) ) ;
2012-10-30 15:52:45 +01:00
if ( i ! = filepointers . end ( ) ) {
2021-02-05 08:17:57 +01:00
i - > second . mode = OpenMode : : UNKNOWN_OM ;
i - > second . lastOperation = Filepointer : : Operation : : UNKNOWN_OP ;
2012-05-22 14:30:22 +02:00
}
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 ;
2021-07-10 13:59:47 +02:00
const Token * fileNameTok = nullptr ;
2021-02-05 08:17:57 +01:00
Filepointer : : Operation operation = Filepointer : : Operation : : NONE ;
2012-10-30 15:52:45 +01:00
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 ) ;
2021-02-05 08:17:57 +01:00
operation = Filepointer : : Operation : : OPEN ;
2021-07-10 13:59:47 +02:00
if ( Token : : Match ( tok , " fopen ( %str% , " ) )
fileNameTok = tok - > tokAt ( 2 ) ;
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 ) ;
2021-02-05 08:17:57 +01:00
operation = Filepointer : : Operation : : OPEN ;
2014-03-12 19:22:44 +01:00
} 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 ( ) ] ;
2021-02-05 08:17:57 +01:00
if ( f . mode = = OpenMode : : READ_MODE )
2014-08-31 21:03:33 +02:00
fflushOnInputStreamError ( tok , fileTok - > str ( ) ) ;
}
2012-10-30 15:52:45 +01:00
}
2021-02-05 08:17:57 +01:00
operation = Filepointer : : Operation : : 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 ( ) ;
2021-02-05 08:17:57 +01:00
operation = Filepointer : : Operation : : 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 ( ) ;
2021-02-05 08:17:57 +01:00
operation = Filepointer : : Operation : : WRITE ;
2012-10-30 15:52:45 +01:00
} else if ( tok - > str ( ) = = " fclose " ) {
2012-05-22 14:30:22 +02:00
fileTok = tok - > tokAt ( 2 ) ;
2021-02-05 08:17:57 +01:00
operation = Filepointer : : Operation : : CLOSE ;
2012-10-30 15:52:45 +01:00
} 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 ( ) ;
2021-02-05 08:17:57 +01:00
operation = Filepointer : : Operation : : 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 ( ) ) ) {
2020-09-21 19:30:47 +02:00
for ( std : : pair < const int , Filepointer > & filepointer : filepointers ) {
const Variable * var = symbolDatabase - > getVariableFromVarId ( filepointer . first ) ;
2015-01-27 21:52:52 +01:00
if ( ! var | | ! ( var - > isLocal ( ) | | var - > isGlobal ( ) | | var - > isStatic ( ) ) ) {
2021-02-05 08:17:57 +01:00
filepointer . second . mode = OpenMode : : UNKNOWN_OM ;
2020-09-21 19:30:47 +02:00
filepointer . second . mode_indent = 0 ;
filepointer . second . op_indent = indent ;
2021-02-05 08:17:57 +01:00
filepointer . second . lastOperation = Filepointer : : Operation : : UNKNOWN_OP ;
2015-01-27 21:52:52 +01:00
}
}
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 ;
2021-02-05 08:17:57 +01:00
operation = Filepointer : : Operation : : UNKNOWN_OP ; // Assume that repositioning was last operation and that the file is opened now
2012-10-30 15:52:45 +01:00
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
2021-02-05 08:17:57 +01:00
filepointers . insert ( std : : make_pair ( fileTok - > varId ( ) , Filepointer ( OpenMode : : UNKNOWN_OM ) ) ) ;
2012-10-30 15:52:45 +01:00
}
2021-07-10 13:59:47 +02:00
2012-10-30 15:52:45 +01:00
Filepointer & f = filepointers [ fileTok - > varId ( ) ] ;
switch ( operation ) {
2021-02-05 08:17:57 +01:00
case Filepointer : : Operation : : OPEN :
2021-07-10 13:59:47 +02:00
if ( fileNameTok ) {
for ( std : : map < int , Filepointer > : : const_iterator it = filepointers . cbegin ( ) ; it ! = filepointers . cend ( ) ; + + it ) {
const Filepointer & fptr = it - > second ;
if ( fptr . filename = = fileNameTok - > str ( ) & & ( fptr . mode = = OpenMode : : RW_MODE | | fptr . mode = = OpenMode : : WRITE_MODE ) )
incompatibleFileOpenError ( tok , fileNameTok - > str ( ) ) ;
}
f . filename = fileNameTok - > str ( ) ;
}
2012-10-30 15:52:45 +01:00
f . mode = getMode ( mode ) ;
2014-03-17 11:02:03 +01:00
if ( mode . find ( ' a ' ) ! = std : : string : : npos ) {
2021-02-05 08:17:57 +01:00
if ( f . mode = = OpenMode : : RW_MODE )
f . append_mode = Filepointer : : AppendMode : : APPEND_EX ;
2014-03-17 11:02:03 +01:00
else
2021-02-05 08:17:57 +01:00
f . append_mode = Filepointer : : AppendMode : : APPEND ;
2015-10-13 15:31:58 +02:00
} else
2021-02-05 08:17:57 +01:00
f . append_mode = Filepointer : : AppendMode : : UNKNOWN_AM ;
2012-10-30 15:52:45 +01:00
f . mode_indent = indent ;
break ;
2021-02-05 08:17:57 +01:00
case Filepointer : : Operation : : POSITIONING :
if ( f . mode = = OpenMode : : CLOSED )
2012-10-30 15:52:45 +01:00
useClosedFileError ( tok ) ;
2021-02-05 08:17:57 +01:00
else if ( f . append_mode = = Filepointer : : AppendMode : : APPEND & & tok - > str ( ) ! = " fflush " & & printWarnings )
2014-03-17 11:02:03 +01:00
seekOnAppendedFileError ( tok ) ;
2012-10-30 15:52:45 +01:00
break ;
2021-02-05 08:17:57 +01:00
case Filepointer : : Operation : : READ :
if ( f . mode = = OpenMode : : CLOSED )
2012-10-30 15:52:45 +01:00
useClosedFileError ( tok ) ;
2021-02-05 08:17:57 +01:00
else if ( f . mode = = OpenMode : : WRITE_MODE )
2012-10-30 15:52:45 +01:00
readWriteOnlyFileError ( tok ) ;
2021-02-05 08:17:57 +01:00
else if ( f . lastOperation = = Filepointer : : Operation : : WRITE )
2012-10-30 15:52:45 +01:00
ioWithoutPositioningError ( tok ) ;
break ;
2021-02-05 08:17:57 +01:00
case Filepointer : : Operation : : WRITE :
if ( f . mode = = OpenMode : : CLOSED )
2012-10-30 15:52:45 +01:00
useClosedFileError ( tok ) ;
2021-02-05 08:17:57 +01:00
else if ( f . mode = = OpenMode : : READ_MODE )
2012-10-30 15:52:45 +01:00
writeReadOnlyFileError ( tok ) ;
2021-02-05 08:17:57 +01:00
else if ( f . lastOperation = = Filepointer : : Operation : : READ )
2012-10-30 15:52:45 +01:00
ioWithoutPositioningError ( tok ) ;
break ;
2021-02-05 08:17:57 +01:00
case Filepointer : : Operation : : CLOSE :
if ( f . mode = = OpenMode : : CLOSED )
2012-10-30 15:52:45 +01:00
useClosedFileError ( tok ) ;
else
2021-02-05 08:17:57 +01:00
f . mode = OpenMode : : CLOSED ;
2012-10-30 15:52:45 +01:00
f . mode_indent = indent ;
break ;
2021-02-05 08:17:57 +01:00
case Filepointer : : Operation : : UNIMPORTANT :
if ( f . mode = = OpenMode : : CLOSED )
2012-10-30 15:52:45 +01:00
useClosedFileError ( tok ) ;
break ;
2021-02-05 08:17:57 +01:00
case Filepointer : : Operation : : UNKNOWN_OP :
f . mode = OpenMode : : UNKNOWN_OM ;
2012-10-30 15:52:45 +01:00
f . mode_indent = 0 ;
break ;
default :
break ;
}
2021-02-05 08:17:57 +01:00
if ( operation ! = Filepointer : : Operation : : NONE & & operation ! = Filepointer : : Operation : : UNIMPORTANT ) {
2012-10-30 15:52:45 +01:00
f . op_indent = indent ;
f . lastOperation = operation ;
}
2012-05-22 14:30:22 +02:00
}
}
2020-09-21 19:30:47 +02:00
for ( std : : pair < const int , Filepointer > & filepointer : filepointers ) {
filepointer . second . op_indent = 0 ;
2021-02-05 08:17:57 +01:00
filepointer . second . mode = OpenMode : : UNKNOWN_OM ;
filepointer . second . lastOperation = Filepointer : : Operation : : UNKNOWN_OP ;
2012-12-08 09:26:10 +01:00
}
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 ,
2021-02-24 22:00:06 +01:00
" fflushOnInputStream " , " fflush() called on input stream ' " + varname + " ' may result in undefined behaviour on non-linux systems. " , CWE398 , Certainty : : normal ) ;
2012-05-22 14:30:22 +02:00
}
void CheckIO : : ioWithoutPositioningError ( const Token * tok )
{
reportError ( tok , Severity : : error ,
2021-02-24 22:00:06 +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 , Certainty : : normal ) ;
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 ,
2021-02-24 22:00:06 +01:00
" readWriteOnlyFile " , " Read operation on a file that was opened only for writing. " , CWE664 , Certainty : : normal ) ;
2012-05-22 14:30:22 +02:00
}
void CheckIO : : writeReadOnlyFileError ( const Token * tok )
{
reportError ( tok , Severity : : error ,
2021-02-24 22:00:06 +01:00
" writeReadOnlyFile " , " Write operation on a file that was opened only for reading. " , CWE664 , Certainty : : normal ) ;
2012-05-22 14:30:22 +02:00
}
void CheckIO : : useClosedFileError ( const Token * tok )
{
reportError ( tok , Severity : : error ,
2021-02-24 22:00:06 +01:00
" useClosedFile " , " Used file that is not opened. " , CWE910 , Certainty : : normal ) ;
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 ,
2021-02-24 22:00:06 +01:00
" seekOnAppendedFile " , " Repositioning operation performed on a file opened in append mode has no effect. " , CWE398 , Certainty : : normal ) ;
2014-03-17 11:02:03 +01:00
}
2021-07-10 13:59:47 +02:00
void CheckIO : : incompatibleFileOpenError ( const Token * tok , const std : : string & filename )
{
reportError ( tok , Severity : : warning ,
" incompatibleFileOpen " , " The file ' " + filename + " ' is opened for read and write access at the same time on different streams " , CWE664 , Certainty : : normal ) ;
}
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 ( )
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : 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 " ,
2021-02-24 22:00:06 +01:00
CWE119 , Certainty : : normal ) ;
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 ;
2023-06-20 18:43:21 +02:00
}
if ( Token : : Match ( argTok , " %var% [,)] " ) & &
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 ( ) ) {
2022-10-02 07:12:40 +02:00
const std : : list < ValueFlow : : Value > : : const_iterator value = std : : find_if (
2022-12-30 15:13:47 +01:00
argTok - > values ( ) . cbegin ( ) , argTok - > values ( ) . cend ( ) , std : : mem_fn ( & ValueFlow : : Value : : isTokValue ) ) ;
if ( value ! = argTok - > values ( ) . cend ( ) & & value - > isTokValue ( ) & & value - > tokvalue & &
2018-12-17 06:07:34 +01:00
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 ( ) ;
2023-03-03 18:36:27 +01:00
const bool isWindows = mSettings - > platform . isWindows ( ) ;
2012-05-20 11:57:07 +02:00
2023-08-29 12:00:52 +02:00
logChecker ( " CheckIO::checkWrongPrintfScanfArguments " ) ;
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 )
{
2023-03-03 18:36:27 +01:00
const bool isWindows = mSettings - > platform . isWindows ( ) ;
2021-02-24 22:00:06 +01:00
const bool printWarning = mSettings - > severity . isEnabled ( Severity : : 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 ;
2022-12-20 20:32:16 +01:00
for ( std : : string : : const_iterator i = formatString . cbegin ( ) ; i ! = formatString . cend ( ) ; + + i ) {
2015-10-10 20:08:15 +02:00
if ( * i = = ' % ' ) {
percent = ! percent ;
} else if ( percent & & * i = = ' [ ' ) {
2022-12-30 15:13:47 +01:00
while ( i ! = formatString . cend ( ) ) {
2015-10-10 20:08:15 +02:00
if ( * i = = ' ] ' ) {
numFormat + + ;
if ( argListTok )
argListTok = argListTok - > nextArgument ( ) ;
percent = false ;
break ;
}
+ + i ;
}
if ( scanf_s ) {
numSecure + + ;
if ( argListTok ) {
argListTok = argListTok - > nextArgument ( ) ;
}
}
2022-12-30 15:13:47 +01:00
if ( i = = formatString . cend ( ) )
2015-10-10 20:08:15 +02:00
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 ;
2022-12-30 15:13:47 +01:00
while ( i ! = formatString . cend ( ) & & * i ! = ' [ ' & & ! std : : isalpha ( ( unsigned char ) * i ) ) {
2015-10-10 20:08:15 +02:00
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 = = ' $ ' ) {
2023-04-08 22:29:09 +02:00
parameterPosition = strToInt < int > ( width ) ;
2015-10-10 20:08:15 +02:00
hasParameterPosition = true ;
width . clear ( ) ;
}
+ + i ;
}
2022-12-30 15:13:47 +01:00
auto bracketBeg = formatString . cend ( ) ;
if ( i ! = formatString . cend ( ) & & * i = = ' [ ' ) {
2021-12-01 09:51:48 +01:00
bracketBeg = i ;
2022-12-30 15:13:47 +01:00
while ( i ! = formatString . cend ( ) ) {
2021-12-01 09:51:48 +01:00
if ( * i = = ' ] ' )
2012-10-30 15:52:45 +01:00
break ;
2021-12-01 09:51:48 +01:00
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
}
}
2022-12-30 15:13:47 +01:00
if ( i = = formatString . cend ( ) )
2015-10-10 20:08:15 +02:00
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
2021-12-01 09:51:48 +01:00
if ( ( argInfo . typeToken & & ! argInfo . isLibraryType ( mSettings ) ) | | * i = = ' ] ' ) {
2015-10-10 20:08:15 +02:00
if ( scan ) {
std : : string specifier ;
bool done = false ;
while ( ! done ) {
switch ( * i ) {
case ' s ' :
2021-12-01 09:51:48 +01:00
case ' ] ' : // charset
specifier + = ( * i = = ' s ' | | bracketBeg = = formatString . end ( ) ) ? std : : string { ' s ' } : std : : string { bracketBeg , i + 1 } ;
2015-10-10 20:08:15 +02:00
if ( argInfo . variableInfo & & argInfo . isKnownType ( ) & & argInfo . variableInfo - > isArray ( ) & & ( argInfo . variableInfo - > dimensions ( ) . size ( ) = = 1 ) & & argInfo . variableInfo - > dimensions ( ) [ 0 ] . known ) {
if ( ! width . empty ( ) ) {
2023-04-08 22:29:09 +02:00
const int numWidth = strToInt < int > ( width ) ;
2015-10-10 20:08:15 +02:00
if ( numWidth ! = ( argInfo . variableInfo - > dimension ( 0 ) - 1 ) )
2021-12-01 09:51:48 +01:00
invalidScanfFormatWidthError ( tok , numFormat , numWidth , argInfo . variableInfo , specifier ) ;
2015-10-10 20:08:15 +02:00
}
}
2021-12-01 09:51:48 +01:00
if ( argListTok & & argListTok - > tokType ( ) ! = Token : : eString & & argInfo . typeToken & &
2015-10-10 20:08:15 +02:00
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 ) ;
}
2021-12-01 09:51:48 +01:00
if ( scanf_s & & argInfo . typeToken ) {
2015-10-10 20:08:15 +02:00
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 ( ) ) {
2023-04-08 22:29:09 +02:00
const int numWidth = strToInt < int > ( width ) ;
2016-05-22 14:00:26 +02:00
if ( numWidth > argInfo . variableInfo - > dimension ( 0 ) )
2021-12-01 09:51:48 +01:00
invalidScanfFormatWidthError ( tok , numFormat , numWidth , argInfo . variableInfo , std : : string ( 1 , * i ) ) ;
2016-05-22 14:00:26 +02:00
}
}
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 ' :
2019-10-24 21:51:20 +02:00
if ( ! ( typesMatch ( argInfo . typeToken - > originalName ( ) , " ssize_t " ) | |
( isWindows & & 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 ' :
2022-12-30 15:13:47 +01:00
if ( ( i + 1 ! = formatString . cend ( ) & & * ( i + 1 ) = = ' 6 ' & &
i + 2 ! = formatString . cend ( ) & & * ( i + 2 ) = = ' 4 ' ) | |
( i + 1 ! = formatString . cend ( ) & & * ( i + 1 ) = = ' 3 ' & &
i + 2 ! = formatString . cend ( ) & & * ( i + 2 ) = = ' 2 ' ) ) {
2015-10-10 20:08:15 +02:00
specifier + = * i + + ;
specifier + = * i + + ;
2022-12-30 15:13:47 +01:00
if ( ( i + 1 ) ! = formatString . cend ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
2015-10-10 20:08:15 +02:00
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 {
2022-12-30 15:13:47 +01:00
if ( ( i + 1 ) ! = formatString . cend ( ) & & ! 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 ' :
2022-12-30 15:13:47 +01:00
if ( i + 1 ! = formatString . cend ( ) & & * ( i + 1 ) = = * i )
2015-10-10 20:08:15 +02:00
specifier + = * i + + ;
2021-01-21 18:13:32 +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
2019-11-21 16:15:26 +01:00
if ( ( i + 1 ) ! = formatString . end ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
2015-10-10 20:08:15 +02:00
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 ) {
2021-11-07 18:19:56 +01:00
if ( i = = formatString . end ( ) ) {
break ;
}
2015-10-10 20:08:15 +02:00
switch ( * i ) {
case ' s ' :
if ( argListTok - > tokType ( ) ! = Token : : eString & &
argInfo . isKnownType ( ) & & ! argInfo . isArrayOrPointer ( ) ) {
if ( ! Token : : Match ( argInfo . typeToken , " char|wchar_t " ) ) {
2019-09-30 21:04:43 +02:00
if ( ! argInfo . element )
2015-10-10 20:08:15 +02:00
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 ' :
2019-10-24 21:51:20 +02:00
if ( ! ( typesMatch ( argInfo . typeToken - > originalName ( ) , " ssize_t " ) | |
( isWindows & & 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 )
2021-08-07 20:51:18 +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
2021-11-07 18:19:56 +01:00
if ( ( i + 1 ) ! = formatString . end ( ) & & * ( i + 1 ) = = * i ) {
2020-08-31 08:46:56 +02:00
if ( ( i + 2 ) ! = formatString . end ( ) & & ! isalpha ( * ( i + 2 ) ) ) {
2018-04-08 08:12:56 +02:00
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 {
2021-11-07 18:19:56 +01:00
if ( ( i + 1 ) ! = formatString . end ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
std : : string modifier ;
modifier + = * i ;
invalidLengthModifierError ( tok , numFormat , modifier ) ;
2013-07-21 08:35:01 +02:00
done = true ;
2021-11-07 18:19:56 +01:00
} else {
specifier = * i + + ;
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 + + ;
}
2021-01-21 18:13:32 +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
2019-11-21 16:15:26 +01:00
if ( ( i + 1 ) ! = formatString . end ( ) & & ! isalpha ( * ( i + 1 ) ) ) {
2015-10-10 20:08:15 +02:00
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 ) {
2022-12-15 08:52:13 +01:00
if ( Token : : Match ( argListTok2 , " %name% ... " ) ) // bailout for parameter pack
return ;
2015-10-10 20:08:15 +02:00
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
2022-10-02 07:12:40 +02:00
for ( const 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 )
2023-08-08 11:05:02 +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.
2022-06-08 16:58:57 +02:00
if ( ! Token : : Match ( arg , " %str% ,|) " ) & & ! ( arg - > variable ( ) & & arg - > variable ( ) - > isArray ( ) ) ) {
2017-04-01 09:31:27 +02:00
const Token * top = arg ;
2020-01-27 06:55:01 +01:00
while ( top - > str ( ) = = " ( " & & ! top - > isCast ( ) )
top = top - > next ( ) ;
2017-04-01 09:31:27 +02:00
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 ) ;
2021-12-20 17:22:39 +01:00
if ( top - > isBinaryOp ( ) & & valuetype - > pointer = = 1 & & ( valuetype - > type = = ValueType : : CHAR | | valuetype - > type = = ValueType : : WCHAR_T ) )
tempToken - > tokType ( Token : : eString ) ;
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 ;
2023-06-20 18:43:21 +02:00
}
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 ) , " ) ,|) " ) ) ) {
2017-04-01 09:31:27 +02:00
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 ;
2023-06-20 18:43:21 +02:00
}
if ( tok1 - > str ( ) = = " ( " | | tok1 - > str ( ) = = " { " | | tok1 - > str ( ) = = " [ " )
2015-10-05 19:04:29 +02:00
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
2023-03-03 18:36:27 +01:00
if ( settings - > platform . sizeof_size_t = = 8 ) {
2015-10-05 19:04:29 +02:00
tempToken - > str ( " long " ) ;
2023-03-03 18:36:27 +01:00
if ( settings - > platform . sizeof_long ! = 8 )
2015-10-05 19:04:29 +02:00
tempToken - > isLong ( true ) ;
2023-03-03 18:36:27 +01:00
} else if ( settings - > platform . sizeof_size_t = = 4 ) {
if ( settings - > platform . sizeof_long = = 4 ) {
2015-10-05 19:04:29 +02:00
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 ;
2023-06-20 18:43:21 +02:00
}
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 ;
2023-06-20 18:43:21 +02:00
}
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 ;
2023-06-20 18:43:21 +02:00
}
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 ;
2023-06-20 18:43:21 +02:00
}
if ( variable - > isStlType ( stl_string ) ) {
2014-07-04 08:56:43 +02:00
typeToken = variable - > typeStartToken ( ) ;
2013-09-07 07:20:06 +02:00
return true ;
2023-06-20 18:43:21 +02:00
}
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 ;
2023-06-20 18:43:21 +02:00
}
if ( Token : : Match ( nameTok , " std :: string|wstring " ) ) {
2014-07-04 08:56:43 +02:00
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 ;
2023-06-20 18:43:21 +02:00
if ( variableInfo & & ! _template )
2013-09-08 20:21:00 +02:00
return variableInfo - > isArrayOrPointer ( ) ;
2023-06-20 18:43:21 +02:00
const Token * tok = typeToken ;
while ( Token : : Match ( tok , " const|struct " ) )
tok = tok - > next ( ) ;
return tok & & tok - > strAt ( 1 ) = = " * " ;
2013-09-08 20:21:00 +02:00
}
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 ( ) ) ;
2023-06-20 18:43:21 +02:00
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 ,
2021-08-07 20:51:18 +02:00
const std : : string & functionName ,
nonneg int numFormat ,
nonneg int numFunction )
2012-05-20 11:57:07 +02:00
{
2023-10-12 11:58:39 +02:00
const Severity severity = numFormat > numFunction ? Severity : : error : Severity : : warning ;
2021-02-24 22:00:06 +01:00
if ( severity ! = Severity : : error & & ! mSettings - > severity . isEnabled ( Severity : : 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
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " wrongPrintfScanfArgNum " , errmsg . str ( ) , CWE685 , Certainty : : normal ) ;
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 ,
2021-08-07 20:51:18 +02:00
nonneg int index , nonneg int numFunction )
2013-07-28 15:00:28 +02:00
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : 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 " ;
}
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " wrongPrintfScanfParameterPositionError " , errmsg . str ( ) , CWE685 , Certainty : : normal ) ;
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidScanfArgType_s " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 ' ) {
2021-09-05 07:34:22 +02:00
if ( specifier [ 1 ] = = ' d ' | | specifier [ 1 ] = = ' i ' )
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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidScanfArgType_int " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidScanfArgType_float " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidPrintfArgType_s " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidPrintfArgType_n " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidPrintfArgType_p " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
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 ' ) {
2021-09-05 07:34:22 +02:00
if ( specifier [ 1 ] = = ' d ' | | specifier [ 1 ] = = ' i ' )
2013-09-29 10:42:47 +02:00
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidPrintfArgType_uint " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidPrintfArgType_sint " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
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
{
2023-10-12 11:58:39 +02:00
const Severity severity = getSeverity ( argInfo ) ;
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . 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 < < " . " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , severity , " invalidPrintfArgType_float " , errmsg . str ( ) , CWE686 , Certainty : : normal ) ;
2016-09-11 17:05:01 +02:00
}
2023-10-12 11:58:39 +02:00
Severity 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 ) ;
}
2021-02-21 17:21:33 +01:00
os < < type - > stringify ( 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 < < " * " ;
2021-02-21 17:21:33 +01:00
os < < " {aka " < < type - > stringify ( 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
{
2021-02-24 22:00:06 +01:00
if ( ! mSettings - > severity . isEnabled ( Severity : : 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. " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " invalidLengthModifierError " , errmsg . str ( ) , CWE704 , Certainty : : normal ) ;
2012-10-21 08:50:29 +02:00
}
2021-12-01 09:51:48 +01:00
void CheckIO : : invalidScanfFormatWidthError ( const Token * tok , nonneg int numFormat , int width , const Variable * var , const std : : string & specifier )
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 ) {
2021-02-24 22:00:06 +01:00
if ( tok ! = nullptr & & ( ! mSettings - > certainty . isEnabled ( Certainty : : inconclusive ) | | ! mSettings - > severity . isEnabled ( Severity : : 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 < < " ]'. " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : warning , " invalidScanfFormatWidth_smaller " , errmsg . str ( ) , CWE ( 0U ) , Certainty : : inconclusive ) ;
2014-09-01 09:33:58 +02:00
} else {
errmsg < < " Width " < < width < < " given in format string (no. " < < numFormat < < " ) is larger than destination buffer ' "
2021-12-01 09:51:48 +01:00
< < varname < < " [ " < < arrlen < < " ]', use % " < < ( specifier = = " c " ? arrlen : ( arrlen - 1 ) ) < < specifier < < " to prevent overflowing it. " ;
2021-02-24 22:00:06 +01:00
reportError ( tok , Severity : : error , " invalidScanfFormatWidth " , errmsg . str ( ) , CWE687 , Certainty : : normal ) ;
2014-09-01 09:33:58 +02:00
}
2012-06-23 07:52:52 +02:00
}