1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
|
/* -*- C++ -*-
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2025 Cppcheck team.
*
* 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/>.
*/
//---------------------------------------------------------------------------
#ifndef checkmemoryleakH
#define checkmemoryleakH
//---------------------------------------------------------------------------
/**
* @file
*
* %Check for memory leaks
*
* 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.
* - CheckMemoryLeakStructMember checks allocation/deallocation of structs and struct members
*/
#include "check.h"
#include "config.h"
#include <cstdint>
#include <list>
#include <string>
class Function;
class Scope;
class Settings;
class Token;
class Variable;
class ErrorLogger;
struct CWE;
class Tokenizer;
enum class Severity : std::uint8_t;
/// @addtogroup Core
/// @{
/** @brief Base class for memory leaks checking */
class CPPCHECKLIB CheckMemoryLeak {
private:
/** For access to the tokens */
const Tokenizer * const mTokenizer_;
/** ErrorLogger used to report errors */
ErrorLogger * const mErrorLogger_;
/** Enabled standards */
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 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
*/
void reportErr(const std::list<const Token *> &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const;
public:
CheckMemoryLeak() = delete;
CheckMemoryLeak(const CheckMemoryLeak &) = delete;
CheckMemoryLeak& operator=(const CheckMemoryLeak &) = delete;
CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e, const Settings *s)
: mTokenizer_(t), mErrorLogger_(e), mSettings_(s) {}
/** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */
enum AllocType : std::uint8_t { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many };
void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const;
/**
* @brief Get type of deallocation at given position
* @param tok position
* @param varid variable id
* @return type of deallocation
*/
AllocType getDeallocationType(const Token *tok, nonneg int varid) const;
/**
* @brief Get type of allocation at given position
*/
AllocType getAllocationType(const Token *tok2, nonneg int varid, std::list<const Function*> *callstack = nullptr) const;
/**
* @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;
/**
* 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;
/**
* 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;
};
/// @}
/// @addtogroup Checks
/// @{
/**
* @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 : public Check, public CheckMemoryLeak {
friend class TestMemleakInFunction;
public:
/** @brief This constructor is used when registering this class */
CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {}
private:
/** @brief This constructor is used when running checks */
CheckMemoryLeakInFunction(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {}
void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override;
/**
* Checking for a memory leak caused by improper realloc usage.
*/
void checkReallocUsage();
/** Report all possible errors (for the --errorlist) */
void getErrorMessages(ErrorLogger *e, const Settings *settings) const override;
/**
* Get name of class (--doc)
* @return name of class
*/
static std::string myName() {
return "Memory leaks (function variables)";
}
/**
* 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 : public Check, private CheckMemoryLeak {
friend class TestMemleakInClass;
public:
CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {}
private:
CheckMemoryLeakInClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {}
void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override;
void check();
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;
static std::string myName() {
return "Memory leaks (class variables)";
}
std::string classInfo() const override {
return "If the constructor allocate memory then the destructor must deallocate it.\n";
}
};
/** @brief detect simple memory leaks for struct members */
class CPPCHECKLIB CheckMemoryLeakStructMember : public Check, private CheckMemoryLeak {
friend class TestMemleakStructMember;
public:
CheckMemoryLeakStructMember() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {}
private:
CheckMemoryLeakStructMember(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {}
void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override;
void check();
/** Is local variable allocated with malloc? */
bool isMalloc(const Variable *variable) const;
void checkStructVariable(const Variable* variable) const;
void getErrorMessages(ErrorLogger * errorLogger, const Settings * settings) const override;
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 : public Check, private CheckMemoryLeak {
friend class TestMemleakNoVar;
public:
CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {}
private:
CheckMemoryLeakNoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {}
void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override;
void check();
/**
* @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);
/**
* @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);
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);
void getErrorMessages(ErrorLogger *e, const Settings *settings) const override;
static std::string myName() {
return "Memory leaks (address not taken)";
}
std::string classInfo() const override {
return "Not taking the address to allocated memory\n";
}
};
/// @}
//---------------------------------------------------------------------------
#endif // checkmemoryleakH
|