forked from danmar/cppcheck
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcheckexceptionsafety.h
167 lines (139 loc) · 7.21 KB
/
checkexceptionsafety.h
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
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2019 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 checkexceptionsafetyH
#define checkexceptionsafetyH
//---------------------------------------------------------------------------
#include "check.h"
#include "config.h"
#include "errorlogger.h"
#include "token.h"
#include "tokenize.h"
#include "utils.h"
#include <list>
#include <string>
class Settings;
// CWE ID used:
static const struct CWE CWE398(398U); // Indicator of Poor Code Quality
static const struct CWE CWE703(703U); // Improper Check or Handling of Exceptional Conditions
/// @addtogroup Checks
/// @{
/**
* @brief %Check exception safety (exceptions shouldn't cause leaks nor corrupt data)
*
* 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.
*/
class CPPCHECKLIB CheckExceptionSafety : public Check {
public:
/** This constructor is used when registering the CheckClass */
CheckExceptionSafety() : Check(myName()) {
}
/** This constructor is used when running checks. */
CheckExceptionSafety(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
: Check(myName(), tokenizer, settings, errorLogger) {
}
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) OVERRIDE {
if (tokenizer->isC())
return;
CheckExceptionSafety checkExceptionSafety(tokenizer, settings, errorLogger);
checkExceptionSafety.destructors();
checkExceptionSafety.deallocThrow();
checkExceptionSafety.checkRethrowCopy();
checkExceptionSafety.checkCatchExceptionByValue();
checkExceptionSafety.nothrowThrows();
checkExceptionSafety.unhandledExceptionSpecification();
}
/** Don't throw exceptions in destructors */
void destructors();
/** deallocating memory and then throw (dead pointer) */
void deallocThrow();
/** Don't rethrow a copy of the caught exception; use a bare throw instead */
void checkRethrowCopy();
/** @brief %Check for exceptions that are caught by value instead of by reference */
void checkCatchExceptionByValue();
/** @brief %Check for functions that throw that shouldn't */
void nothrowThrows();
/** @brief %Check for unhandled exception specification */
void unhandledExceptionSpecification();
private:
/** Don't throw exceptions in destructors */
void destructorsError(const Token * const tok, const std::string &className) {
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 "
"is thrown that is caught in an outer scope the program will terminate.", CWE398, false);
}
void deallocThrowError(const Token * const tok, const std::string &varname) {
reportError(tok, Severity::warning, "exceptDeallocThrow", "Exception thrown in invalid state, '" +
varname + "' points at deallocated memory.", CWE398, false);
}
void rethrowCopyError(const Token * const tok, const std::string &varname) {
reportError(tok, Severity::style, "exceptRethrowCopy",
"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 + "'. "
"To rethrow the caught exception without unnecessary copying or slicing, use a bare 'throw;'.", CWE398, false);
}
void catchExceptionByValueError(const Token *tok) {
reportError(tok, Severity::style,
"catchExceptionByValue", "Exception should be caught by reference.\n"
"The exception is caught by value. It could be caught "
"as a (const) reference which is usually recommended in C++.", CWE398, false);
}
void noexceptThrowError(const Token * const tok) {
reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, false);
}
/** Missing exception specification */
void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) {
const std::string str1(tok1 ? tok1->str() : "foo");
const std::list<const Token*> locationList = { tok1, tok2 };
reportError(locationList, Severity::style, "unhandledExceptionSpecification",
"Unhandled exception specification when calling function " + str1 + "().\n"
"Unhandled exception specification when calling function " + str1 + "(). "
"Either use a try/catch around the function call, or add a exception specification for " + funcname + "() also.", CWE703, true);
}
/** Generate all possible errors (for --errorlist) */
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const OVERRIDE {
CheckExceptionSafety c(nullptr, settings, errorLogger);
c.destructorsError(nullptr, "Class");
c.deallocThrowError(nullptr, "p");
c.rethrowCopyError(nullptr, "varname");
c.catchExceptionByValueError(nullptr);
c.noexceptThrowError(nullptr);
c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname");
}
/** Short description of class (for --doc) */
static std::string myName() {
return "Exception Safety";
}
/** wiki formatted description of the class (for --doc) */
std::string classInfo() const OVERRIDE {
return "Checking exception safety\n"
"- 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"
"- Throwing exception in noexcept, nothrow(), __attribute__((nothrow)) or __declspec(nothrow) function\n"
"- Unhandled exception specification when calling function foo()\n";
}
};
/// @}
//---------------------------------------------------------------------------
#endif // checkexceptionsafetyH