cppcheck/lib/checkmemoryleak.h

360 lines
13 KiB
C
Raw Normal View History

2008-12-18 22:28:57 +01:00
/*
* Cppcheck - A tool for static C/C++ code analysis
2023-01-28 10:16:34 +01:00
* Copyright (C) 2007-2023 Cppcheck team.
2008-12-18 22:28:57 +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/>.
2008-12-18 22:28:57 +01:00
*/
//---------------------------------------------------------------------------
#ifndef checkmemoryleakH
#define checkmemoryleakH
2008-12-18 22:28:57 +01:00
//---------------------------------------------------------------------------
/**
2010-03-13 22:16:06 +01:00
* @file
*
* %Check for memory leaks
*
2011-09-08 18:23:25 +02:00
* The checking is split up into three specialized classes.
* - CheckMemoryLeakInFunction can detect when a function variable is allocated but not deallocated properly.
* - CheckMemoryLeakInClass can detect when a class variable is allocated but not deallocated properly.
2010-03-13 21:12:18 +01:00
* - CheckMemoryLeakStructMember checks allocation/deallocation of structs and struct members
*/
2008-12-18 22:28:57 +01:00
2009-03-20 18:16:21 +01:00
#include "check.h"
2017-05-27 04:33:47 +02:00
#include "config.h"
2020-05-23 07:16:49 +02:00
#include "errortypes.h"
2017-05-27 04:33:47 +02:00
#include "tokenize.h"
2009-03-20 18:16:21 +01:00
2008-12-18 22:28:57 +01:00
#include <list>
2009-03-20 18:16:21 +01:00
#include <string>
2008-12-18 22:28:57 +01:00
class Function;
2017-05-27 04:33:47 +02:00
class Scope;
class Settings;
class Token;
class Variable;
2020-05-23 07:16:49 +02:00
class ErrorLogger;
2009-03-20 18:16:21 +01:00
2010-03-13 21:12:18 +01:00
/// @addtogroup Core
/// @{
/** @brief Base class for memory leaks checking */
class CPPCHECKLIB CheckMemoryLeak {
2018-06-17 23:09:41 +02:00
private:
2010-03-13 21:12:18 +01:00
/** For access to the tokens */
2018-06-17 23:09:41 +02:00
const Tokenizer * const mTokenizer_;
2010-03-13 21:12:18 +01:00
/** ErrorLogger used to report errors */
2018-06-17 23:09:41 +02:00
ErrorLogger * const mErrorLogger_;
/** Enabled standards */
2018-06-17 23:09:41 +02:00
const Settings * const mSettings_;
/**
* Report error. Similar with the function Check::reportError
* @param tok the token where the error occurs
* @param severity the severity of the bug
* @param id type of message
* @param msg text
* @param cwe cwe number
*/
void reportErr(const Token *tok, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const;
/**
* Report error. Similar with the function Check::reportError
* @param callstack callstack of error
* @param severity the severity of the bug
* @param id type of message
* @param msg text
* @param cwe cwe number
*/
2016-02-27 16:03:50 +01:00
void reportErr(const std::list<const Token *> &callstack, Severity::SeverityType severity, const std::string &id, const std::string &msg, const CWE &cwe) const;
public:
CheckMemoryLeak() = delete;
CheckMemoryLeak(const CheckMemoryLeak &) = delete;
void operator=(const CheckMemoryLeak &) = delete;
2013-07-05 20:55:31 +02:00
CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e, const Settings *s)
2021-08-07 20:51:18 +02:00
: mTokenizer_(t), mErrorLogger_(e), mSettings_(s) {}
2010-03-13 21:12:18 +01:00
/** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */
enum AllocType { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many };
void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const;
/**
2010-03-13 21:12:18 +01:00
* @brief Get type of deallocation at given position
* @param tok position
2009-10-15 18:52:29 +02:00
* @param varid variable id
* @return type of deallocation
*/
2019-07-17 09:11:42 +02:00
AllocType getDeallocationType(const Token *tok, nonneg int varid) const;
/**
2010-03-13 21:12:18 +01:00
* @brief Get type of allocation at given position
*/
2019-07-17 09:11:42 +02:00
AllocType getAllocationType(const Token *tok2, nonneg int varid, std::list<const Function*> *callstack = nullptr) const;
/**
2010-03-13 21:12:18 +01:00
* @brief Get type of reallocation at given position
*/
AllocType getReallocationType(const Token *tok2, nonneg int varid) const;
/**
* Check if token reopens a standard stream
* @param tok token to check
*/
bool isReopenStandardStream(const Token *tok) const;
/**
* Check if token opens /dev/null
* @param tok token to check
*/
bool isOpenDevNull(const Token *tok) const;
2011-09-08 18:23:25 +02:00
/**
* Report that there is a memory leak (new/malloc/etc)
* @param tok token where memory is leaked
* @param varname name of variable
*/
void memleakError(const Token *tok, const std::string &varname) const;
2011-09-08 18:23:25 +02:00
/**
* Report that there is a resource leak (fopen/popen/etc)
* @param tok token where resource is leaked
* @param varname name of variable
*/
void resourceLeakError(const Token *tok, const std::string &varname) const;
void deallocuseError(const Token *tok, const std::string &varname) const;
void mismatchAllocDealloc(const std::list<const Token *> &callstack, const std::string &varname) const;
void memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const;
/** What type of allocated memory does the given function return? */
AllocType functionReturnType(const Function* func, std::list<const Function*> *callstack = nullptr) const;
};
2010-03-13 21:12:18 +01:00
/// @}
/// @addtogroup Checks
/// @{
/**
2010-03-13 22:16:06 +01:00
* @brief %CheckMemoryLeakInFunction detects when a function variable is allocated but not deallocated properly.
*
* The checking is done by looking at each function variable separately. By repeating these 4 steps over and over:
* -# locate a function variable
* -# create a simple token list that describes the usage of the function variable.
* -# simplify the token list.
* -# finally, check if the simplified token list contain any leaks.
*/
class CPPCHECKLIB CheckMemoryLeakInFunction : private Check, public CheckMemoryLeak {
2008-12-18 22:28:57 +01:00
public:
2010-03-13 21:12:18 +01:00
/** @brief This constructor is used when registering this class */
2021-08-07 20:51:18 +02:00
CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {}
2009-03-20 18:16:21 +01:00
2010-03-13 21:12:18 +01:00
/** @brief This constructor is used when running checks */
2018-06-18 09:40:27 +02:00
CheckMemoryLeakInFunction(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
2021-08-07 20:51:18 +02:00
: Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {}
2009-03-20 18:16:21 +01:00
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) override {
2018-06-18 09:40:27 +02:00
CheckMemoryLeakInFunction checkMemoryLeak(tokenizer, settings, errorLogger);
checkMemoryLeak.checkReallocUsage();
2009-03-20 18:16:21 +01:00
}
2010-03-13 21:12:18 +01:00
/** @brief Unit testing : testing the white list */
static bool test_white_list(const std::string &funcname, const Settings *settings, bool cpp);
/**
* Checking for a memory leak caused by improper realloc usage.
*/
void checkReallocUsage();
private:
2010-03-13 21:12:18 +01:00
/** Report all possible errors (for the --errorlist) */
void getErrorMessages(ErrorLogger *e, const Settings *settings) const override {
CheckMemoryLeakInFunction c(nullptr, settings, e);
c.memleakError(nullptr, "varname");
c.resourceLeakError(nullptr, "varname");
c.deallocuseError(nullptr, "varname");
2018-04-04 21:51:31 +02:00
const std::list<const Token *> callstack;
c.mismatchAllocDealloc(callstack, "varname");
c.memleakUponReallocFailureError(nullptr, "realloc", "varname");
2009-08-04 21:36:55 +02:00
}
2010-03-13 21:12:18 +01:00
/**
* Get name of class (--doc)
* @return name of class
*/
2014-11-20 14:20:09 +01:00
static std::string myName() {
2009-06-12 15:20:08 +02:00
return "Memory leaks (function variables)";
}
2010-03-13 21:12:18 +01:00
/**
* Get class information (--doc)
* @return Wiki formatted information about this class
*/
std::string classInfo() const override {
return "Is there any allocated memory when a function goes out of scope\n";
}
};
/**
* @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor
*/
class CPPCHECKLIB CheckMemoryLeakInClass : private Check, private CheckMemoryLeak {
public:
2021-08-07 20:51:18 +02:00
CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {}
2018-06-18 09:40:27 +02:00
CheckMemoryLeakInClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
2021-08-07 20:51:18 +02:00
: Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {}
void runChecks(const Tokenizer *tokenizr, const Settings *settings, ErrorLogger *errLog) override {
if (!tokenizr->isCPP())
return;
2010-04-08 22:56:34 +02:00
CheckMemoryLeakInClass checkMemoryLeak(tokenizr, settings, errLog);
checkMemoryLeak.check();
}
void check();
private:
void variable(const Scope *scope, const Token *tokVarname);
/** Public functions: possible double-allocation */
void checkPublicFunctions(const Scope *scope, const Token *classtok);
void publicAllocationError(const Token *tok, const std::string &varname);
void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname);
void getErrorMessages(ErrorLogger *e, const Settings *settings) const override {
CheckMemoryLeakInClass c(nullptr, settings, e);
c.publicAllocationError(nullptr, "varname");
c.unsafeClassError(nullptr, "class", "class::varname");
}
2014-11-20 14:20:09 +01:00
static std::string myName() {
2009-06-12 15:20:08 +02:00
return "Memory leaks (class variables)";
}
std::string classInfo() const override {
return "If the constructor allocate memory then the destructor must deallocate it.\n";
}
2008-12-18 22:28:57 +01:00
};
/** @brief detect simple memory leaks for struct members */
class CPPCHECKLIB CheckMemoryLeakStructMember : private Check, private CheckMemoryLeak {
public:
2021-08-07 20:51:18 +02:00
CheckMemoryLeakStructMember() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {}
2018-06-18 09:40:27 +02:00
CheckMemoryLeakStructMember(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
2021-08-07 20:51:18 +02:00
: Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {}
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) override {
2018-06-18 09:40:27 +02:00
CheckMemoryLeakStructMember checkMemoryLeak(tokenizer, settings, errorLogger);
checkMemoryLeak.check();
}
void check();
private:
/** Is local variable allocated with malloc? */
static bool isMalloc(const Variable *variable);
void checkStructVariable(const Variable * const variable);
void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const override {}
2014-11-20 14:20:09 +01:00
static std::string myName() {
return "Memory leaks (struct members)";
}
std::string classInfo() const override {
return "Don't forget to deallocate struct members\n";
}
};
/** @brief detect simple memory leaks (address not taken) */
class CPPCHECKLIB CheckMemoryLeakNoVar : private Check, private CheckMemoryLeak {
public:
2021-08-07 20:51:18 +02:00
CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {}
2018-06-18 09:40:27 +02:00
CheckMemoryLeakNoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
2021-08-07 20:51:18 +02:00
: Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {}
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) override {
2018-06-18 09:40:27 +02:00
CheckMemoryLeakNoVar checkMemoryLeak(tokenizer, settings, errorLogger);
checkMemoryLeak.check();
}
void check();
2012-03-15 18:52:51 +01:00
private:
/**
* @brief %Check if an input argument to a function is the return value of an allocation function
* like malloc(), and the function does not release it.
* @param scope The scope of the function to check.
*/
void checkForUnreleasedInputArgument(const Scope *scope);
/**
* @brief %Check if a call to an allocation function like malloc() is made and its return value is not assigned.
* @param scope The scope of the function to check.
*/
void checkForUnusedReturnValue(const Scope *scope);
2012-03-15 18:52:51 +01:00
/**
* @brief %Check if an exception could cause a leak in an argument constructed with shared_ptr/unique_ptr.
* @param scope The scope of the function to check.
*/
void checkForUnsafeArgAlloc(const Scope *scope);
2012-03-15 18:52:51 +01:00
void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall);
void returnValueNotUsedError(const Token* tok, const std::string &alloc);
void unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string &objType);
2012-03-15 18:52:51 +01:00
void getErrorMessages(ErrorLogger *e, const Settings *settings) const override {
CheckMemoryLeakNoVar c(nullptr, settings, e);
c.functionCallLeak(nullptr, "funcName", "funcName");
c.returnValueNotUsedError(nullptr, "funcName");
c.unsafeArgAllocError(nullptr, "funcName", "shared_ptr", "int");
}
2014-11-20 14:20:09 +01:00
static std::string myName() {
return "Memory leaks (address not taken)";
}
std::string classInfo() const override {
return "Not taking the address to allocated memory\n";
}
};
/// @}
2008-12-18 22:28:57 +01:00
//---------------------------------------------------------------------------
#endif // checkmemoryleakH