Skip to content

Commit 3a45657

Browse files
authored
Merge pull request github#6378 from geoffw0/impropnull
C++: Test and improve cpp/improper-null-termination
2 parents 07f6ce7 + bb96ca3 commit 3a45657

File tree

4 files changed

+238
-4
lines changed

4 files changed

+238
-4
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,19 @@ class ImproperNullTerminationReachability extends StackVariableReachabilityWithR
2929
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
3030
node = declWithNoInit(v)
3131
or
32-
exists(Call c, VariableAccess va |
32+
exists(Call c, int bufferArg, int sizeArg |
3333
c = node and
34-
c.getTarget().hasName("readlink") and
35-
c.getArgument(1) = va and
36-
va.getTarget() = v
34+
(
35+
c.getTarget().hasName("readlink") and bufferArg = 1 and sizeArg = 2
36+
or
37+
c.getTarget().hasName("readlinkat") and bufferArg = 2 and sizeArg = 3
38+
) and
39+
c.getArgument(bufferArg).(VariableAccess).getTarget() = v and
40+
(
41+
// buffer size parameter likely matches the full buffer size
42+
c.getArgument(sizeArg) instanceof SizeofOperator or
43+
c.getArgument(sizeArg).getValue().toInt() = v.getType().getSize()
44+
)
3745
)
3846
}
3947

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
| test.cpp:25:10:25:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:22:8:22:14 | buffer1 | buffer1 |
2+
| test.cpp:26:10:26:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:23:8:23:14 | buffer2 | buffer2 |
3+
| test.cpp:39:10:39:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:35:8:35:14 | buffer2 | buffer2 |
4+
| test.cpp:59:10:59:13 | ptr1 | Variable $@ may not be null terminated. | test.cpp:56:9:56:12 | ptr1 | ptr1 |
5+
| test.cpp:69:10:69:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:64:8:64:14 | buffer1 | buffer1 |
6+
| test.cpp:70:10:70:12 | ptr | Variable $@ may not be null terminated. | test.cpp:64:8:64:14 | buffer1 | buffer1 |
7+
| test.cpp:81:10:81:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer2 | buffer2 |
8+
| test.cpp:82:10:82:12 | ptr | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer2 | buffer2 |
9+
| test.cpp:93:10:93:15 | buffer | Variable $@ may not be null terminated. | test.cpp:86:8:86:13 | buffer | buffer |
10+
| test.cpp:116:10:116:15 | buffer | Variable $@ may not be null terminated. | test.cpp:109:8:109:13 | buffer | buffer |
11+
| test.cpp:130:14:130:19 | buffer | Variable $@ may not be null terminated. | test.cpp:127:7:127:12 | buffer | buffer |
12+
| test.cpp:139:10:139:15 | buffer | Variable $@ may not be null terminated. | test.cpp:136:8:136:13 | buffer | buffer |
13+
| test.cpp:147:14:147:19 | buffer | Variable $@ may not be null terminated. | test.cpp:143:8:143:13 | buffer | buffer |
14+
| test.cpp:170:10:170:15 | buffer | Variable $@ may not be null terminated. | test.cpp:166:8:166:13 | buffer | buffer |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Likely Bugs/Memory Management/ImproperNullTermination.ql
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
2+
typedef unsigned int size_t;
3+
typedef signed int ssize_t;
4+
5+
size_t strlen(const char *s);
6+
char *strcpy(char *s1, const char *s2);
7+
char *strdup(const char *s1);
8+
9+
void *malloc(size_t size);
10+
11+
void *memset(void *s, int c, size_t n);
12+
void *memcpy(void *s1, const void *s2, size_t n);
13+
14+
ssize_t readlink(const char *path, char *buffer, size_t buffer_size);
15+
ssize_t readlinkat(int fd, const char *path, char *buffer, size_t buffer_size);
16+
17+
bool cond();
18+
19+
void test_unassigned()
20+
{
21+
{
22+
char buffer1[1024];
23+
char buffer2[1024];
24+
25+
strdup(buffer1); // BAD
26+
strdup(buffer2); // BAD
27+
28+
memcpy(buffer2, buffer1, sizeof(buffer2));
29+
strdup(buffer1); // BAD [NOT DETECTED]
30+
strdup(buffer2); // BAD [NOT DETECTED]
31+
}
32+
33+
{
34+
char buffer1[1024];
35+
char buffer2[1024];
36+
37+
strcpy(buffer1, "content");
38+
strdup(buffer1); // GOOD
39+
strdup(buffer2); // BAD
40+
41+
memcpy(buffer2, buffer1, sizeof(buffer2));
42+
strdup(buffer1); // GOOD
43+
strdup(buffer2); // GOOD
44+
}
45+
46+
{
47+
char buffer1[1024] = {0};
48+
char buffer2[1024];
49+
50+
memset(buffer2, 0, sizeof(buffer2));
51+
strdup(buffer1); // GOOD
52+
strdup(buffer2); // GOOD
53+
}
54+
55+
{
56+
char *ptr1;
57+
char *ptr2 = "content";
58+
59+
strdup(ptr1); // BAD
60+
strdup(ptr2); // GOOD
61+
}
62+
63+
{
64+
char buffer1[1024];
65+
char buffer2[1024];
66+
char *ptr;
67+
68+
ptr = buffer1;
69+
strdup(buffer1); // BAD
70+
strdup(ptr); // BAD
71+
72+
strcpy(buffer1, "content");
73+
strdup(buffer1); // GOOD
74+
strdup(ptr); // GOOD
75+
76+
ptr = buffer1;
77+
strdup(buffer1); // GOOD
78+
strdup(ptr); // GOOD
79+
80+
ptr = buffer2;
81+
strdup(buffer2); // BAD
82+
strdup(ptr); // BAD
83+
}
84+
85+
{
86+
char buffer[1024];
87+
88+
if (cond())
89+
{
90+
strcpy(buffer, "content");
91+
strdup(buffer); // GOOD
92+
}
93+
strdup(buffer); // BAD
94+
}
95+
96+
{
97+
char buffer[1024];
98+
99+
if (cond())
100+
{
101+
strcpy(buffer, "content");
102+
} else {
103+
strcpy(buffer, "alternative");
104+
}
105+
strdup(buffer); // GOOD
106+
}
107+
108+
{
109+
char buffer[1024];
110+
111+
while (cond())
112+
{
113+
strcpy(buffer, "content");
114+
strdup(buffer); // GOOD
115+
}
116+
strdup(buffer); // BAD
117+
}
118+
}
119+
120+
void test_callee(char *p1, char *p2)
121+
{
122+
strdup(p1);
123+
}
124+
125+
void test_caller()
126+
{
127+
char buffer[1024];
128+
129+
test_callee("content", buffer); // GOOD
130+
test_callee(buffer, "content"); // BAD
131+
}
132+
133+
void test_readlink(int fd, const char *path, size_t sz)
134+
{
135+
{
136+
char buffer[1024];
137+
138+
readlink(path, buffer, sizeof(buffer));
139+
strdup(buffer); // BAD
140+
}
141+
142+
{
143+
char buffer[1024];
144+
int v;
145+
146+
readlinkat(fd, path, buffer, sizeof(buffer));
147+
v = strlen(buffer); // BAD
148+
}
149+
150+
{
151+
char buffer[1024] = {0};
152+
153+
readlink(path, buffer, sizeof(buffer) - 1);
154+
strdup(buffer); // GOOD
155+
}
156+
157+
{
158+
char buffer[1024];
159+
160+
memset(buffer, 0, sizeof(buffer));
161+
readlink(path, buffer, sizeof(buffer) - 1);
162+
strdup(buffer); // GOOD
163+
}
164+
165+
{
166+
char buffer[1024];
167+
168+
memset(buffer, 0, sizeof(buffer));
169+
readlink(path, buffer, sizeof(buffer));
170+
strdup(buffer); // BAD
171+
}
172+
173+
{
174+
char buffer[1024];
175+
176+
memset(buffer, 0, sizeof(buffer));
177+
readlink(path, buffer, sizeof(buffer));
178+
buffer[sizeof(buffer) - 1] = 0;
179+
strdup(buffer); // GOOD
180+
}
181+
182+
{
183+
char *buffer = (char *)malloc(1024);
184+
185+
readlink(path, buffer, 1024);
186+
strdup(buffer); // BAD [NOT DETECTED]
187+
}
188+
189+
{
190+
char *buffer = (char *)malloc(1024);
191+
192+
buffer[1023] = 0;
193+
readlink(path, buffer, 1023);
194+
strdup(buffer); // GOOD
195+
}
196+
197+
{
198+
char *buffer = (char *)malloc(sz);
199+
200+
readlink(path, buffer, sz);
201+
strdup(buffer); // BAD [NOT DETECTED]
202+
}
203+
204+
{
205+
char *buffer = (char *)malloc(sz);
206+
207+
memset(buffer, 0, sz);
208+
readlink(path, buffer, sz - 1);
209+
strdup(buffer); // GOOD
210+
}
211+
}

0 commit comments

Comments
 (0)