Skip to content

Commit caf4a76

Browse files
authored
Merge pull request #219 from geoffw0/resource-not-released
C++: Exclude placement new from AV Rule 79.ql
2 parents e21a5e4 + 492d79e commit caf4a76

File tree

5 files changed

+165
-62
lines changed

5 files changed

+165
-62
lines changed

change-notes/1.19/analysis-cpp.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
| **Query** | **Expected impact** | **Change** |
1414
|----------------------------|------------------------|------------------------------------------------------------------|
15-
| *@name of query (Query ID)*| *Impact on results* | *How/why the query has changed* |
15+
| Resource not released in destructor | Fewer false positive results | Placement new is now excluded from the query. |
1616

1717

1818
## Changes to QL libraries

cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql

Lines changed: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,69 @@
1010
* external/cwe/cwe-404
1111
*/
1212
import cpp
13+
import Critical.NewDelete
1314

14-
// List pairs of functions that do resource acquisition/release
15-
// Extend this to add custom function pairs. As written the query
16-
// will only apply if the resource is the *return value* of the
17-
// first call and a *parameter* to the second. Other cases should
18-
// be handled differently.
19-
predicate resourceManagementPair(string acquire, string release) {
20-
21-
(acquire = "fopen" and release = "fclose")
22-
or
23-
(acquire = "open" and release = "close")
24-
or
25-
(acquire = "socket" and release = "close")
26-
27-
}
28-
29-
// List functions that return malloc-allocated memory. Customize
30-
// to list your own functions there
31-
predicate mallocFunction(Function malloc) {
32-
malloc.hasName("malloc") or malloc.hasName("calloc") or // Not realloc: doesn't acquire it, really
33-
malloc.hasName("strdup")
15+
/**
16+
* An expression that acquires a resource, and the kind of resource that is acquired. The
17+
* kind of a resource indicates which acquisition/release expressions can be paired.
18+
*/
19+
predicate acquireExpr(Expr acquire, string kind) {
20+
exists(FunctionCall fc, Function f, string name |
21+
fc = acquire and
22+
f = fc.getTarget() and
23+
name = f.getName() and
24+
(
25+
(
26+
name = "fopen" and
27+
kind = "file"
28+
) or (
29+
name = "open" and
30+
kind = "file descriptor"
31+
) or (
32+
name = "socket" and
33+
kind = "file descriptor"
34+
)
35+
)
36+
) or (
37+
allocExpr(acquire, kind)
38+
)
3439
}
3540

36-
private predicate isRelease(string release) {
37-
resourceManagementPair(_, release) or
38-
release = "free" or
39-
release = "delete"
41+
/**
42+
* An expression that releases a resource, and the kind of resource that is released. The
43+
* kind of a resource indicates which acquisition/release expressions can be paired.
44+
*/
45+
predicate releaseExpr(Expr release, Expr resource, string kind) {
46+
exists(FunctionCall fc, Function f, string name |
47+
fc = release and
48+
f = fc.getTarget() and
49+
name = f.getName() and
50+
(
51+
(
52+
name = "fclose" and
53+
resource = fc.getArgument(0) and
54+
kind = "file"
55+
) or (
56+
name = "close" and
57+
resource = fc.getArgument(0) and
58+
kind = "file descriptor"
59+
)
60+
)
61+
) or exists(string releaseKind |
62+
freeExpr(release, resource, releaseKind) and
63+
(
64+
(
65+
kind = "malloc" and
66+
releaseKind = "free"
67+
) or (
68+
kind = "new" and
69+
releaseKind = "delete"
70+
) or (
71+
kind = "new[]" and
72+
releaseKind = "delete[]"
73+
)
74+
)
75+
)
4076
}
4177

4278
/**
@@ -52,35 +88,23 @@ Expr exprOrDereference(Expr e) {
5288
* Holds if the expression `e` releases expression `released`, whether directly
5389
* or via one or more function call(s).
5490
*/
55-
private predicate exprReleases(Expr e, Expr released, string releaseType) {
91+
private predicate exprReleases(Expr e, Expr released, string kind) {
5692
(
57-
// `e` is a call to a release function and `released` is any argument
58-
e.(FunctionCall).getTarget().getName() = releaseType and
59-
isRelease(releaseType) and
60-
e.(FunctionCall).getAnArgument() = released
61-
) or (
62-
// `e` is a call to `delete` and `released` is the target
63-
e.(DeleteExpr).getExpr() = released and
64-
releaseType = "delete"
65-
) or (
66-
// `e` is a call to `delete[]` and `released` is the target
67-
e.(DeleteArrayExpr).getExpr() = released and
68-
releaseType = "delete"
93+
// `e` is a call to a release function and `released` is the released argument
94+
releaseExpr(e, released, kind)
6995
) or exists(Function f, int arg |
7096
// `e` is a call to a function that releases one of it's parameters,
7197
// and `released` is the corresponding argument
7298
e.(FunctionCall).getTarget() = f and
7399
e.(FunctionCall).getArgument(arg) = released and
74-
exprReleases(_, exprOrDereference(f.getParameter(arg).getAnAccess()), releaseType)
75-
) or exists(Function f, Expr innerThis |
100+
exprReleases(_, exprOrDereference(f.getParameter(arg).getAnAccess()), kind)
101+
) or exists(Function f, ThisExpr innerThis |
76102
// `e` is a call to a method that releases `this`, and `released`
77103
// is the object that is called
78104
e.(FunctionCall).getTarget() = f and
79105
e.(FunctionCall).getQualifier() = exprOrDereference(released) and
80106
innerThis.getEnclosingFunction() = f and
81-
exprReleases(_, innerThis, releaseType) and
82-
innerThis instanceof ThisExpr and
83-
releaseType = "delete"
107+
exprReleases(_, innerThis, kind)
84108
)
85109
}
86110

@@ -109,28 +133,17 @@ class Resource extends MemberVariable {
109133
)
110134
}
111135

112-
predicate acquisitionWithRequiredRelease(Expr acquire, string releaseName) {
113-
acquire.(Assignment).getLValue() = this.getAnAccess() and
136+
predicate acquisitionWithRequiredRelease(Assignment acquireAssign, string kind) {
137+
// acquireAssign is an assignment to this resource
138+
acquireAssign.(Assignment).getLValue() = this.getAnAccess() and
114139
// Should be in this class, but *any* member method will do
115-
this.inSameClass(acquire) and
140+
this.inSameClass(acquireAssign) and
116141
// Check that it is an acquisition function and return the corresponding free
117-
(
118-
exists(Function f | f = acquire.(Assignment).getRValue().(FunctionCall).getTarget() and
119-
(resourceManagementPair(f.getName(), releaseName) or (mallocFunction(f) and (releaseName = "free" or releaseName = "delete")))
120-
)
121-
or
122-
(acquire = this.getANew() and releaseName = "delete")
123-
)
124-
}
125-
126-
private Assignment getANew() {
127-
result.getLValue() = this.getAnAccess() and
128-
(result.getRValue() instanceof NewExpr or result.getRValue() instanceof NewArrayExpr) and
129-
this.inSameClass(result)
142+
acquireExpr(acquireAssign.getRValue(), kind)
130143
}
131144

132-
Expr getAReleaseExpr(string releaseName) {
133-
exprReleases(result, this.getAnAccess(), releaseName)
145+
Expr getAReleaseExpr(string kind) {
146+
exprReleases(result, this.getAnAccess(), kind)
134147
}
135148
}
136149

cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/AV Rule 79.expected

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,9 @@
1111
| ExternalOwners.cpp:49:3:49:20 | ... = ... | Resource a is acquired by class MyScreen but not released anywhere in this class. |
1212
| ListDelete.cpp:21:3:21:21 | ... = ... | Resource first is acquired by class MyThingColection but not released anywhere in this class. |
1313
| NoDestructor.cpp:23:3:23:20 | ... = ... | Resource n is acquired by class MyClass5 but not released anywhere in this class. |
14+
| PlacementNew.cpp:36:3:36:36 | ... = ... | Resource p1 is acquired by class MyTestForPlacementNew but not released anywhere in this class. |
1415
| SelfRegistering.cpp:25:3:25:24 | ... = ... | Resource side is acquired by class MyOwner but not released anywhere in this class. |
15-
| Variants.cpp:23:3:23:13 | ... = ... | Resource f is acquired by class MyClass4 but not released anywhere in this class. |
16+
| Variants.cpp:25:3:25:13 | ... = ... | Resource f is acquired by class MyClass4 but not released anywhere in this class. |
17+
| Variants.cpp:65:3:65:17 | ... = ... | Resource a is acquired by class MyClass6 but not released anywhere in this class. |
18+
| Variants.cpp:66:3:66:36 | ... = ... | Resource b is acquired by class MyClass6 but not released anywhere in this class. |
19+
| Variants.cpp:67:3:67:41 | ... = ... | Resource c is acquired by class MyClass6 but not released anywhere in this class. |
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
typedef unsigned long size_t;
3+
4+
namespace std
5+
{
6+
using ::size_t;
7+
struct nothrow_t {};
8+
extern const nothrow_t nothrow;
9+
}
10+
11+
// nothrow new
12+
void* operator new(std::size_t size, const std::nothrow_t&) throw();
13+
14+
// placement new
15+
void* operator new (std::size_t size, void* ptr) throw();
16+
17+
// ---
18+
19+
class MyClassForPlacementNew
20+
{
21+
public:
22+
MyClassForPlacementNew(int _v) : v(_v) {}
23+
~MyClassForPlacementNew() {}
24+
25+
private:
26+
int v;
27+
};
28+
29+
class MyTestForPlacementNew
30+
{
31+
public:
32+
MyTestForPlacementNew()
33+
{
34+
void *buffer_ptr = buffer;
35+
36+
p1 = new MyClassForPlacementNew(1); // BAD: not released
37+
p2 = new (std::nothrow) MyClassForPlacementNew(2); // BAD: not released [NOT DETECTED]
38+
p3 = new (buffer_ptr) MyClassForPlacementNew(3); // GOOD: placement new, not an allocation
39+
}
40+
41+
~MyTestForPlacementNew()
42+
{
43+
}
44+
45+
MyClassForPlacementNew *p1, *p2, *p3;
46+
char buffer[sizeof(MyClassForPlacementNew)];
47+
};

cpp/ql/test/query-tests/jsf/4.10 Classes/AV Rule 79/Variants.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// library
33
typedef unsigned int size_t;
44
void *malloc(size_t size);
5+
void *calloc(size_t nmemb, size_t size);
6+
void *realloc(void *ptr, size_t size);
57
void free(void* ptr);
68

79
int *ID(int *x)
@@ -34,3 +36,40 @@ class MyClass4
3436

3537
int *a, *b, *c, *d, *e, *f, *g;
3638
};
39+
40+
class MyClass5
41+
{
42+
public:
43+
MyClass5()
44+
{
45+
a = new int[10]; // GOOD
46+
b = (int *)calloc(10, sizeof(int)); // GOOD
47+
c = (int *)realloc(0, 10 * sizeof(int)); // GOOD
48+
}
49+
50+
~MyClass5()
51+
{
52+
delete [] a;
53+
free(b);
54+
free(c);
55+
}
56+
57+
int *a, *b, *c;
58+
};
59+
60+
class MyClass6
61+
{
62+
public:
63+
MyClass6()
64+
{
65+
a = new int[10]; // BAD
66+
b = (int *)calloc(10, sizeof(int)); // BAD
67+
c = (int *)realloc(0, 10 * sizeof(int)); // BAD
68+
}
69+
70+
~MyClass6()
71+
{
72+
}
73+
74+
int *a, *b, *c;
75+
};

0 commit comments

Comments
 (0)