-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathLoopBounds.ql
More file actions
146 lines (134 loc) · 4.79 KB
/
LoopBounds.ql
File metadata and controls
146 lines (134 loc) · 4.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* @name Unbounded loop
* @description All loops should have a fixed upper bound; the counter should also be incremented along all paths within the loop.
* This check excludes loops that are meant to be nonterminating (like schedulers).
* @kind problem
* @id cpp/jpl-c/loop-bounds
* @problem.severity warning
* @tags correctness
* external/jpl
*/
import cpp
predicate validVarForBound(Loop loop, Variable var) {
// The variable is read in the loop controlling expression
var.getAnAccess().getParent*() = loop.getControllingExpr() and
// The variable is not assigned in the loop body
not inScope(loop, var.getAnAssignment().getEnclosingStmt()) and
// The variable is not incremented/decremented in the loop body
not inScope(loop, var.getAnAccess().getParent().(CrementOperation).getEnclosingStmt())
}
predicate upperBoundCheck(Loop loop, VariableAccess checked) {
exists(RelationalOperation rop | loop.getControllingExpr().getAChild*() = rop |
checked = rop.getLesserOperand() and
// The RHS is something "valid", i.e. a constant or
// a variable that isn't assigned in the loop body
(
exists(rop.getGreaterOperand().getValue()) or
rop.getGreaterOperand().(VariableAccess).getTarget().isConst() or
validVarForBound(loop, rop.getGreaterOperand().(VariableAccess).getTarget())
) and
not rop.getGreaterOperand() instanceof CharLiteral
)
}
predicate lowerBoundCheck(Loop loop, VariableAccess checked) {
exists(RelationalOperation rop | loop.getControllingExpr().getAChild*() = rop |
checked = rop.getGreaterOperand() and
// The RHS is something "valid", i.e. a constant or
// a variable that isn't assigned in the loop body
(
exists(rop.getLesserOperand().getValue()) or
rop.getLesserOperand().(VariableAccess).getTarget().isConst() or
validVarForBound(loop, rop.getLesserOperand().(VariableAccess).getTarget())
) and
not rop.getLesserOperand() instanceof CharLiteral
)
}
VariableAccess getAnIncrement(Variable var) {
result.getTarget() = var and
(
result.getParent() instanceof IncrementOperation
or
exists(AssignAddExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
or
exists(AssignExpr a | a.getLValue() = result |
a.getRValue() =
any(AddExpr ae |
ae.getAnOperand() = var.getAnAccess() and
ae.getAnOperand().getValue().toInt() > 0
)
)
)
}
VariableAccess getADecrement(Variable var) {
result.getTarget() = var and
(
result.getParent() instanceof DecrementOperation
or
exists(AssignSubExpr a | a.getLValue() = result and a.getRValue().getValue().toInt() > 0)
or
exists(AssignExpr a | a.getLValue() = result |
a.getRValue() =
any(SubExpr ae |
ae.getLeftOperand() = var.getAnAccess() and
ae.getRightOperand().getValue().toInt() > 0
)
)
)
}
predicate inScope(Loop l, Stmt s) { l.getAChild*() = s }
predicate reachesNoInc(VariableAccess source, ControlFlowNode target) {
upperBoundCheck(_, source) and source.getASuccessor() = target
or
exists(ControlFlowNode mid |
reachesNoInc(source, mid) and not mid = getAnIncrement(source.getTarget())
|
target = mid.getASuccessor() and
inScope(source.getEnclosingStmt(), target.getEnclosingStmt())
)
}
predicate reachesNoDec(VariableAccess source, ControlFlowNode target) {
lowerBoundCheck(_, source) and source.getASuccessor() = target
or
exists(ControlFlowNode mid |
reachesNoDec(source, mid) and not mid = getADecrement(source.getTarget())
|
target = mid.getASuccessor() and
inScope(source.getEnclosingStmt(), target.getEnclosingStmt())
)
}
predicate hasSafeBound(Loop l) {
exists(VariableAccess bound | upperBoundCheck(l, bound) | not reachesNoInc(bound, bound))
or
exists(VariableAccess bound | lowerBoundCheck(l, bound) | not reachesNoDec(bound, bound))
or
exists(l.getControllingExpr().getValue())
}
predicate markedAsNonterminating(Loop l) {
exists(Comment c | c.getContents().matches("%@non-terminating@%") | c.getCommentedElement() = l)
}
from Loop loop, string msg
where
not hasSafeBound(loop) and
not markedAsNonterminating(loop) and
(
not upperBoundCheck(loop, _) and
not lowerBoundCheck(loop, _) and
msg = "This loop does not have a fixed bound."
or
exists(VariableAccess bound |
upperBoundCheck(loop, bound) and
reachesNoInc(bound, bound) and
msg =
"The loop counter " + bound.getTarget().getName() +
" is not always incremented in the loop body."
)
or
exists(VariableAccess bound |
lowerBoundCheck(loop, bound) and
reachesNoDec(bound, bound) and
msg =
"The loop counter " + bound.getTarget().getName() +
" is not always decremented in the loop body."
)
)
select loop, msg