-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathLengthComparisonOffByOne.ql
66 lines (60 loc) · 2.16 KB
/
LengthComparisonOffByOne.ql
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
/**
* @name Off-by-one comparison against length
* @description An array index is compared to be less than or equal to the 'length' property,
* and then used in an indexing operation that could be out of bounds.
* @kind problem
* @problem.severity warning
* @id js/index-out-of-bounds
* @tags reliability
* correctness
* logic
* external/cwe/cwe-193
* @precision high
*/
import javascript
/**
* Gets an access to `array.length`.
*/
PropAccess arrayLen(Variable array) { result.accesses(array.getAnAccess(), "length") }
/**
* Gets a condition that checks that `index` is less than or equal to `array.length`.
*/
ConditionGuardNode getLengthLEGuard(Variable index, Variable array) {
exists(RelationalComparison cmp | cmp instanceof GEExpr or cmp instanceof LEExpr |
cmp = result.getTest() and
result.getOutcome() = true and
cmp.getGreaterOperand() = arrayLen(array) and
cmp.getLesserOperand() = index.getAnAccess()
)
}
/**
* Gets a condition that checks that `index` is not equal to `array.length`.
*/
ConditionGuardNode getLengthNEGuard(Variable index, Variable array) {
exists(EqualityTest eq |
eq = result.getTest() and
result.getOutcome() = eq.getPolarity().booleanNot() and
eq.hasOperands(index.getAnAccess(), arrayLen(array))
)
}
/**
* Holds if `ea` is a read from `array[index]` in basic block `bb`.
*/
predicate elementRead(IndexExpr ea, Variable array, Variable index, BasicBlock bb) {
ea.getBase() = array.getAnAccess() and
ea.getIndex() = index.getAnAccess() and
ea instanceof RValue and
bb = ea.getBasicBlock()
}
from ConditionGuardNode cond, Variable array, Variable index, IndexExpr ea, BasicBlock bb
where
// there is a comparison `index <= array.length`
cond = getLengthLEGuard(index, array) and
// there is a read from `array[index]`
elementRead(ea, array, index, bb) and
// and the read is guarded by the comparison
cond.dominates(bb) and
// but the read is not guarded by another check that `index != array.length`
not getLengthNEGuard(index, array).dominates(bb)
select cond.getTest(), "Off-by-one index comparison against length may lead to out-of-bounds $@.",
ea, "read"