Skip to content

Commit c375f24

Browse files
authored
Merge pull request #20423 from smowton/smowton/fix/length-comparison-off-by-one-fp
JS: Recognise that a less-than test is as good as a non-equal test for mitigating off-by-one array access
2 parents 9231119 + db5c581 commit c375f24

File tree

3 files changed

+27
-2
lines changed

3 files changed

+27
-2
lines changed

javascript/ql/src/LanguageFeatures/LengthComparisonOffByOne.ql

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ ConditionGuardNode getLengthLEGuard(Variable index, Variable array) {
3333
)
3434
}
3535

36+
/**
37+
* Gets a condition that checks that `index` is less than `array.length`.
38+
*/
39+
ConditionGuardNode getLengthLTGuard(Variable index, Variable array) {
40+
exists(RelationalComparison cmp | cmp instanceof GTExpr or cmp instanceof LTExpr |
41+
cmp = result.getTest() and
42+
result.getOutcome() = true and
43+
cmp.getGreaterOperand() = arrayLen(array) and
44+
cmp.getLesserOperand() = index.getAnAccess()
45+
)
46+
}
47+
3648
/**
3749
* Gets a condition that checks that `index` is not equal to `array.length`.
3850
*/
@@ -62,7 +74,8 @@ where
6274
elementRead(ea, array, index, bb) and
6375
// and the read is guarded by the comparison
6476
cond.dominates(bb) and
65-
// but the read is not guarded by another check that `index != array.length`
66-
not getLengthNEGuard(index, array).dominates(bb)
77+
// but the read is not guarded by another check that `index != array.length` or `index < array.length`
78+
not getLengthNEGuard(index, array).dominates(bb) and
79+
not getLengthLTGuard(index, array).dominates(bb)
6780
select cond.getTest(), "Off-by-one index comparison against length may lead to out-of-bounds $@.",
6881
ea, "read"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Query `js/index-out-of-bounds` no longer produces a false-positive when a strictly-less-than check overrides a previous less-than-or-equal test.

javascript/ql/test/query-tests/LanguageFeatures/LengthComparisonOffByOne/tst.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,11 @@ function badContains(a, elt) {
5555
return true;
5656
return false;
5757
}
58+
59+
// OK - incorrect upper bound, but extra check
60+
function badContains2(a, elt) {
61+
for (let i = 0; i <= a.length; ++i)
62+
if (i < a.length && a[i] === elt)
63+
return true;
64+
return false;
65+
}

0 commit comments

Comments
 (0)