Skip to content

Commit 586aa0a

Browse files
committed
Updated query to look for Microsoft-specific '_alloca' and '_malloca' entry points. Added sundry positive and negative test cases.
1 parent a547fbe commit 586aa0a

File tree

8 files changed

+398
-4
lines changed

8 files changed

+398
-4
lines changed

cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* correctness
99
* external/cwe/cwe-770
1010
*/
11+
1112
import cpp
1213

1314
Loop getAnEnclosingLoopOfExpr(Expr e) {
@@ -21,7 +22,15 @@ Loop getAnEnclosingLoopOfStmt(Stmt s) {
2122
}
2223

2324
from Loop l, FunctionCall fc
24-
where getAnEnclosingLoopOfExpr(fc) = l
25-
and fc.getTarget().getName() = "__builtin_alloca"
26-
and not l.(DoStmt).getCondition().getValue() = "0"
27-
select fc, "Stack allocation is inside a $@ and could lead to overflow.", l, l.toString()
25+
where
26+
getAnEnclosingLoopOfExpr(fc) = l and
27+
(
28+
fc.getTarget().getName() = "__builtin_alloca"
29+
or
30+
(
31+
(fc.getTarget().getName() = "_alloca" or fc.getTarget().getName() = "_malloca") and
32+
fc.getTarget().getADeclarationEntry().getFile().getBaseName() = "malloc.h"
33+
)
34+
) and
35+
not l.(DoStmt).getCondition().getValue() = "0"
36+
select fc, "Stack allocation is inside a $@ and could lead to stack overflow.", l, l.toString()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| AllocaInLoop1.cpp:25:18:25:23 | call to __builtin_alloca | Stack allocation is inside a $@ and could lead to stack overflow. | AllocaInLoop1.cpp:16:2:33:2 | for(...;...;...) ... | for(...;...;...) ... |
2+
| AllocaInLoop1.cpp:49:19:49:24 | call to __builtin_alloca | Stack allocation is inside a $@ and could lead to stack overflow. | AllocaInLoop1.cpp:39:2:58:2 | for(...;...;...) ... | for(...;...;...) ... |
3+
| AllocaInLoop1.cpp:74:19:74:24 | call to __builtin_alloca | Stack allocation is inside a $@ and could lead to stack overflow. | AllocaInLoop1.cpp:65:3:82:3 | for(...;...;...) ... | for(...;...;...) ... |
4+
| AllocaInLoop1ms.cpp:22:18:22:24 | call to _alloca | Stack allocation is inside a $@ and could lead to stack overflow. | AllocaInLoop1ms.cpp:13:2:30:2 | for(...;...;...) ... | for(...;...;...) ... |
5+
| AllocaInLoop1ms.cpp:46:19:46:26 | call to _malloca | Stack allocation is inside a $@ and could lead to stack overflow. | AllocaInLoop1ms.cpp:36:2:57:2 | for(...;...;...) ... | for(...;...;...) ... |
6+
| AllocaInLoop1ms.cpp:73:19:73:25 | call to _alloca | Stack allocation is inside a $@ and could lead to stack overflow. | AllocaInLoop1ms.cpp:64:3:81:3 | for(...;...;...) ... | for(...;...;...) ... |
7+
| AllocaInLoop2.c:39:30:39:35 | call to __builtin_alloca | Stack allocation is inside a $@ and could lead to stack overflow. | AllocaInLoop2.c:29:5:48:19 | do (...) ... | do (...) ... |
8+
| AllocaInLoop3.cpp:38:23:38:28 | call to __builtin_alloca | Stack allocation is inside a $@ and could lead to stack overflow. | AllocaInLoop3.cpp:36:2:42:19 | do (...) ... | do (...) ... |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Likely Bugs/Memory Management/AllocaInLoop.ql
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// semmle-extractor-options: --clang
2+
struct vtype {
3+
int i1, i2;
4+
};
5+
extern int w1, w2;
6+
7+
void *__builtin_alloca(int sz);
8+
#define alloca __builtin_alloca
9+
typedef unsigned long size_t;
10+
11+
int printf(const char *format, ...);
12+
void *memcpy(void *dst, const void *src, size_t len);
13+
14+
// case 1: alloca directly contained in an unbounded loop
15+
void foo(const struct vtype* vec, int count) {
16+
for (int i = 0; i < count; i++) {
17+
const vtype* v = vec + i;
18+
char *b1 = 0;
19+
if (b1 == nullptr) {
20+
if (w1 > w2) {
21+
// Allocate the buffer on heap
22+
b1 = new char[w1];
23+
} else {
24+
// Allocate the buffer on stack
25+
b1 = (char*) alloca(w1); // [FLAG]
26+
}
27+
}
28+
memcpy(b1, v, w1);
29+
printf("%s\n", b1);
30+
if (w1 > w2) {
31+
delete b1;
32+
}
33+
}
34+
}
35+
36+
// case 2: alloca contained in a do-while(0) that is in turn contained
37+
// in an unbounded loop
38+
void bar(const struct vtype* vec, int count) {
39+
for (int i = 0; i < count; i++) {
40+
const vtype* v = vec + i;
41+
char *b1 = 0;
42+
do {
43+
if (b1 == nullptr) {
44+
if (w1 > w2) {
45+
// Allocate the buffer on heap
46+
b1 = new char[w1];
47+
} else {
48+
// Allocate the buffer on stack
49+
b1 = (char*) alloca(w1); // [FLAG]
50+
}
51+
}
52+
} while (0);
53+
memcpy(b1, v, w1);
54+
printf("%s\n", b1);
55+
if (w1 > w2) {
56+
delete b1;
57+
}
58+
}
59+
}
60+
61+
// case 3: alloca contained in an unbounded loop that is in turn contained
62+
// in a do-while(0)
63+
void baz(const struct vtype* vec, int count) {
64+
do {
65+
for (int i = 0; i < count; i++) {
66+
const vtype* v = vec + i;
67+
char *b1 = 0;
68+
if (b1 == nullptr) {
69+
if (w1 > w2) {
70+
// Allocate the buffer on heap
71+
b1 = new char[w1];
72+
} else {
73+
// Allocate the buffer on stack
74+
b1 = (char*) alloca(w1); // [FLAG]
75+
}
76+
}
77+
memcpy(b1, v, w1);
78+
printf("%s\n", b1);
79+
if (w1 > w2) {
80+
delete b1;
81+
}
82+
}
83+
} while (0);
84+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// semmle-extractor-options: --clang
2+
#include "malloc.h"
3+
struct vtype {
4+
int i1, i2;
5+
};
6+
extern int w1, w2;
7+
8+
int printf(const char *format, ...);
9+
void *memcpy(void *dst, const void *src, size_t len);
10+
11+
// case 1: _alloca directly contained in an unbounded loop
12+
void foo(const struct vtype* vec, int count) {
13+
for (int i = 0; i < count; i++) {
14+
const vtype* v = vec + i;
15+
char *b1 = 0;
16+
if (b1 == nullptr) {
17+
if (w1 > w2) {
18+
// Allocate the buffer on heap
19+
b1 = new char[w1];
20+
} else {
21+
// Allocate the buffer on stack
22+
b1 = (char*) _alloca(w1); // [FLAG]
23+
}
24+
}
25+
memcpy(b1, v, w1);
26+
printf("%s\n", b1);
27+
if (w1 > w2) {
28+
delete b1;
29+
}
30+
}
31+
}
32+
33+
// case 2: _malloca contained in a do-while(0) that is in turn contained
34+
// in an unbounded loop
35+
void bar(const struct vtype* vec, int count) {
36+
for (int i = 0; i < count; i++) {
37+
const vtype* v = vec + i;
38+
char *b1 = 0;
39+
do {
40+
if (b1 == nullptr) {
41+
if (w1 > w2) {
42+
// Allocate the buffer on heap
43+
b1 = new char[w1];
44+
} else {
45+
// Allocate the buffer on stack
46+
b1 = (char*) _malloca(w1); // [FLAG]
47+
}
48+
}
49+
} while (0);
50+
memcpy(b1, v, w1);
51+
printf("%s\n", b1);
52+
if (w1 > w2) {
53+
delete b1;
54+
} else {
55+
_freea(b1);
56+
}
57+
}
58+
}
59+
60+
// case 3: _alloca contained in an unbounded loop that is in turn contained
61+
// in a do-while(0)
62+
void baz(const struct vtype* vec, int count) {
63+
do {
64+
for (int i = 0; i < count; i++) {
65+
const vtype* v = vec + i;
66+
char *b1 = 0;
67+
if (b1 == nullptr) {
68+
if (w1 > w2) {
69+
// Allocate the buffer on heap
70+
b1 = new char[w1];
71+
} else {
72+
// Allocate the buffer on stack
73+
b1 = (char*) _alloca(w1); // [FLAG]
74+
}
75+
}
76+
memcpy(b1, v, w1);
77+
printf("%s\n", b1);
78+
if (w1 > w2) {
79+
delete b1;
80+
}
81+
}
82+
} while (0);
83+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// semmle-extractor-options: --clang
2+
int printf(const char *restrict format, ...);
3+
int sprintf(char *restrict buf, const char *restrict format, ...);
4+
typedef unsigned long size_t;
5+
void *memcpy(void *restrict dst, const void *restrict src, size_t len);
6+
void *malloc(size_t sz);
7+
void free(void *ptr);
8+
9+
struct vtype { int i1, i2; };
10+
extern int w1, w2;
11+
12+
void *_builtin_alloca(int sz);
13+
#define alloca __builtin_alloca
14+
15+
// We forward-declare the Microsoft routines
16+
//_alloca and _malloca here. Since they do not
17+
// originate from the <malloc.h> header, they
18+
// should not be flagged by our queries
19+
void *_alloca(size_t sz);
20+
void *_malloca(size_t sz);
21+
void _freea(void *ptr);
22+
23+
#define NULL (void *)0
24+
25+
// case 1: alloca called within a provably infinite loop
26+
void foo(const struct vtype* vec, int count) {
27+
char iter;
28+
29+
do {
30+
const struct vtype* v = vec + 2;
31+
char *b1 = 0;
32+
iter = 0;
33+
if (b1 == NULL) {
34+
if (w1 > w2) {
35+
// Allocate the buffer on heap
36+
b1 = (char *)malloc(w1);
37+
} else {
38+
// Allocate the buffer on stack
39+
b1 = (char*) alloca(w1); // [FLAG]
40+
iter = 1;
41+
}
42+
}
43+
memcpy(b1, v, w1);
44+
printf("%s\n", b1);
45+
if (w1 > w2) {
46+
free(b1);
47+
}
48+
} while (iter);
49+
}
50+
51+
// case 2: alloca called within nested do-while(0) loops
52+
void bar(const struct vtype* vec, int count) {
53+
54+
do {
55+
const struct vtype* v = vec + 2;
56+
char *b1 = 0;
57+
do {
58+
if (b1 == NULL) {
59+
if (w1 > w2) {
60+
// Allocate the buffer on heap
61+
b1 = (char *)malloc(w1);
62+
} else {
63+
// Allocate the buffer on stack
64+
b1 = (char*) alloca(w1); // [DO NOT FLAG]
65+
}
66+
}
67+
} while (0);
68+
memcpy(b1, v, w1);
69+
printf("%s\n", b1);
70+
if (w1 > w2) {
71+
free(b1);
72+
}
73+
} while (0);
74+
}
75+
76+
// case 3: alloca called outside any loops
77+
void baz(int count) {
78+
79+
char *buf = (char *)alloca(32); // [DO NOT FLAG]
80+
sprintf(buf, "Value is %d\n", count);
81+
printf("%s", buf);
82+
}
83+
84+
////// Negative Microsoft test cases
85+
86+
// case 4: _alloca directly contained in an unbounded loop
87+
void foo_ms(const struct vtype* vec, int count) {
88+
for (int i = 0; i < count; i++) {
89+
const struct vtype* v = vec + i;
90+
char *b1 = 0;
91+
if (b1 == NULL) {
92+
if (w1 > w2) {
93+
// Allocate the buffer on heap
94+
(char *)malloc(w1);
95+
} else {
96+
// Allocate the buffer on stack
97+
b1 = (char*) _alloca(w1); // [DO NOT FLAG]
98+
}
99+
}
100+
memcpy(b1, v, w1);
101+
printf("%s\n", b1);
102+
if (w1 > w2) {
103+
free(b1);
104+
}
105+
}
106+
}
107+
108+
// case 5: _malloca contained in a do-while(0) that is in turn contained
109+
// in an unbounded loop
110+
void bar_ms(const struct vtype* vec, int count) {
111+
for (int i = 0; i < count; i++) {
112+
const struct vtype* v = vec + i;
113+
char *b1 = 0;
114+
do {
115+
if (b1 == NULL) {
116+
if (w1 > w2) {
117+
// Allocate the buffer on heap
118+
b1 = (char *)malloc(w1);
119+
} else {
120+
// Allocate the buffer on stack
121+
b1 = (char*) _malloca(w1); // [DO NOT FLAG]
122+
}
123+
}
124+
} while (0);
125+
memcpy(b1, v, w1);
126+
printf("%s\n", b1);
127+
if (w1 > w2) {
128+
free(b1);
129+
} else {
130+
_freea(b1);
131+
}
132+
}
133+
}
134+
135+
// case 6: _alloca contained in an unbounded loop that is in turn contained
136+
// in a do-while(0)
137+
void baz_ms(const struct vtype* vec, int count) {
138+
do {
139+
for (int i = 0; i < count; i++) {
140+
const struct vtype* v = vec + i;
141+
char *b1 = 0;
142+
if (b1 == NULL) {
143+
if (w1 > w2) {
144+
// Allocate the buffer on heap
145+
b1 = (char *)malloc(w1);
146+
} else {
147+
// Allocate the buffer on stack
148+
b1 = (char*) _alloca(w1); // [DO NOT FLAG]
149+
}
150+
}
151+
memcpy(b1, v, w1);
152+
printf("%s\n", b1);
153+
if (w1 > w2) {
154+
free(b1);
155+
}
156+
}
157+
} while (0);
158+
}

0 commit comments

Comments
 (0)