Blame lib/checkvaarg.cpp

Packit 2035a7
/*
Packit 2035a7
 * Cppcheck - A tool for static C/C++ code analysis
Packit 2035a7
 * Copyright (C) 2007-2017 Cppcheck team.
Packit 2035a7
 *
Packit 2035a7
 * This program is free software: you can redistribute it and/or modify
Packit 2035a7
 * it under the terms of the GNU General Public License as published by
Packit 2035a7
 * the Free Software Foundation, either version 3 of the License, or
Packit 2035a7
 * (at your option) any later version.
Packit 2035a7
 *
Packit 2035a7
 * This program is distributed in the hope that it will be useful,
Packit 2035a7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 2035a7
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 2035a7
 * GNU General Public License for more details.
Packit 2035a7
 *
Packit 2035a7
 * You should have received a copy of the GNU General Public License
Packit 2035a7
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit 2035a7
 */
Packit 2035a7
Packit 2035a7
#include "checkvaarg.h"
Packit 2035a7
Packit 2035a7
#include "errorlogger.h"
Packit 2035a7
#include "settings.h"
Packit 2035a7
#include "symboldatabase.h"
Packit 2035a7
#include "token.h"
Packit 2035a7
#include "tokenize.h"
Packit 2035a7
Packit 2035a7
#include <cstddef>
Packit 2035a7
#include <list>
Packit 2035a7
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
Packit 2035a7
// Register this check class (by creating a static instance of it)
Packit 2035a7
namespace {
Packit 2035a7
    CheckVaarg instance;
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
// Ensure that correct parameter is passed to va_start()
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
Packit 2035a7
// CWE ids used:
Packit 2035a7
static const struct CWE CWE664(664U);   // Improper Control of a Resource Through its Lifetime
Packit 2035a7
static const struct CWE CWE688(688U);   // Function Call With Incorrect Variable or Reference as Argument
Packit 2035a7
static const struct CWE CWE758(758U);   // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior
Packit 2035a7
Packit 2035a7
void CheckVaarg::va_start_argument()
Packit 2035a7
{
Packit 2035a7
    const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
Packit 2035a7
    const std::size_t functions = symbolDatabase->functionScopes.size();
Packit 2035a7
    const bool printWarnings = _settings->isEnabled(Settings::WARNING);
Packit 2035a7
Packit 2035a7
    for (std::size_t i = 0; i < functions; ++i) {
Packit 2035a7
        const Scope* scope = symbolDatabase->functionScopes[i];
Packit 2035a7
        const Function* function = scope->function;
Packit 2035a7
        if (!function)
Packit 2035a7
            continue;
Packit 2035a7
        for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
Packit 2035a7
            if (!tok->scope()->isExecutable())
Packit 2035a7
                tok = tok->scope()->classEnd;
Packit 2035a7
            else if (Token::simpleMatch(tok, "va_start (")) {
Packit 2035a7
                const Token* param2 = tok->tokAt(2)->nextArgument();
Packit 2035a7
                if (!param2)
Packit 2035a7
                    continue;
Packit 2035a7
                const Variable* var = param2->variable();
Packit 2035a7
                if (var && var->isReference())
Packit 2035a7
                    referenceAs_va_start_error(param2, var->name());
Packit 2035a7
                if (var && var->index() + 2 < function->argCount() && printWarnings) {
Packit 2035a7
                    std::list<Variable>::const_reverse_iterator it = function->argumentList.rbegin();
Packit 2035a7
                    ++it;
Packit 2035a7
                    wrongParameterTo_va_start_error(tok, var->name(), it->name());
Packit 2035a7
                }
Packit 2035a7
                tok = tok->linkAt(1);
Packit 2035a7
            }
Packit 2035a7
        }
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckVaarg::wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName)
Packit 2035a7
{
Packit 2035a7
    reportError(tok, Severity::warning,
Packit 2035a7
                "va_start_wrongParameter", "'" + paramIsName + "' given to va_start() is not last named argument of the function. Did you intend to pass '" + paramShouldName + "'?", CWE688, false);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckVaarg::referenceAs_va_start_error(const Token *tok, const std::string& paramName)
Packit 2035a7
{
Packit 2035a7
    reportError(tok, Severity::error,
Packit 2035a7
                "va_start_referencePassed", "Using reference '" + paramName + "' as parameter for va_start() results in undefined behaviour.", CWE758, false);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
// Detect missing va_end() if va_start() was used
Packit 2035a7
// Detect va_list usage after va_end()
Packit 2035a7
//---------------------------------------------------------------------------
Packit 2035a7
Packit 2035a7
void CheckVaarg::va_list_usage()
Packit 2035a7
{
Packit 2035a7
    const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase();
Packit 2035a7
    for (unsigned int varid = 1; varid < symbolDatabase->getVariableListSize(); varid++) {
Packit 2035a7
        const Variable* var = symbolDatabase->getVariableFromVarId(varid);
Packit 2035a7
        if (!var || var->isPointer() || var->isReference() || var->isArray() || !var->scope() || var->typeStartToken()->str() != "va_list")
Packit 2035a7
            continue;
Packit 2035a7
        if (!var->isLocal() && !var->isArgument()) // Check only local variables and arguments
Packit 2035a7
            continue;
Packit 2035a7
Packit 2035a7
        bool open = var->isArgument(); // va_list passed as argument are opened
Packit 2035a7
        bool exitOnEndOfStatement = false;
Packit 2035a7
Packit 2035a7
        const Token* tok = var->nameToken()->next();
Packit 2035a7
        for (;  tok && tok != var->scope()->classEnd; tok = tok->next()) {
Packit 2035a7
            if (Token::Match(tok, "va_start ( %varid%", var->declarationId())) {
Packit 2035a7
                if (open)
Packit 2035a7
                    va_start_subsequentCallsError(tok, var->name());
Packit 2035a7
                open = true;
Packit 2035a7
                tok = tok->linkAt(1);
Packit 2035a7
            } else if (Token::Match(tok, "va_end ( %varid%", var->declarationId())) {
Packit 2035a7
                if (!open)
Packit 2035a7
                    va_list_usedBeforeStartedError(tok, var->name());
Packit 2035a7
                open = false;
Packit 2035a7
                tok = tok->linkAt(1);
Packit 2035a7
            } else if (Token::simpleMatch(tok, "va_copy (")) {
Packit 2035a7
                bool nopen = open;
Packit 2035a7
                if (tok->linkAt(1)->previous()->varId() == var->declarationId()) { // Source
Packit 2035a7
                    if (!open)
Packit 2035a7
                        va_list_usedBeforeStartedError(tok, var->name());
Packit 2035a7
                }
Packit 2035a7
                if (tok->tokAt(2)->varId() == var->declarationId()) { // Destination
Packit 2035a7
                    if (open)
Packit 2035a7
                        va_start_subsequentCallsError(tok, var->name());
Packit 2035a7
                    nopen = true;
Packit 2035a7
                }
Packit 2035a7
                open = nopen;
Packit 2035a7
                tok = tok->linkAt(1);
Packit 2035a7
            } else if (Token::Match(tok, "throw|return"))
Packit 2035a7
                exitOnEndOfStatement = true;
Packit 2035a7
            else if (tok->str() == "break") {
Packit 2035a7
                const Scope* scope = tok->scope();
Packit 2035a7
                while (scope->nestedIn && scope->type != Scope::eFor && scope->type != Scope::eWhile && scope->type != Scope::eDo && scope->type != Scope::eSwitch)
Packit 2035a7
                    scope = scope->nestedIn;
Packit 2035a7
                tok = scope->classEnd;
Packit 2035a7
                if (!tok)
Packit 2035a7
                    return;
Packit 2035a7
            } else if (tok->str() == "goto" || (_tokenizer->isCPP() && tok->str() == "try")) {
Packit 2035a7
                open = false;
Packit 2035a7
                break;
Packit 2035a7
            } else if (!open && tok->varId() == var->declarationId())
Packit 2035a7
                va_list_usedBeforeStartedError(tok, var->name());
Packit 2035a7
            else if (exitOnEndOfStatement && tok->str() == ";")
Packit 2035a7
                break;
Packit 2035a7
        }
Packit 2035a7
        if (open && !var->isArgument())
Packit 2035a7
            va_end_missingError(tok, var->name());
Packit 2035a7
    }
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckVaarg::va_end_missingError(const Token *tok, const std::string& varname)
Packit 2035a7
{
Packit 2035a7
    reportError(tok, Severity::error,
Packit 2035a7
                "va_end_missing", "va_list '" + varname + "' was opened but not closed by va_end().", CWE664, false);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckVaarg::va_list_usedBeforeStartedError(const Token *tok, const std::string& varname)
Packit 2035a7
{
Packit 2035a7
    reportError(tok, Severity::error,
Packit 2035a7
                "va_list_usedBeforeStarted", "va_list '" + varname + "' used before va_start() was called.", CWE664, false);
Packit 2035a7
}
Packit 2035a7
Packit 2035a7
void CheckVaarg::va_start_subsequentCallsError(const Token *tok, const std::string& varname)
Packit 2035a7
{
Packit 2035a7
    reportError(tok, Severity::error,
Packit 2035a7
                "va_start_subsequentCalls", "va_start() or va_copy() called subsequently on '" + varname + "' without va_end() in between.", CWE664, false);
Packit 2035a7
}