Skip to content

Commit 28dca3f

Browse files
authored
Merge pull request #8245 from ihsinme/ihsinme-patch-67
CPP: Add query for CWE-476: NULL Pointer Dereference when using exception handling blocks
2 parents 53b26eb + b98ddc7 commit 28dca3f

File tree

6 files changed

+534
-0
lines changed

6 files changed

+534
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
...
2+
try {
3+
if (checkValue) throw exception();
4+
bufMyData = new myData*[sizeInt];
5+
6+
}
7+
catch (...)
8+
{
9+
for (size_t i = 0; i < sizeInt; i++)
10+
{
11+
delete[] bufMyData[i]->buffer; // BAD
12+
delete bufMyData[i];
13+
}
14+
...
15+
try {
16+
if (checkValue) throw exception();
17+
bufMyData = new myData*[sizeInt];
18+
19+
}
20+
catch (...)
21+
{
22+
for (size_t i = 0; i < sizeInt; i++)
23+
{
24+
if(bufMyData[i])
25+
{
26+
delete[] bufMyData[i]->buffer; // GOOD
27+
delete bufMyData[i];
28+
}
29+
}
30+
31+
...
32+
catch (const exception &) {
33+
delete valData;
34+
throw;
35+
}
36+
catch (...)
37+
{
38+
delete valData; // BAD
39+
...
40+
catch (const exception &) {
41+
delete valData;
42+
valData = NULL;
43+
throw;
44+
}
45+
catch (...)
46+
{
47+
delete valData; // GOOD
48+
...
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>When releasing memory in a catch block, be sure that the memory was allocated and has not already been released.</p>
7+
8+
</overview>
9+
10+
<example>
11+
<p>The following example shows erroneous and fixed ways to use exception handling.</p>
12+
<sample src="DangerousUseOfExceptionBlocks.cpp" />
13+
14+
</example>
15+
<references>
16+
17+
<li>
18+
CERT C Coding Standard:
19+
<a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers">EXP34-C. Do not dereference null pointers</a>.
20+
</li>
21+
22+
</references>
23+
</qhelp>
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/**
2+
* @name Dangerous use of exception blocks.
3+
* @description When clearing the data in the catch block, you must be sure that the memory was allocated before the exception.
4+
* @kind problem
5+
* @id cpp/dangerous-use-of-exception-blocks
6+
* @problem.severity warning
7+
* @precision medium
8+
* @tags correctness
9+
* security
10+
* external/cwe/cwe-476
11+
* external/cwe/cwe-415
12+
*/
13+
14+
import cpp
15+
16+
/** Holds if `vr` may be released in the `try` block associated with `cb`, or in a `catch` block prior to `cb`. */
17+
pragma[inline]
18+
predicate doubleCallDelete(BlockStmt b, CatchAnyBlock cb, Variable vr) {
19+
// Search for exceptions after freeing memory.
20+
exists(Expr e1 |
21+
// `e1` is a delete of `vr`
22+
(
23+
e1 = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr().(DeleteArrayExpr) or
24+
e1 = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr().(DeleteExpr)
25+
) and
26+
e1.getEnclosingFunction() = cb.getEnclosingFunction() and
27+
// there is no assignment `vr = 0` in the `try` block after `e1`
28+
not exists(AssignExpr ae |
29+
ae.getLValue().(VariableAccess).getTarget() = vr and
30+
ae.getRValue().getValue() = "0" and
31+
e1.getASuccessor+() = ae and
32+
ae.getEnclosingStmt().getParentStmt*() = b
33+
) and
34+
// `e2` is a `throw` (or a function call that may throw) that occurs in the `try` or `catch` block after `e1`
35+
exists(Expr e2, ThrowExpr th |
36+
(
37+
e2 = th or
38+
e2 = th.getEnclosingFunction().getACallToThisFunction()
39+
) and
40+
e2.getEnclosingStmt().getParentStmt*() = b and
41+
e1.getASuccessor+() = e2
42+
) and
43+
e1.getEnclosingStmt().getParentStmt*() = b and
44+
(
45+
// Search for a situation where there is a release in the block of `try`.
46+
b = cb.getTryStmt().getStmt()
47+
or
48+
// Search for a situation when there is a higher catch block that also frees memory.
49+
exists(b.(CatchBlock).getParameter())
50+
) and
51+
// Exclude the presence of a check in catch block.
52+
not exists(IfStmt ifst | ifst.getEnclosingStmt().getParentStmt*() = cb.getAStmt())
53+
)
54+
}
55+
56+
/** Holds if an exception can be thrown before the memory is allocated, and when the exception is handled, an attempt is made to access unallocated memory in the catch block. */
57+
pragma[inline]
58+
predicate pointerDereference(CatchAnyBlock cb, Variable vr, Variable vro) {
59+
// Search exceptions before allocating memory.
60+
exists(Expr e0, Expr e1 |
61+
(
62+
// `e0` is a `new` expression (or equivalent function call) assigned to `vro`
63+
exists(AssignExpr ase |
64+
ase = vro.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
65+
(
66+
e0 = ase.getRValue().(NewOrNewArrayExpr) or
67+
e0 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
68+
) and
69+
vro = ase.getLValue().(VariableAccess).getTarget()
70+
)
71+
or
72+
// `e0` is a `new` expression (or equivalent function call) assigned to the array element `vro`
73+
exists(AssignExpr ase |
74+
ase = vro.getAnAccess().(Qualifier).getEnclosingStmt().(ExprStmt).getExpr() and
75+
(
76+
e0 = ase.getRValue().(NewOrNewArrayExpr) or
77+
e0 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
78+
) and
79+
not ase.getLValue() instanceof VariableAccess and
80+
vro = ase.getLValue().getAPredecessor().(VariableAccess).getTarget()
81+
)
82+
) and
83+
// `e1` is a `new` expression (or equivalent function call) assigned to `vr`
84+
exists(AssignExpr ase |
85+
ase = vr.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
86+
(
87+
e1 = ase.getRValue().(NewOrNewArrayExpr) or
88+
e1 = ase.getRValue().(NewOrNewArrayExpr).getEnclosingFunction().getACallToThisFunction()
89+
) and
90+
vr = ase.getLValue().(VariableAccess).getTarget()
91+
) and
92+
e0.getASuccessor*() = e1 and
93+
e0.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
94+
e1.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
95+
// `e2` is a `throw` (or a function call that may throw) that occurs in the `try` block before `e0`
96+
exists(Expr e2, ThrowExpr th |
97+
(
98+
e2 = th or
99+
e2 = th.getEnclosingFunction().getACallToThisFunction()
100+
) and
101+
e2.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
102+
e2.getASuccessor+() = e0
103+
)
104+
) and
105+
// We exclude checking the value of a variable or its parent in the catch block.
106+
not exists(IfStmt ifst |
107+
ifst.getEnclosingStmt().getParentStmt*() = cb.getAStmt() and
108+
(
109+
ifst.getCondition().getAChild*().(VariableAccess).getTarget() = vr or
110+
ifst.getCondition().getAChild*().(VariableAccess).getTarget() = vro
111+
)
112+
)
113+
}
114+
115+
/** Holds if `vro` may be released in the `catch`. */
116+
pragma[inline]
117+
predicate newThrowDelete(CatchAnyBlock cb, Variable vro) {
118+
exists(Expr e0, AssignExpr ase, NewOrNewArrayExpr nae |
119+
ase = vro.getAnAccess().getEnclosingStmt().(ExprStmt).getExpr() and
120+
nae = ase.getRValue() and
121+
not nae.getAChild*().toString() = "nothrow" and
122+
(
123+
e0 = nae or
124+
e0 = nae.getEnclosingFunction().getACallToThisFunction()
125+
) and
126+
vro = ase.getLValue().(VariableAccess).getTarget() and
127+
e0.getEnclosingStmt().getParentStmt*() = cb.getTryStmt().getStmt() and
128+
not exists(AssignExpr ase1 |
129+
vro = ase1.getLValue().(VariableAccess).getTarget() and
130+
ase1.getRValue().getValue() = "0" and
131+
ase1.getASuccessor*() = e0
132+
)
133+
) and
134+
not exists(Initializer it |
135+
vro.getInitializer() = it and
136+
it.getExpr().getValue() = "0"
137+
) and
138+
not exists(ConstructorFieldInit ci | vro = ci.getTarget())
139+
}
140+
141+
from CatchAnyBlock cb, string msg
142+
where
143+
exists(Variable vr, Variable vro, Expr exp |
144+
exp.getEnclosingStmt().getParentStmt*() = cb and
145+
exists(VariableAccess va |
146+
(
147+
(
148+
va = exp.(DeleteArrayExpr).getExpr().getAPredecessor+().(Qualifier) or
149+
va = exp.(DeleteArrayExpr).getExpr().getAPredecessor+()
150+
) and
151+
vr = exp.(DeleteArrayExpr).getExpr().(VariableAccess).getTarget()
152+
or
153+
(
154+
va = exp.(DeleteExpr).getExpr().getAPredecessor+().(Qualifier) or
155+
va = exp.(DeleteExpr).getExpr().getAPredecessor+()
156+
) and
157+
vr = exp.(DeleteExpr).getExpr().(VariableAccess).getTarget()
158+
) and
159+
va.getEnclosingStmt() = exp.getEnclosingStmt() and
160+
vro = va.getTarget() and
161+
vr != vro
162+
) and
163+
pointerDereference(cb, vr, vro) and
164+
msg =
165+
"it is possible to dereference a pointer when accessing a " + vr.getName() +
166+
", since it is possible to throw an exception before the memory for the " + vro.getName() +
167+
" is allocated"
168+
)
169+
or
170+
exists(Expr exp, Variable vr |
171+
(
172+
exp.(DeleteExpr).getEnclosingStmt().getParentStmt*() = cb and
173+
vr = exp.(DeleteExpr).getExpr().(VariableAccess).getTarget()
174+
or
175+
exp.(DeleteArrayExpr).getEnclosingStmt().getParentStmt*() = cb and
176+
vr = exp.(DeleteArrayExpr).getExpr().(VariableAccess).getTarget()
177+
) and
178+
doubleCallDelete(_, cb, vr) and
179+
msg =
180+
"This allocation may have been released in the try block or a previous catch block." +
181+
vr.getName()
182+
)
183+
or
184+
exists(Variable vro, Expr exp |
185+
exp.getEnclosingStmt().getParentStmt*() = cb and
186+
exists(VariableAccess va |
187+
(
188+
va = exp.(DeleteArrayExpr).getExpr() or
189+
va = exp.(DeleteExpr).getExpr()
190+
) and
191+
va.getEnclosingStmt() = exp.getEnclosingStmt() and
192+
vro = va.getTarget()
193+
) and
194+
newThrowDelete(cb, vro) and
195+
msg =
196+
"If the allocation in the try block fails, then an unallocated pointer " + vro.getName() +
197+
" will be freed in the catch block."
198+
)
199+
select cb, msg
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| test.cpp:63:3:71:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer bufMyData will be freed in the catch block. |
2+
| test.cpp:63:3:71:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
3+
| test.cpp:63:3:71:3 | { ... } | it is possible to dereference a pointer when accessing a buffer, since it is possible to throw an exception before the memory for the bufMyData is allocated |
4+
| test.cpp:91:3:100:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
5+
| test.cpp:120:3:128:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
6+
| test.cpp:143:3:151:3 | { ... } | If the allocation in the try block fails, then an unallocated pointer buffer will be freed in the catch block. |
7+
| test.cpp:181:3:183:3 | { ... } | This allocation may have been released in the try block or a previous catch block.valData |
8+
| test.cpp:219:3:221:3 | { ... } | This allocation may have been released in the try block or a previous catch block.valData |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-476/DangerousUseOfExceptionBlocks.ql

0 commit comments

Comments
 (0)