-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathArraySizing.qll
More file actions
155 lines (137 loc) · 5.15 KB
/
ArraySizing.qll
File metadata and controls
155 lines (137 loc) · 5.15 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
147
148
149
150
151
152
153
154
155
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.DefUse
import semmle.code.java.security.Random
private import BoundingChecks
/**
* If the `Array` accessed by the `ArrayAccess` is a fixed size, return the array size.
*/
int fixedArraySize(ArrayAccess arrayAccess) {
exists(Variable v |
v.getAnAccess() = arrayAccess.getArray() and
result = v.getAnAssignedValue().(ArrayCreationExpr).getFirstDimensionSize()
)
}
/**
* Holds if an `ArrayIndexOutOfBoundsException` is ever caught.
*/
private predicate arrayIndexOutOfBoundExceptionCaught(ArrayAccess arrayAccess) {
exists(TryStmt ts, CatchClause cc, RefType exc |
(
ts.getBlock().getAChild*() = arrayAccess.getEnclosingStmt() or
ts.getAResourceDecl().getAChild*() = arrayAccess.getEnclosingStmt() or
ts.getAResourceExpr().getAChildExpr*() = arrayAccess
) and
cc = ts.getACatchClause() and
exc = cc.getVariable().getType() and
exc.hasQualifiedName("java.lang", "ArrayIndexOutOfBoundsException")
)
}
/**
* A pointless loop, of the type seen frequently in Juliet tests, of the form:
*
* ```
* while(true) {
* ...
* break;
* }
* ```
*/
class PointlessLoop extends WhileStmt {
PointlessLoop() {
getCondition().(BooleanLiteral).getBooleanValue() = true and
// The only `break` must be the last statement.
forall(BreakStmt break | break.(JumpStmt).getTarget() = this |
this.getStmt().(BlockStmt).getLastStmt() = break
) and
// No `continue` statements.
not exists(ContinueStmt continue | continue.(JumpStmt).getTarget() = this)
}
}
/**
* An `ArrayAccess` for which we can determine whether the index is appropriately bound checked.
*
* We only consider first dimension array accesses, and we only consider indices in loops, if it's
* obvious that the loop only executes once.
*/
class CheckableArrayAccess extends ArrayAccess {
CheckableArrayAccess() {
// We are not interested in array accesses that don't access the first dimension.
not this.getArray() instanceof ArrayAccess and
// Array accesses within loops can make it difficult to verify whether the index is checked
// prior to access. Ignore "pointless" loops of the sort found in Juliet test cases.
not exists(LoopStmt loop |
loop.getBody().getAChild*() = getEnclosingStmt() and
not loop instanceof PointlessLoop
) and
// The possible exception is not caught
not arrayIndexOutOfBoundExceptionCaught(this)
}
/**
* Holds if we believe this indexing expression can throw an `ArrayIndexOutOfBoundsException`.
*/
predicate canThrowOutOfBounds(Expr index) {
index = getIndexExpr() and
not (
// There is a condition dominating this expression ensuring that the index is >= 0.
lowerBound(index) >= 0 and
// There is a condition dominating this expression that ensures the index is less than the length.
lessthanLength(this)
)
}
/**
* Holds if we believe this indexing expression can throw an `ArrayIndexOutOfBoundsException` due
* to the array being initialized with `sizeExpr`, which may be zero.
*/
predicate canThrowOutOfBoundsDueToEmptyArray(Expr sizeExpr, ArrayCreationExpr arrayCreation) {
// Find an `ArrayCreationExpr` for the array used in this indexing operation.
exists(VariableAssign assign |
assign.getSource() = arrayCreation and
defUsePair(assign, this.getArray())
) and
// If the array access is protected by a conditional that verifies the index is less than the array
// length, then the array will never be accessed if the size is zero.
not lessthanLength(this) and
// Verify that the size expression is never checked to be greater than 0.
sizeExpr = arrayCreation.getDimension(0) and
not lowerBound(sizeExpr) > 0
}
}
/**
* A source of "flow" which has an upper or lower bound.
*/
abstract class BoundedFlowSource extends DataFlow::Node {
/**
* Return a lower bound for the input, if possible.
*/
abstract int lowerBound();
/**
* Return an upper bound for the input, if possible.
*/
abstract int upperBound();
/**
* Return a description for this flow source, suitable for putting in an alert message.
*/
abstract string getDescription();
}
/**
* Input that is constructed using a random value.
*/
class RandomValueFlowSource extends BoundedFlowSource {
RandomDataSource nextAccess;
RandomValueFlowSource() { this.asExpr() = nextAccess }
override int lowerBound() { result = nextAccess.getLowerBound() }
override int upperBound() { result = nextAccess.getUpperBound() }
override string getDescription() { result = "Random value" }
}
/**
* A compile time constant expression that evaluates to a numeric type.
*/
class NumericLiteralFlowSource extends BoundedFlowSource {
NumericLiteralFlowSource() { exists(this.asExpr().(CompileTimeConstantExpr).getIntValue()) }
override int lowerBound() { result = this.asExpr().(CompileTimeConstantExpr).getIntValue() }
override int upperBound() { result = this.asExpr().(CompileTimeConstantExpr).getIntValue() }
override string getDescription() {
result = "Literal value " + this.asExpr().(CompileTimeConstantExpr).getIntValue()
}
}