2009-10-19 20:57:11 +02:00
|
|
|
/*
|
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2021-03-21 20:58:32 +01:00
|
|
|
* Copyright (C) 2007-2021 Cppcheck team.
|
2009-10-19 20:57:11 +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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifndef checkexceptionsafetyH
|
|
|
|
#define checkexceptionsafetyH
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#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 "token.h"
|
|
|
|
#include "tokenize.h"
|
2009-10-19 20:57:11 +02:00
|
|
|
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <list>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
class Settings;
|
2020-05-23 07:16:49 +02:00
|
|
|
class ErrorLogger;
|
2017-05-27 04:33:47 +02:00
|
|
|
|
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
2016-04-12 19:29:40 +02:00
|
|
|
// CWE ID used:
|
|
|
|
static const struct CWE CWE398(398U); // Indicator of Poor Code Quality
|
2016-08-24 12:43:45 +02:00
|
|
|
static const struct CWE CWE703(703U); // Improper Check or Handling of Exceptional Conditions
|
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
CWE mapping of unsafeClassCanLeak, zerodivcond, invalidPointerCast, redundantCopy, redundantAssignment, comparisonFunctionIsAlwaysTrueOrFalse, checkCastIntToCharAndBack, cstyleCast, passedByValue, clarifyCondition, exceptThrowInDestructor, exceptDeallocThrow, exceptRethrowCopy, catchExceptionByValue, fflushOnInputStream, seekOnAppendedFile, publicAllocationError
2016-04-12 19:29:40 +02:00
|
|
|
|
|
|
|
|
2009-10-19 20:57:11 +02:00
|
|
|
/// @addtogroup Checks
|
|
|
|
/// @{
|
|
|
|
|
|
|
|
|
2010-03-13 21:12:18 +01:00
|
|
|
/**
|
2010-03-13 22:16:06 +01:00
|
|
|
* @brief %Check exception safety (exceptions shouldn't cause leaks nor corrupt data)
|
2010-03-13 21:12:18 +01:00
|
|
|
*
|
|
|
|
* The problem with these checks is that Cppcheck can't determine what the valid
|
|
|
|
* values are for variables. But in some cases (dead pointers) it can be determined
|
|
|
|
* that certain variable values are corrupt.
|
|
|
|
*/
|
|
|
|
|
2012-06-10 14:19:09 +02:00
|
|
|
class CPPCHECKLIB CheckExceptionSafety : public Check {
|
2009-10-19 20:57:11 +02:00
|
|
|
public:
|
|
|
|
/** This constructor is used when registering the CheckClass */
|
2014-11-20 14:20:09 +01:00
|
|
|
CheckExceptionSafety() : Check(myName()) {
|
2013-08-07 16:27:37 +02:00
|
|
|
}
|
2009-10-19 20:57:11 +02:00
|
|
|
|
2010-03-17 22:16:18 +01:00
|
|
|
/** This constructor is used when running checks. */
|
2009-10-19 20:57:11 +02:00
|
|
|
CheckExceptionSafety(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
|
2014-11-20 14:20:09 +01:00
|
|
|
: Check(myName(), tokenizer, settings, errorLogger) {
|
2013-08-07 16:27:37 +02:00
|
|
|
}
|
2009-10-19 20:57:11 +02:00
|
|
|
|
2019-03-16 07:19:48 +01:00
|
|
|
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE {
|
2012-09-10 19:02:32 +02:00
|
|
|
if (tokenizer->isC())
|
|
|
|
return;
|
|
|
|
|
2009-10-19 20:57:11 +02:00
|
|
|
CheckExceptionSafety checkExceptionSafety(tokenizer, settings, errorLogger);
|
|
|
|
checkExceptionSafety.destructors();
|
2009-11-08 09:54:08 +01:00
|
|
|
checkExceptionSafety.deallocThrow();
|
2011-02-05 10:11:09 +01:00
|
|
|
checkExceptionSafety.checkRethrowCopy();
|
2012-02-02 16:17:42 +01:00
|
|
|
checkExceptionSafety.checkCatchExceptionByValue();
|
2014-04-10 16:17:10 +02:00
|
|
|
checkExceptionSafety.nothrowThrows();
|
2014-04-20 08:58:36 +02:00
|
|
|
checkExceptionSafety.unhandledExceptionSpecification();
|
2009-10-19 20:57:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Don't throw exceptions in destructors */
|
|
|
|
void destructors();
|
|
|
|
|
2010-12-31 13:58:17 +01:00
|
|
|
/** deallocating memory and then throw (dead pointer) */
|
2009-11-08 09:54:08 +01:00
|
|
|
void deallocThrow();
|
|
|
|
|
2011-02-05 10:11:09 +01:00
|
|
|
/** Don't rethrow a copy of the caught exception; use a bare throw instead */
|
|
|
|
void checkRethrowCopy();
|
|
|
|
|
2012-02-02 16:17:42 +01:00
|
|
|
/** @brief %Check for exceptions that are caught by value instead of by reference */
|
|
|
|
void checkCatchExceptionByValue();
|
|
|
|
|
2014-04-20 20:40:55 +02:00
|
|
|
/** @brief %Check for functions that throw that shouldn't */
|
2014-04-10 16:17:10 +02:00
|
|
|
void nothrowThrows();
|
|
|
|
|
2014-04-20 08:58:36 +02:00
|
|
|
/** @brief %Check for unhandled exception specification */
|
|
|
|
void unhandledExceptionSpecification();
|
|
|
|
|
2009-10-19 20:57:11 +02:00
|
|
|
private:
|
|
|
|
/** Don't throw exceptions in destructors */
|
2014-11-20 14:20:09 +01:00
|
|
|
void destructorsError(const Token * const tok, const std::string &className) {
|
2014-07-19 03:29:50 +02:00
|
|
|
reportError(tok, Severity::warning, "exceptThrowInDestructor",
|
|
|
|
"Class " + className + " is not safe, destructor throws exception\n"
|
|
|
|
"The class " + className + " is not safe because its destructor "
|
|
|
|
"throws an exception. If " + className + " is used and an exception "
|
2021-02-24 22:00:06 +01:00
|
|
|
"is thrown that is caught in an outer scope the program will terminate.", CWE398, Certainty::normal);
|
2009-10-19 20:57:11 +02:00
|
|
|
}
|
|
|
|
|
2014-11-20 14:20:09 +01:00
|
|
|
void deallocThrowError(const Token * const tok, const std::string &varname) {
|
2012-07-09 11:11:05 +02:00
|
|
|
reportError(tok, Severity::warning, "exceptDeallocThrow", "Exception thrown in invalid state, '" +
|
2021-02-24 22:00:06 +01:00
|
|
|
varname + "' points at deallocated memory.", CWE398, Certainty::normal);
|
2009-11-08 09:54:08 +01:00
|
|
|
}
|
|
|
|
|
2014-11-20 14:20:09 +01:00
|
|
|
void rethrowCopyError(const Token * const tok, const std::string &varname) {
|
2011-02-05 10:11:09 +01:00
|
|
|
reportError(tok, Severity::style, "exceptRethrowCopy",
|
2012-07-09 11:11:05 +02:00
|
|
|
"Throwing a copy of the caught exception instead of rethrowing the original exception.\n"
|
|
|
|
"Rethrowing an exception with 'throw " + varname + ";' creates an unnecessary copy of '" + varname + "'. "
|
2021-02-24 22:00:06 +01:00
|
|
|
"To rethrow the caught exception without unnecessary copying or slicing, use a bare 'throw;'.", CWE398, Certainty::normal);
|
2011-02-05 10:11:09 +01:00
|
|
|
}
|
|
|
|
|
2014-11-20 14:20:09 +01:00
|
|
|
void catchExceptionByValueError(const Token *tok) {
|
2012-02-02 16:17:42 +01:00
|
|
|
reportError(tok, Severity::style,
|
|
|
|
"catchExceptionByValue", "Exception should be caught by reference.\n"
|
2012-07-09 11:11:05 +02:00
|
|
|
"The exception is caught by value. It could be caught "
|
2021-02-24 22:00:06 +01:00
|
|
|
"as a (const) reference which is usually recommended in C++.", CWE398, Certainty::normal);
|
2012-02-02 16:17:42 +01:00
|
|
|
}
|
|
|
|
|
2014-11-20 14:20:09 +01:00
|
|
|
void noexceptThrowError(const Token * const tok) {
|
2021-02-24 22:00:06 +01:00
|
|
|
reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal);
|
2014-05-04 20:47:20 +02:00
|
|
|
}
|
|
|
|
|
2014-04-20 08:58:36 +02:00
|
|
|
/** Missing exception specification */
|
2014-11-20 14:20:09 +01:00
|
|
|
void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) {
|
2018-04-04 21:51:31 +02:00
|
|
|
const std::string str1(tok1 ? tok1->str() : "foo");
|
2018-04-08 22:54:10 +02:00
|
|
|
const std::list<const Token*> locationList = { tok1, tok2 };
|
2014-05-02 06:57:23 +02:00
|
|
|
reportError(locationList, Severity::style, "unhandledExceptionSpecification",
|
2014-04-20 08:58:36 +02:00
|
|
|
"Unhandled exception specification when calling function " + str1 + "().\n"
|
|
|
|
"Unhandled exception specification when calling function " + str1 + "(). "
|
2021-02-24 22:00:06 +01:00
|
|
|
"Either use a try/catch around the function call, or add a exception specification for " + funcname + "() also.", CWE703, Certainty::inconclusive);
|
2014-04-20 08:58:36 +02:00
|
|
|
}
|
|
|
|
|
2010-03-13 21:12:18 +01:00
|
|
|
/** Generate all possible errors (for --errorlist) */
|
2019-01-12 07:37:42 +01:00
|
|
|
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE {
|
2016-05-07 16:30:54 +02:00
|
|
|
CheckExceptionSafety c(nullptr, settings, errorLogger);
|
|
|
|
c.destructorsError(nullptr, "Class");
|
|
|
|
c.deallocThrowError(nullptr, "p");
|
|
|
|
c.rethrowCopyError(nullptr, "varname");
|
|
|
|
c.catchExceptionByValueError(nullptr);
|
|
|
|
c.noexceptThrowError(nullptr);
|
2017-08-09 20:00:26 +02:00
|
|
|
c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname");
|
2009-10-19 20:57:11 +02:00
|
|
|
}
|
|
|
|
|
2010-03-13 21:12:18 +01:00
|
|
|
/** Short description of class (for --doc) */
|
2014-11-20 14:20:09 +01:00
|
|
|
static std::string myName() {
|
2009-10-19 20:57:11 +02:00
|
|
|
return "Exception Safety";
|
|
|
|
}
|
|
|
|
|
2010-03-13 21:12:18 +01:00
|
|
|
/** wiki formatted description of the class (for --doc) */
|
2019-01-12 07:37:42 +01:00
|
|
|
std::string classInfo() const OVERRIDE {
|
2009-10-19 20:57:11 +02:00
|
|
|
return "Checking exception safety\n"
|
2014-09-30 14:56:12 +02:00
|
|
|
"- Throwing exceptions in destructors\n"
|
|
|
|
"- Throwing exception during invalid state\n"
|
|
|
|
"- Throwing a copy of a caught exception instead of rethrowing the original exception\n"
|
|
|
|
"- Exception caught by value instead of by reference\n"
|
2015-01-09 20:18:09 +01:00
|
|
|
"- Throwing exception in noexcept, nothrow(), __attribute__((nothrow)) or __declspec(nothrow) function\n"
|
2014-09-30 14:56:12 +02:00
|
|
|
"- Unhandled exception specification when calling function foo()\n";
|
2009-10-19 20:57:11 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
/// @}
|
|
|
|
//---------------------------------------------------------------------------
|
2013-09-04 20:59:49 +02:00
|
|
|
#endif // checkexceptionsafetyH
|