Skip to content

Commit 53d4b2d

Browse files
authored
Merge pull request #1365 from geoffw0/uninit
CPP: Fix for the 'LoopConditionAlwaysTrueUponEntry' logic
2 parents 9d18b35 + 659fa47 commit 53d4b2d

File tree

8 files changed

+272
-14
lines changed

8 files changed

+272
-14
lines changed

change-notes/1.22/analysis-cpp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717

1818
- The predicate `Variable.getAnAssignedValue()` now reports assignments to fields resulting from aggregate initialization (` = {...}`).
1919
- The predicate `TypeMention.toString()` has been simplified to always return the string "`type mention`". This may improve performance when using `Element.toString()` or its descendants.
20+
- Fixed the `LocalScopeVariableReachability.qll` library's handling of loops with an entry condition is both always true upon first entry, and where there is more than one control flow path through the loop condition. This change increases the accuracy of the `LocalScopeVariableReachability.qll` library and queries which depend on it.

cpp/ql/src/semmle/code/cpp/controlflow/LocalScopeVariableReachability.qll

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -131,20 +131,28 @@ private predicate bbLoopEntryConditionAlwaysTrueAt(BasicBlock bb, int i, Control
131131
}
132132

133133
/**
134-
* Basic block `pred` ends with a condition belonging to a loop, and that
135-
* condition is provably true upon entry. Basic block `succ` is a successor
136-
* of `pred`, and `skipsLoop` indicates whether `succ` is the false-successor
137-
* of `pred`.
134+
* Basic block `pred` contains all or part of the condition belonging to a loop,
135+
* and there is an edge from `pred` to `succ` that concludes the condition.
136+
* If the edge corrseponds with the loop condition being found to be `true`, then
137+
* `skipsLoop` is `false`. Otherwise the edge corresponds with the loop condition
138+
* being found to be `false` and `skipsLoop` is `true`. Non-concluding edges
139+
* within a complex loop condition are not matched by this predicate.
138140
*/
139141
private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(BasicBlock pred, BasicBlock succ, boolean skipsLoop) {
140-
succ = pred.getASuccessor() and
141-
exists(ControlFlowNode last |
142-
last = pred.getEnd() and
143-
loopConditionAlwaysTrueUponEntry(_, last) and
144-
if succ = pred.getAFalseSuccessor() then
145-
skipsLoop = true
146-
else
147-
skipsLoop = false
142+
exists(Expr cond |
143+
loopConditionAlwaysTrueUponEntry(_, cond) and
144+
cond.getAChild*() = pred.getEnd() and
145+
succ = pred.getASuccessor() and
146+
not cond.getAChild*() = succ.getStart() and
147+
(
148+
(
149+
succ = pred.getAFalseSuccessor() and
150+
skipsLoop = true
151+
) or (
152+
succ = pred.getATrueSuccessor() and
153+
skipsLoop = false
154+
)
155+
)
148156
)
149157
}
150158

@@ -176,7 +184,7 @@ predicate bbSuccessorEntryReachesLoopInvariant(BasicBlock pred, BasicBlock succ,
176184
// The edge from `pred` to `succ` is _not_ from a loop condition provably
177185
// true upon entry, so the values of `predSkipsFirstLoopAlwaysTrueUponEntry`
178186
// and `succSkipsFirstLoopAlwaysTrueUponEntry` must be the same.
179-
not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, _, _) and
187+
not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, _) and
180188
succSkipsFirstLoopAlwaysTrueUponEntry = predSkipsFirstLoopAlwaysTrueUponEntry and
181189
// Moreover, if `pred` contains the entry point of a loop where the
182190
// condition is provably true upon entry, then `succ` is not allowed

cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class Stmt extends StmtParent, @stmt {
4545

4646
/**
4747
* Gets the statement following this statement in the same block, if any.
48+
*
49+
* Note that this is not widely useful, because this doesn't have a result for
50+
* the last statement of a block. Consider using the `ControlFlowNode` class
51+
* to trace the flow of control instead.
4852
*/
4953
Stmt getFollowingStmt() {
5054
exists(Block b, int i | this = b.getStmt(i) and
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
| test.cpp:35:2:37:2 | for(...;...;...) ... | test.cpp:35:18:35:23 | ... < ... | 1 | i | { ... } | i | ExprStmt |
2+
| test.cpp:43:2:45:2 | for(...;...;...) ... | test.cpp:43:18:43:26 | ... < ... | | i | { ... } | i | ExprStmt |
3+
| test.cpp:74:2:77:2 | while (...) ... | test.cpp:74:9:74:17 | ... > ... | 1 | count | { ... } | count | ExprStmt |
4+
| test.cpp:84:2:88:2 | while (...) ... | test.cpp:84:9:84:17 | ... > ... | | count | { ... } | count | if (...) ... |
5+
| test.cpp:171:3:173:3 | while (...) ... | test.cpp:171:10:171:43 | ... != ... | 0 | | { ... } | 0 | return ... |
6+
| test.cpp:251:2:255:2 | while (...) ... | test.cpp:251:9:251:12 | loop | 1 | loop | { ... } | loop | return ... |
7+
| test.cpp:263:2:267:2 | while (...) ... | test.cpp:263:9:263:20 | ... && ... | 1 | 1 | { ... } | ... && ... | return ... |
8+
| test.cpp:275:2:279:2 | while (...) ... | test.cpp:275:9:275:13 | ! ... | 1 | stop | { ... } | stop | return ... |
9+
| test.cpp:287:2:291:2 | while (...) ... | test.cpp:287:9:287:20 | ... && ... | 1 | loop | { ... } | loop | return ... |
10+
| test.cpp:299:2:303:2 | while (...) ... | test.cpp:299:9:299:20 | ... && ... | 1 | loop | { ... } | ... && ..., loop | return ... |
11+
| test.cpp:311:2:315:2 | while (...) ... | test.cpp:311:9:311:21 | ... \|\| ... | 1 | ... \|\| ... | { ... } | 0 | return ... |
12+
| test.cpp:323:2:328:2 | while (...) ... | test.cpp:323:9:323:17 | ... ? ... : ... | | b, c | { ... } | c | return ... |
13+
| test.cpp:336:2:341:2 | while (...) ... | test.cpp:336:9:336:21 | ... \|\| ... | 1 | b, c | { ... } | c | return ... |
14+
| test.cpp:348:2:351:17 | do (...) ... | test.cpp:351:11:351:15 | 0 | | { ... } | { ... } | { ... } | return ... |
15+
| test.cpp:361:2:364:2 | while (...) ... | test.cpp:361:9:361:21 | ... \|\| ... | 1 | ... \|\| ... | { ... } | 0 | while (...) ... |
16+
| test.cpp:365:2:368:2 | while (...) ... | test.cpp:365:9:365:13 | ! ... | 1 | stop | { ... } | stop | while (...) ... |
17+
| test.cpp:369:2:373:2 | while (...) ... | test.cpp:369:9:369:21 | ... \|\| ... | 1 | b, c | { ... } | c | do (...) ... |
18+
| test.cpp:374:2:376:17 | do (...) ... | test.cpp:376:11:376:15 | 0 | | do (...) ... | { ... } | { ... } | return ... |
19+
| test.cpp:384:2:386:2 | while (...) ... | test.cpp:384:9:384:12 | 1 | 1 | 1 | { ... } | | return ... |
20+
| test.cpp:394:2:396:2 | while (...) ... | test.cpp:394:9:394:21 | ... , ... | | { ... } | { ... } | | |
21+
| test.cpp:404:3:408:3 | while (...) ... | test.cpp:404:10:404:13 | loop | 1 | loop | { ... } | | |
22+
| test.cpp:416:2:418:2 | for(...;...;...) ... | test.cpp:416:18:416:23 | ... < ... | 1 | i | { ... } | i | return ... |
23+
| test.cpp:424:2:425:2 | for(...;...;...) ... | test.cpp:424:18:424:23 | ... < ... | 1 | i | { ... } | i | return ... |
24+
| test.cpp:433:2:434:2 | for(...;...;...) ... | test.cpp:433:18:433:22 | 0 | 0 | | { ... } | 0 | return ... |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import cpp
2+
import LoopConditionsConst
3+
4+
from Loop l, Expr condition
5+
where
6+
l.getCondition() = condition
7+
select
8+
l, condition,
9+
concat(int val | loopEntryConst(condition, val) | val.toString(), ", "),
10+
concat(BasicBlock bb | bb.getASuccessor() = l.getStmt() | bb.toString(), ", "),
11+
concat(l.getStmt().toString(), ", "),
12+
concat(BasicBlock bb | bb.getASuccessor() = l.getFollowingStmt() | bb.toString(), ", "),
13+
concat(l.getFollowingStmt().toString(), ", ")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import cpp
2+
import semmle.code.cpp.controlflow.internal.ConstantExprs
3+
4+
predicate loopEntryConst(Expr condition, int val)
5+
{
6+
exists(LoopEntryConditionEvaluator x, ControlFlowNode loop |
7+
x.isLoopEntry(condition, loop) and
8+
val = x.getValue(condition)
9+
)
10+
}

cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/UninitializedLocal.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@
88
| test.cpp:132:9:132:9 | j | The variable $@ may not be initialized here. | test.cpp:126:6:126:6 | j | j |
99
| test.cpp:219:3:219:3 | x | The variable $@ may not be initialized here. | test.cpp:218:7:218:7 | x | x |
1010
| test.cpp:243:13:243:13 | i | The variable $@ may not be initialized here. | test.cpp:241:6:241:6 | i | i |
11+
| test.cpp:329:9:329:11 | val | The variable $@ may not be initialized here. | test.cpp:321:6:321:8 | val | val |
12+
| test.cpp:336:10:336:10 | a | The variable $@ may not be initialized here. | test.cpp:333:7:333:7 | a | a |
13+
| test.cpp:369:10:369:10 | a | The variable $@ may not be initialized here. | test.cpp:358:7:358:7 | a | a |
14+
| test.cpp:378:9:378:11 | val | The variable $@ may not be initialized here. | test.cpp:359:6:359:8 | val | val |
15+
| test.cpp:417:10:417:10 | j | The variable $@ may not be initialized here. | test.cpp:414:9:414:9 | j | j |
16+
| test.cpp:436:9:436:9 | j | The variable $@ may not be initialized here. | test.cpp:431:9:431:9 | j | j |

cpp/ql/test/query-tests/Security/CWE/CWE-457/semmle/tests/test.cpp

Lines changed: 193 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,4 +242,196 @@ void test21()
242242

243243
v3 = v1 >> i; // BAD: i is not initialized
244244
v3 = v2 >> 1; // BAD: v2 is not initialized [NOT DETECTED]
245-
}
245+
}
246+
247+
int test22() {
248+
bool loop = true;
249+
int val;
250+
251+
while (loop)
252+
{
253+
val = 1;
254+
loop = false;
255+
}
256+
return val; // GOOD
257+
}
258+
259+
int test23() {
260+
bool loop = true, stop = false;
261+
int val;
262+
263+
while (loop && true)
264+
{
265+
val = 1;
266+
loop = false;
267+
}
268+
return val; // GOOD
269+
}
270+
271+
int test24() {
272+
bool stop = false;
273+
int val;
274+
275+
while (!stop)
276+
{
277+
val = 1;
278+
stop = true;
279+
}
280+
return val; // GOOD
281+
}
282+
283+
int test25() {
284+
bool loop = true, stop = false;
285+
int val;
286+
287+
while (true && loop)
288+
{
289+
val = 1;
290+
loop = false;
291+
}
292+
return val; // GOOD
293+
}
294+
295+
int test26() {
296+
bool loop = true, stop = false;
297+
int val;
298+
299+
while (loop && loop)
300+
{
301+
val = 1;
302+
loop = false;
303+
}
304+
return val; // GOOD
305+
}
306+
307+
int test27() {
308+
bool loop = true, stop = false;
309+
int val;
310+
311+
while (loop || false)
312+
{
313+
val = 1;
314+
loop = false;
315+
}
316+
return val; // GOOD
317+
}
318+
319+
int test28() {
320+
bool a = true, b = true, c = true;
321+
int val;
322+
323+
while (a ? b : c)
324+
{
325+
val = 1;
326+
a = false;
327+
c = false;
328+
}
329+
return val; // GOOD [FALSE POSITIVE]
330+
}
331+
332+
int test29() {
333+
bool a, b = true, c = true;
334+
int val;
335+
336+
while ((a && b) || c) // BAD (a is uninitialized)
337+
{
338+
val = 1;
339+
b = false;
340+
c = false;
341+
}
342+
return val; // GOOD
343+
}
344+
345+
int test30() {
346+
int val;
347+
348+
do
349+
{
350+
val = 1;
351+
} while (false);
352+
return val; // GOOD
353+
}
354+
355+
int test31() {
356+
bool loop = true;
357+
bool stop = false;
358+
bool a, b = true, c = true;
359+
int val;
360+
361+
while (loop || false)
362+
{
363+
loop = false;
364+
}
365+
while (!stop)
366+
{
367+
stop = true;
368+
}
369+
while ((a && b) || c) // BAD (a is uninitialized)
370+
{
371+
b = false;
372+
c = false;
373+
}
374+
do
375+
{
376+
} while (false);
377+
378+
return val; // BAD
379+
}
380+
381+
int test32() {
382+
int val;
383+
384+
while (true)
385+
{
386+
}
387+
388+
return val; // GOOD (never reached)
389+
}
390+
391+
int test33() {
392+
int val;
393+
394+
while (val = 1, true) {
395+
return val; // GOOD
396+
}
397+
}
398+
399+
int test34() {
400+
bool loop = true;
401+
int val;
402+
403+
{
404+
while (loop)
405+
{
406+
val = 1;
407+
loop = false;
408+
}
409+
}
410+
return val; // GOOD
411+
}
412+
413+
int test35() {
414+
int i, j;
415+
416+
for (int i = 0; i < 10; i++, j = 1) {
417+
return j; // BAD
418+
}
419+
}
420+
421+
int test36() {
422+
int i, j;
423+
424+
for (int i = 0; i < 10; i++, j = 1) {
425+
}
426+
427+
return j; // GOOD
428+
}
429+
430+
int test38() {
431+
int i, j;
432+
433+
for (int i = 0; false; i++, j = 1) {
434+
}
435+
436+
return j; // BAD
437+
}

0 commit comments

Comments
 (0)