2015-11-21 20:24:30 +01:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2016-01-01 14:34:45 +01:00
* Copyright ( C ) 2007 - 2016 Cppcheck team .
2015-11-21 20:24:30 +01: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/>.
*/
//---------------------------------------------------------------------------
// Check functions
//---------------------------------------------------------------------------
# include "checkfunctions.h"
# include "symboldatabase.h"
2015-11-25 10:16:04 +01:00
# include <cmath>
2015-11-21 20:24:30 +01:00
//---------------------------------------------------------------------------
// Register this check class (by creating a static instance of it)
namespace {
CheckFunctions instance ;
}
CWE mapping of duplicateExpression, duplicateBreak (CWE561), unreachableCode, unsignedLessThanZero, unsignedPositive, pointerLessThanZero, pointerPositive, varFuncNullUB, nanInArithmeticExpression, commaSeparatedReturn, (#797)
ignoredReturnValue
2016-05-22 13:17:38 +02:00
static const CWE CWE252 ( 252U ) ; // Unchecked Return Value
2016-08-04 13:55:38 +02:00
static const CWE CWE477 ( 477U ) ; // Use of Obsolete Functions
2016-05-14 11:13:33 +02:00
static const CWE CWE758 ( 758U ) ; // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
static const CWE CWE628 ( 628U ) ; // Function Call with Incorrectly Specified Arguments
2016-01-25 20:01:48 +01:00
2015-11-22 13:48:55 +01:00
void CheckFunctions : : checkProhibitedFunctions ( )
2015-11-21 20:24:30 +01:00
{
2017-04-11 11:49:09 +02:00
const bool checkAlloca = _settings - > isEnabled ( Settings : : WARNING ) & & ( ( _settings - > standards . c > = Standards : : C99 & & _tokenizer - > isC ( ) ) | | _settings - > standards . cpp > = Standards : : CPP11 ) ;
2015-11-21 20:24:30 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
for ( unsigned int i = 0 ; i < symbolDatabase - > functionScopes . size ( ) ; i + + ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( tok - > isName ( ) & & tok - > varId ( ) = = 0 & & tok - > strAt ( 1 ) = = " ( " ) {
// alloca() is special as it depends on the code being C or C++, so it is not in Library
2015-11-22 11:00:36 +01:00
if ( checkAlloca & & Token : : simpleMatch ( tok , " alloca ( " ) & & ( ! tok - > function ( ) | | tok - > function ( ) - > nestedIn - > type = = Scope : : eGlobal ) ) {
2016-06-20 09:30:41 +02:00
if ( _tokenizer - > isC ( ) ) {
if ( _settings - > standards . c > Standards : : C89 )
reportError ( tok , Severity : : warning , " allocaCalled " ,
" Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead. \n "
" The obsolete function 'alloca' is called. In C99 and later it is recommended to use a variable length array or "
" a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons "
" (http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca). " ) ;
} else
2015-11-21 20:24:30 +01:00
reportError ( tok , Severity : : warning , " allocaCalled " ,
2016-04-08 11:21:53 +02:00
" Obsolete function 'alloca' called. \n "
2015-11-21 20:24:30 +01:00
" The obsolete function 'alloca' is called. In C++11 and later it is recommended to use std::array<> or "
" a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons "
" (http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca). " ) ;
} else {
if ( tok - > function ( ) & & tok - > function ( ) - > hasBody ( ) )
continue ;
const Library : : WarnInfo * wi = _settings - > library . getWarnInfo ( tok ) ;
if ( wi ) {
2017-04-11 11:49:09 +02:00
if ( _settings - > isEnabled ( wi - > severity ) & & _settings - > standards . c > = wi - > standards . c & & _settings - > standards . cpp > = wi - > standards . cpp ) {
2016-08-04 13:55:38 +02:00
reportError ( tok , wi - > severity , tok - > str ( ) + " Called " , wi - > message , CWE477 , false ) ;
2015-11-21 20:24:30 +01:00
}
}
}
}
}
}
}
2015-11-22 13:48:55 +01:00
//---------------------------------------------------------------------------
// strtol(str, 0, radix) <- radix must be 0 or 2-36
//---------------------------------------------------------------------------
void CheckFunctions : : invalidFunctionUsage ( )
{
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( ! tok - > isName ( ) | | ! Token : : Match ( tok , " %name% ( !!) " ) )
continue ;
const Token * const functionToken = tok ;
int argnr = 1 ;
const Token * argtok = tok - > tokAt ( 2 ) ;
do {
if ( Token : : Match ( argtok , " %num% [,)] " ) ) {
if ( MathLib : : isInt ( argtok - > str ( ) ) & &
! _settings - > library . isargvalid ( functionToken , argnr , MathLib : : toLongNumber ( argtok - > str ( ) ) ) )
invalidFunctionArgError ( argtok , functionToken - > str ( ) , argnr , _settings - > library . validarg ( functionToken , argnr ) ) ;
} else {
const Token * top = argtok ;
while ( top - > astParent ( ) & & top - > astParent ( ) - > str ( ) ! = " , " & & top - > astParent ( ) ! = tok - > next ( ) )
top = top - > astParent ( ) ;
2015-12-26 01:38:41 +01:00
const Token * var = top ;
2015-12-26 01:52:43 +01:00
while ( Token : : Match ( var , " .|:: " ) )
2015-12-26 01:38:41 +01:00
var = var - > astOperand2 ( ) ;
if ( Token : : Match ( top , " %comp%|%oror%|&&|!|true|false " ) | |
2017-03-04 11:13:28 +01:00
( var & & var - > variable ( ) & & Token : : Match ( var - > variable ( ) - > typeStartToken ( ) , " bool|_Bool " ) ) ) {
2015-11-22 13:48:55 +01:00
if ( _settings - > library . isboolargbad ( functionToken , argnr ) )
invalidFunctionArgBoolError ( top , functionToken - > str ( ) , argnr ) ;
// Are the values 0 and 1 valid?
else if ( ! _settings - > library . isargvalid ( functionToken , argnr , 0 ) )
invalidFunctionArgError ( top , functionToken - > str ( ) , argnr , _settings - > library . validarg ( functionToken , argnr ) ) ;
else if ( ! _settings - > library . isargvalid ( functionToken , argnr , 1 ) )
invalidFunctionArgError ( top , functionToken - > str ( ) , argnr , _settings - > library . validarg ( functionToken , argnr ) ) ;
}
}
argnr + + ;
argtok = argtok - > nextArgument ( ) ;
} while ( argtok & & argtok - > str ( ) ! = " ) " ) ;
}
}
}
void CheckFunctions : : invalidFunctionArgError ( const Token * tok , const std : : string & functionName , int argnr , const std : : string & validstr )
{
std : : ostringstream errmsg ;
errmsg < < " Invalid " < < functionName < < " () argument nr " < < argnr ;
if ( ! tok )
;
else if ( tok - > isNumber ( ) )
errmsg < < " . The value is " < < tok - > str ( ) < < " but the valid values are ' " < < validstr < < " '. " ;
else if ( tok - > isComparisonOp ( ) )
errmsg < < " . The value is 0 or 1 (comparison result) but the valid values are ' " < < validstr < < " '. " ;
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " invalidFunctionArg " , errmsg . str ( ) , CWE628 , false ) ;
2015-11-22 13:48:55 +01:00
}
void CheckFunctions : : invalidFunctionArgBoolError ( const Token * tok , const std : : string & functionName , int argnr )
{
std : : ostringstream errmsg ;
errmsg < < " Invalid " < < functionName < < " () argument nr " < < argnr < < " . A non-boolean value is required. " ;
2016-01-25 20:01:48 +01:00
reportError ( tok , Severity : : error , " invalidFunctionArgBool " , errmsg . str ( ) , CWE628 , false ) ;
2015-11-22 13:48:55 +01:00
}
//---------------------------------------------------------------------------
// Check for ignored return values.
//---------------------------------------------------------------------------
void CheckFunctions : : checkIgnoredReturnValue ( )
{
2017-04-11 11:49:09 +02:00
if ( ! _settings - > isEnabled ( Settings : : WARNING ) )
2015-11-22 13:48:55 +01:00
return ;
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
2017-03-08 10:23:38 +01:00
// skip c++11 initialization, ({...})
2017-04-02 19:44:27 +02:00
if ( Token : : Match ( tok , " %var%|(|, { " ) )
2017-02-12 16:33:28 +01:00
tok = tok - > linkAt ( 1 ) ;
2017-04-07 13:18:53 +02:00
if ( Token : : Match ( tok - > previous ( ) , " %name% ( " ) ) {
2017-04-07 19:19:41 +02:00
bool semicolon = false ;
for ( const Token * tok2 = tok - > tokAt ( 2 ) ; tok2 & & tok2 - > str ( ) ! = " ) " ; tok2 = tok2 - > next ( ) ) {
if ( tok2 - > str ( ) = = " ; " )
semicolon = true ;
else if ( Token : : Match ( tok2 , " [({] " ) )
tok2 = tok2 - > link ( ) ;
}
if ( semicolon )
tok = tok - > link ( ) ;
}
2017-04-07 13:18:53 +02:00
2017-03-05 22:23:16 +01:00
if ( tok - > varId ( ) | | ! Token : : Match ( tok , " %name% ( " ) )
continue ;
if ( tok - > next ( ) - > astParent ( ) )
2015-11-22 13:48:55 +01:00
continue ;
if ( ! tok - > scope ( ) - > isExecutable ( ) ) {
tok = tok - > scope ( ) - > classEnd ;
continue ;
}
const Token * parent = tok ;
while ( parent - > astParent ( ) & & parent - > astParent ( ) - > str ( ) = = " :: " )
parent = parent - > astParent ( ) ;
2017-03-26 11:23:39 +02:00
if ( CHECK_WRONG_DATA ( tok - > next ( ) - > astOperand1 ( ) ) & & ! tok - > next ( ) - > astParent ( ) & & ( ! tok - > function ( ) | | ! Token : : Match ( tok - > function ( ) - > retDef , " void %name% " ) ) & & _settings - > library . isUseRetVal ( tok ) )
2017-03-05 22:23:16 +01:00
ignoredReturnValueError ( tok , tok - > next ( ) - > astOperand1 ( ) - > expressionString ( ) ) ;
2015-11-22 13:48:55 +01:00
}
}
}
void CheckFunctions : : ignoredReturnValueError ( const Token * tok , const std : : string & function )
{
reportError ( tok , Severity : : warning , " ignoredReturnValue " ,
CWE mapping of duplicateExpression, duplicateBreak (CWE561), unreachableCode, unsignedLessThanZero, unsignedPositive, pointerLessThanZero, pointerPositive, varFuncNullUB, nanInArithmeticExpression, commaSeparatedReturn, (#797)
ignoredReturnValue
2016-05-22 13:17:38 +02:00
" Return value of function " + function + " () is not used. " , CWE252 , false ) ;
2015-11-22 13:48:55 +01:00
}
//---------------------------------------------------------------------------
// Detect passing wrong values to <cmath> functions like atan(0, x);
//---------------------------------------------------------------------------
void CheckFunctions : : checkMathFunctions ( )
{
2017-04-11 11:49:09 +02:00
const bool styleC99 = _settings - > isEnabled ( Settings : : STYLE ) & & _settings - > standards . c ! = Standards : : C89 & & _settings - > standards . cpp ! = Standards : : CPP03 ;
const bool printWarnings = _settings - > isEnabled ( Settings : : WARNING ) ;
2015-11-22 13:48:55 +01:00
const SymbolDatabase * symbolDatabase = _tokenizer - > getSymbolDatabase ( ) ;
const std : : size_t functions = symbolDatabase - > functionScopes . size ( ) ;
for ( std : : size_t i = 0 ; i < functions ; + + i ) {
const Scope * scope = symbolDatabase - > functionScopes [ i ] ;
for ( const Token * tok = scope - > classStart - > next ( ) ; tok ! = scope - > classEnd ; tok = tok - > next ( ) ) {
if ( tok - > varId ( ) )
continue ;
2015-12-24 11:07:47 +01:00
if ( printWarnings & & Token : : Match ( tok , " %name% ( !!) " ) ) {
2015-11-22 13:48:55 +01:00
if ( tok - > strAt ( - 1 ) ! = " . "
& & Token : : Match ( tok , " log|logf|logl|log10|log10f|log10l ( %num% ) " ) ) {
const std : : string & number = tok - > strAt ( 2 ) ;
bool isNegative = MathLib : : isNegative ( number ) ;
bool isInt = MathLib : : isInt ( number ) ;
bool isFloat = MathLib : : isFloat ( number ) ;
if ( isNegative & & isInt & & MathLib : : toLongNumber ( number ) < = 0 ) {
mathfunctionCallWarning ( tok ) ; // case log(-2)
} else if ( isNegative & & isFloat & & MathLib : : toDoubleNumber ( number ) < = 0. ) {
mathfunctionCallWarning ( tok ) ; // case log(-2.0)
} else if ( ! isNegative & & isFloat & & MathLib : : toDoubleNumber ( number ) < = 0. ) {
mathfunctionCallWarning ( tok ) ; // case log(0.0)
} else if ( ! isNegative & & isInt & & MathLib : : toLongNumber ( number ) < = 0 ) {
mathfunctionCallWarning ( tok ) ; // case log(0)
}
}
// acos( x ), asin( x ) where x is defined for interval [-1,+1], but not beyond
else if ( Token : : Match ( tok , " acos|acosl|acosf|asin|asinf|asinl ( %num% ) " )) {
if ( std : : fabs ( MathLib : : toDoubleNumber ( tok - > strAt ( 2 ) ) ) > 1.0 )
mathfunctionCallWarning ( tok ) ;
}
// sqrt( x ): if x is negative the result is undefined
else if ( Token : : Match ( tok , " sqrt|sqrtf|sqrtl ( %num% ) " )) {
if ( MathLib : : isNegative ( tok - > strAt ( 2 ) ) )
mathfunctionCallWarning ( tok ) ;
}
// atan2 ( x , y): x and y can not be zero, because this is mathematically not defined
else if ( Token : : Match ( tok , " atan2|atan2f|atan2l ( %num% , %num% ) " )) {
if ( MathLib : : isNullValue ( tok - > strAt ( 2 ) ) & & MathLib : : isNullValue ( tok - > strAt ( 4 ) ) )
mathfunctionCallWarning ( tok , 2 ) ;
}
// fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined).
2015-12-24 14:40:48 +01:00
else if ( Token : : Match ( tok , " fmod|fmodf|fmodl ( " ) ) {
2015-11-22 13:48:55 +01:00
const Token * nextArg = tok - > tokAt ( 2 ) - > nextArgument ( ) ;
if ( nextArg & & nextArg - > isNumber ( ) & & MathLib : : isNullValue ( nextArg - > str ( ) ) )
mathfunctionCallWarning ( tok , 2 ) ;
}
// pow ( x , y) If x is zero, and y is negative --> division by zero
else if ( Token : : Match ( tok , " pow|powf|powl ( %num% , %num% ) " )) {
if ( MathLib : : isNullValue ( tok - > strAt ( 2 ) ) & & MathLib : : isNegative ( tok - > strAt ( 4 ) ) )
mathfunctionCallWarning ( tok , 2 ) ;
}
}
if ( styleC99 ) {
if ( Token : : Match ( tok , " %num% - erf ( " ) & & Tokenizer : : isOneNumber ( tok - > str ( ) ) & & tok - > next ( ) - > astOperand2 ( ) = = tok - > tokAt ( 3 ) ) {
mathfunctionCallWarning ( tok , " 1 - erf(x) " , " erfc(x) " ) ;
} else if ( Token : : simpleMatch ( tok , " exp ( " ) & & Token : : Match ( tok - > linkAt ( 1 ) , " ) - %num% " ) & & Tokenizer : : isOneNumber ( tok - > linkAt ( 1 ) - > strAt ( 2 ) ) & & tok - > linkAt ( 1 ) - > next ( ) - > astOperand1 ( ) = = tok - > next ( ) ) {
mathfunctionCallWarning ( tok , " exp(x) - 1 " , " expm1(x) " ) ;
} else if ( Token : : simpleMatch ( tok , " log ( " ) & & tok - > next ( ) - > astOperand2 ( ) ) {
const Token * plus = tok - > next ( ) - > astOperand2 ( ) ;
if ( plus - > str ( ) = = " + " & & ( ( plus - > astOperand1 ( ) & & Tokenizer : : isOneNumber ( plus - > astOperand1 ( ) - > str ( ) ) ) | | ( plus - > astOperand2 ( ) & & Tokenizer : : isOneNumber ( plus - > astOperand2 ( ) - > str ( ) ) ) ) )
mathfunctionCallWarning ( tok , " log(1 + x) " , " log1p(x) " ) ;
}
}
}
}
}
void CheckFunctions : : mathfunctionCallWarning ( const Token * tok , const unsigned int numParam )
{
if ( tok ) {
if ( numParam = = 1 )
2016-05-14 11:13:33 +02:00
reportError ( tok , Severity : : warning , " wrongmathcall " , " Passing value " + tok - > strAt ( 2 ) + " to " + tok - > str ( ) + " () leads to implementation-defined result. " , CWE758 , false ) ;
2015-11-22 13:48:55 +01:00
else if ( numParam = = 2 )
2016-05-14 11:13:33 +02:00
reportError ( tok , Severity : : warning , " wrongmathcall " , " Passing values " + tok - > strAt ( 2 ) + " and " + tok - > strAt ( 4 ) + " to " + tok - > str ( ) + " () leads to implementation-defined result. " , CWE758 , false ) ;
2015-11-22 13:48:55 +01:00
} else
2016-05-14 11:13:33 +02:00
reportError ( tok , Severity : : warning , " wrongmathcall " , " Passing value '#' to #() leads to implementation-defined result. " , CWE758 , false ) ;
2015-11-22 13:48:55 +01:00
}
void CheckFunctions : : mathfunctionCallWarning ( const Token * tok , const std : : string & oldexp , const std : : string & newexp )
{
2016-05-14 11:13:33 +02:00
reportError ( tok , Severity : : style , " unpreciseMathCall " , " Expression ' " + oldexp + " ' can be replaced by ' " + newexp + " ' to avoid loss of precision. " , CWE758 , false ) ;
2015-11-22 13:48:55 +01:00
}
void CheckFunctions : : checkLibraryMatchFunctions ( )
{
2017-04-11 11:49:09 +02:00
if ( ! _settings - > checkLibrary | | ! _settings - > isEnabled ( Settings : : INFORMATION ) )
2015-11-22 13:48:55 +01:00
return ;
for ( const Token * tok = _tokenizer - > tokens ( ) ; tok ; tok = tok - > next ( ) ) {
if ( Token : : Match ( tok , " %name% ( " ) & &
! Token : : Match ( tok , " for|if|while|switch|sizeof|catch|asm|return " ) & &
! tok - > function ( ) & &
! tok - > varId ( ) & &
! tok - > type ( ) & &
! tok - > isStandardType ( ) & &
tok - > linkAt ( 1 ) - > strAt ( 1 ) ! = " ( " & &
tok - > astParent ( ) = = tok - > next ( ) & &
_settings - > library . isNotLibraryFunction ( tok ) ) {
reportError ( tok ,
Severity : : information ,
" checkLibraryFunction " ,
" --check-library: There is no matching configuration for function " + tok - > str ( ) + " () " ) ;
}
}
2015-11-22 16:56:44 +01:00
}