Skip to content

Commit 7c54512

Browse files
authored
Merge pull request #5010 from ihsinme/ihsinme-patch-220
CPP: Add query for CWE-570 detect and handle memory allocation errors.
2 parents 35e620a + 43045c1 commit 7c54512

File tree

6 files changed

+252
-0
lines changed

6 files changed

+252
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// BAD: on memory allocation error, the program terminates.
2+
void badFunction(const int *source, std::size_t length) noexcept {
3+
int * dest = new int[length];
4+
std::memset(dest, 0, length);
5+
// ..
6+
}
7+
// GOOD: memory allocation error will be handled.
8+
void goodFunction(const int *source, std::size_t length) noexcept {
9+
try {
10+
int * dest = new int[length];
11+
} catch(std::bad_alloc) {
12+
// ...
13+
}
14+
std::memset(dest, 0, length);
15+
// ..
16+
}
17+
// BAD: memory allocation error will not be handled.
18+
void badFunction(const int *source, std::size_t length) noexcept {
19+
try {
20+
int * dest = new (std::nothrow) int[length];
21+
} catch(std::bad_alloc) {
22+
// ...
23+
}
24+
std::memset(dest, 0, length);
25+
// ..
26+
}
27+
// GOOD: memory allocation error will be handled.
28+
void goodFunction(const int *source, std::size_t length) noexcept {
29+
int * dest = new (std::nothrow) int[length];
30+
if (!dest) {
31+
return;
32+
}
33+
std::memset(dest, 0, length);
34+
// ..
35+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>When using the <code>new</code> operator to allocate memory, you need to pay attention to the different ways of detecting errors. <code>::operator new(std::size_t)</code> throws an exception on error, whereas <code>::operator new(std::size_t, const std::nothrow_t &amp;)</code> returns zero on error. The programmer can get confused and check the error that occurs when allocating memory incorrectly. That can lead to an unhandled program termination or to a violation of the program logic.</p>
7+
8+
</overview>
9+
<recommendation>
10+
11+
<p>Use the correct error detection method corresponding with the memory allocation.</p>
12+
13+
</recommendation>
14+
<example>
15+
<p>The following example demonstrates various approaches to detecting memory allocation errors using the <code>new</code> operator.</p>
16+
<sample src="WrongInDetectingAndHandlingMemoryAllocationErrors.cpp" />
17+
18+
</example>
19+
<references>
20+
21+
<li>
22+
CERT C++ Coding Standard:
23+
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM52-CPP.+Detect+and+handle+memory+allocation+errors">MEM52-CPP. Detect and handle memory allocation errors</a>.
24+
</li>
25+
26+
</references>
27+
</qhelp>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @name Detect And Handle Memory Allocation Errors
3+
* @description --::operator new(std::size_t) throws an exception on error, and ::operator new(std::size_t, const std::nothrow_t &) returns zero on error.
4+
* --the programmer can get confused when check the error that occurs when allocating memory incorrectly.
5+
* @kind problem
6+
* @id cpp/detect-and-handle-memory-allocation-errors
7+
* @problem.severity warning
8+
* @precision medium
9+
* @tags correctness
10+
* security
11+
* external/cwe/cwe-570
12+
*/
13+
14+
import cpp
15+
16+
/**
17+
* Lookup if condition compare with 0
18+
*/
19+
class IfCompareWithZero extends IfStmt {
20+
IfCompareWithZero() {
21+
this.getCondition().(EQExpr).getAChild().getValue() = "0"
22+
or
23+
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
24+
this.hasElse()
25+
or
26+
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
27+
this.getThen().getAChild*() instanceof ReturnStmt
28+
}
29+
}
30+
31+
/**
32+
* lookup for calls to `operator new`, with incorrect error handling.
33+
*/
34+
class WrongCheckErrorOperatorNew extends FunctionCall {
35+
Expr exp;
36+
37+
WrongCheckErrorOperatorNew() {
38+
this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and
39+
(
40+
this.getTarget().hasGlobalOrStdName("operator new")
41+
or
42+
this.getTarget().hasGlobalOrStdName("operator new[]")
43+
)
44+
}
45+
46+
/**
47+
* Holds if handler `try ... catch` exists.
48+
*/
49+
predicate isExistsTryCatchBlock() {
50+
exists(TryStmt ts | this.getEnclosingStmt() = ts.getStmt().getAChild*())
51+
}
52+
53+
/**
54+
* Holds if results call `operator new` check in `operator if`.
55+
*/
56+
predicate isExistsIfCondition() {
57+
exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it |
58+
// call `operator new` directly from the condition of `operator if`.
59+
this = ifc.getCondition().getAChild*()
60+
or
61+
// check results call `operator new` with variable appropriation
62+
postDominates(ifc, this) and
63+
aex.getAChild() = exp and
64+
ifc.getCondition().getAChild().(VariableAccess).getTarget() =
65+
aex.getLValue().(VariableAccess).getTarget()
66+
or
67+
// check results call `operator new` with declaration variable
68+
postDominates(ifc, this) and
69+
exp = it.getExpr() and
70+
it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget()
71+
)
72+
}
73+
74+
/**
75+
* Holds if `(std::nothrow)` exists in call `operator new`.
76+
*/
77+
predicate isExistsNothrow() { this.getAChild().toString() = "nothrow" }
78+
}
79+
80+
from WrongCheckErrorOperatorNew op
81+
where
82+
// use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if`
83+
op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock()
84+
or
85+
// use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block
86+
not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition()
87+
select op, "memory allocation error check is incorrect or missing"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| test.cpp:30:15:30:26 | call to operator new[] | memory allocation error check is incorrect or missing |
2+
| test.cpp:38:9:38:20 | call to operator new[] | memory allocation error check is incorrect or missing |
3+
| test.cpp:50:13:50:38 | call to operator new[] | memory allocation error check is incorrect or missing |
4+
| test.cpp:51:22:51:47 | call to operator new[] | memory allocation error check is incorrect or missing |
5+
| test.cpp:53:18:53:43 | call to operator new[] | memory allocation error check is incorrect or missing |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#define NULL ((void*)0)
2+
class exception {};
3+
4+
namespace std{
5+
struct nothrow_t {};
6+
typedef unsigned long size_t;
7+
class bad_alloc{
8+
const char* what() const throw();
9+
};
10+
extern const std::nothrow_t nothrow;
11+
}
12+
13+
using namespace std;
14+
15+
void* operator new(std::size_t _Size);
16+
void* operator new[](std::size_t _Size);
17+
void* operator new( std::size_t count, const std::nothrow_t& tag );
18+
void* operator new[]( std::size_t count, const std::nothrow_t& tag );
19+
20+
void badNew_0_0()
21+
{
22+
while (true) {
23+
new int[100]; // BAD [NOT DETECTED]
24+
if(!(new int[100])) // BAD [NOT DETECTED]
25+
return;
26+
}
27+
}
28+
void badNew_0_1()
29+
{
30+
int * i = new int[100]; // BAD
31+
if(i == 0)
32+
return;
33+
if(!i)
34+
return;
35+
if(i == NULL)
36+
return;
37+
int * j;
38+
j = new int[100]; // BAD
39+
if(j == 0)
40+
return;
41+
if(!j)
42+
return;
43+
if(j == NULL)
44+
return;
45+
}
46+
void badNew_1_0()
47+
{
48+
try {
49+
while (true) {
50+
new(std::nothrow) int[100]; // BAD
51+
int* p = new(std::nothrow) int[100]; // BAD
52+
int* p1;
53+
p1 = new(std::nothrow) int[100]; // BAD
54+
}
55+
} catch (const exception &){//const std::bad_alloc& e) {
56+
// std::cout << e.what() << '\n';
57+
}
58+
}
59+
void badNew_1_1()
60+
{
61+
while (true) {
62+
int* p = new(std::nothrow) int[100]; // BAD [NOT DETECTED]
63+
new(std::nothrow) int[100]; // BAD [NOT DETECTED]
64+
}
65+
}
66+
67+
void goodNew_0_0()
68+
{
69+
try {
70+
while (true) {
71+
new int[100]; // GOOD
72+
}
73+
} catch (const exception &){//const std::bad_alloc& e) {
74+
// std::cout << e.what() << '\n';
75+
}
76+
}
77+
78+
void goodNew_1_0()
79+
{
80+
while (true) {
81+
int* p = new(std::nothrow) int[100]; // GOOD
82+
if (p == nullptr) {
83+
// std::cout << "Allocation returned nullptr\n";
84+
break;
85+
}
86+
int* p1;
87+
p1 = new(std::nothrow) int[100]; // GOOD
88+
if (p1 == nullptr) {
89+
// std::cout << "Allocation returned nullptr\n";
90+
break;
91+
}
92+
if (new(std::nothrow) int[100] == nullptr) { // GOOD
93+
// std::cout << "Allocation returned nullptr\n";
94+
break;
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)