Skip to content

Commit 3877f03

Browse files
authored
Merge pull request #4979 from geoffw0/cpp401
C++: Improvements to experimental query cpp/memory-leak-on-failed-call-to-realloc
2 parents d0663e5 + d2dd19a commit 3877f03

File tree

3 files changed

+70
-33
lines changed

3 files changed

+70
-33
lines changed

cpp/ql/src/experimental/Security/CWE/CWE-401/MemoryLeakOnFailedCallToRealloc.ql

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@
1313

1414
import cpp
1515

16+
/**
17+
* A function call that potentially does not return (such as `exit`).
18+
*/
19+
class CallMayNotReturn extends FunctionCall {
20+
CallMayNotReturn() {
21+
// call that is known to not return
22+
not exists(this.(ControlFlowNode).getASuccessor())
23+
or
24+
// call to another function that may not return
25+
exists(CallMayNotReturn exit | getTarget() = exit.getEnclosingFunction())
26+
}
27+
}
28+
1629
/**
1730
* A call to `realloc` of the form `v = realloc(v, size)`, for some variable `v`.
1831
*/
@@ -30,40 +43,15 @@ class ReallocCallLeak extends FunctionCall {
3043
)
3144
}
3245

33-
predicate isExistsIfWithExitCall() {
34-
exists(IfStmt ifc |
35-
this.getArgument(0) = v.getAnAccess() and
36-
ifc.getCondition().getAChild*() = v.getAnAccess() and
37-
ifc.getEnclosingFunction() = this.getEnclosingFunction() and
38-
ifc.getLocation().getStartLine() >= this.getArgument(0).getLocation().getStartLine() and
39-
exists(FunctionCall fc |
40-
fc.getTarget().hasName("exit") and
41-
fc.getEnclosingFunction() = this.getEnclosingFunction() and
42-
(ifc.getThen().getAChild*() = fc or ifc.getElse().getAChild*() = fc)
43-
)
44-
or
45-
exists(FunctionCall fc, FunctionCall ftmp1, FunctionCall ftmp2 |
46-
ftmp1.getTarget().hasName("exit") and
47-
ftmp2.(ControlFlowNode).getASuccessor*() = ftmp1 and
48-
fc = ftmp2.getEnclosingFunction().getACallToThisFunction() and
49-
fc.getEnclosingFunction() = this.getEnclosingFunction() and
50-
(ifc.getThen().getAChild*() = fc or ifc.getElse().getAChild*() = fc)
51-
)
52-
)
53-
}
54-
55-
predicate isExistsAssertWithArgumentCall() {
56-
exists(FunctionCall fc |
57-
fc.getTarget().hasName("__assert_fail") and
58-
this.getEnclosingFunction() = fc.getEnclosingFunction() and
59-
fc.getLocation().getStartLine() > this.getArgument(0).getLocation().getEndLine() and
60-
fc.getArgument(0).toString().matches("%" + this.getArgument(0).toString() + "%")
61-
)
46+
/**
47+
* Holds if failure of this allocation may be handled by termination, for
48+
* example a call to `exit()`.
49+
*/
50+
predicate mayHandleByTermination() {
51+
this.(ControlFlowNode).getASuccessor*() instanceof CallMayNotReturn
6252
}
6353
}
6454

6555
from ReallocCallLeak rcl
66-
where
67-
not rcl.isExistsIfWithExitCall() and
68-
not rcl.isExistsAssertWithArgumentCall()
56+
where not rcl.mayHandleByTermination()
6957
select rcl, "possible loss of original pointer on unsuccessful call realloc"

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-401/semmle/tests/MemoryLeakOnFailedCallToRealloc.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@
22
| test.c:63:29:63:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
33
| test.c:139:29:139:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
44
| test.c:186:29:186:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
5+
| test.c:282:29:282:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
6+
| test.c:299:26:299:32 | call to realloc | possible loss of original pointer on unsuccessful call realloc |

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-401/semmle/tests/test.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#define NULL ((void*)0)
33

44
#define assert(x) if (!(x)) __assert_fail(#x,__FILE__,__LINE__)
5-
void __assert_fail(const char *assertion, const char *file, int line) { }
5+
void __assert_fail(const char *assertion, const char *file, int line);
66

77
void aFakeFailed_1(int file, int line)
88
{
@@ -272,3 +272,50 @@ unsigned char * noBadResize_2_7(unsigned char * buffer,size_t currentSize,size_t
272272
myASSERT_2(buffer);
273273
return buffer;
274274
}
275+
276+
unsigned char *goodResize_3_1(unsigned char *buffer, size_t currentSize, size_t newSize)
277+
{
278+
// GOOD: this way we will exclude possible memory leak [FALSE POSITIVE]
279+
unsigned char *tmp = buffer;
280+
if (currentSize < newSize)
281+
{
282+
buffer = (unsigned char *)realloc(buffer, newSize);
283+
if (buffer == NULL)
284+
{
285+
free(tmp);
286+
return NULL;
287+
}
288+
}
289+
290+
return buffer;
291+
}
292+
293+
unsigned char *goodResize_3_2(unsigned char *buffer, size_t currentSize, size_t newSize)
294+
{
295+
// GOOD: this way we will exclude possible memory leak [FALSE POSITIVE]
296+
unsigned char *tmp = buffer;
297+
if (currentSize < newSize)
298+
{
299+
tmp = (unsigned char *)realloc(tmp, newSize);
300+
if (tmp != 0)
301+
{
302+
buffer = tmp;
303+
}
304+
}
305+
306+
return buffer;
307+
}
308+
309+
void abort(void);
310+
311+
unsigned char *noBadResize_4_1(unsigned char *buffer, size_t currentSize, size_t newSize)
312+
{
313+
// GOOD: program to end
314+
if (currentSize < newSize)
315+
{
316+
if (buffer = (unsigned char *)realloc(buffer, newSize))
317+
abort();
318+
}
319+
320+
return buffer;
321+
}

0 commit comments

Comments
 (0)