Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 064b109

Browse files
committed
JS: range analysis through phi nodes
1 parent 09ca665 commit 064b109

2 files changed

Lines changed: 114 additions & 2 deletions

File tree

javascript/ql/src/semmle/javascript/RangeAnalysis.qll

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,15 +302,87 @@ module RangeAnalysis {
302302
)
303303
}
304304

305+
/**
306+
* Holds if `node` is a phi node with `left` and `right` has the only two inputs.
307+
*
308+
* Note that this predicate is symmetric: when it holds for (left, right) it also holds for (right, left).
309+
*/
310+
predicate binaryPhiNode(DataFlow::Node node, DataFlow::Node left, DataFlow::Node right) {
311+
exists (SsaPhiNode phi | node = DataFlow::ssaDefinitionNode(phi) |
312+
strictcount(phi.getAnInput()) = 2 and
313+
left = DataFlow::ssaDefinitionNode(phi.getAnInput()) and
314+
right = DataFlow::ssaDefinitionNode(phi.getAnInput()) and
315+
left != right)
316+
}
317+
318+
/**
319+
* Holds if `A <= B + c` can be determined based on a phi node.
320+
*/
321+
predicate phiEdge(ControlFlowNode cfg, DataFlow::Node a, int asign, DataFlow::Node b, int bsign, int c) {
322+
exists (DataFlow::Node phi, DataFlow::Node left, DataFlow::Node right |
323+
binaryPhiNode(phi, left, right) and
324+
cfg = phi.getBasicBlock()
325+
|
326+
// Both inputs are defined in terms of the same root:
327+
// phi = PHI(root + bias1, root + bias2)
328+
exists (DataFlow::Node root, int sign, int bias1, int bias2 |
329+
linearDefinition(left, root, sign, bias1) and
330+
linearDefinition(right, root, sign, bias2) and
331+
bias1 < bias2 and
332+
// root + bias1 <= phi <= root + bias2
333+
(
334+
// root <= phi - bias1
335+
a = root and asign = 1 and
336+
b = phi and bsign = 1 and
337+
c = -bias1
338+
or
339+
// phi <= root + bias2
340+
a = phi and asign = 1 and
341+
b = root and bsign = 1 and
342+
c = bias2
343+
)
344+
)
345+
or
346+
// One input is defined in terms of the phi node itself:
347+
// phi = PHI(phi + increment, x)
348+
exists (int increment, DataFlow::Node root, int sign, int bias |
349+
linearDefinition(left, phi, 1, increment) and
350+
linearDefinition(right, root, sign, bias) and
351+
(
352+
// If increment is positive (or zero):
353+
// phi >= right' + bias
354+
increment >= 0 and
355+
a = root and asign = sign and
356+
b = phi and bsign = 1 and
357+
c = -bias
358+
or
359+
// If increment is negative (or zero):
360+
// phi <= right' + bias
361+
increment <= 0 and
362+
a = phi and asign = 1 and
363+
b = root and bsign = sign and
364+
c = bias
365+
)
366+
)
367+
)
368+
}
369+
305370
/**
306371
* The set of initial edges including those from dual constraints.
307372
*/
308373
private predicate seedEdge(ControlFlowNode cfg, DataFlow::Node a, int asign, DataFlow::Node b, int bsign, int c) {
309374
// A <= B + c
310375
comparisonEdge(cfg, a, asign, b, bsign, c)
311376
or
377+
phiEdge(cfg, a, asign, b, bsign, c)
378+
}
379+
380+
private predicate seedEdgeWithDual(ControlFlowNode cfg, DataFlow::Node a, int asign, DataFlow::Node b, int bsign, int c) {
381+
// A <= B + c
382+
seedEdge(cfg, a, asign, b, bsign, c)
383+
or
312384
// -B <= -A + c (dual constraint)
313-
comparisonEdge(cfg, b, -bsign, a, -asign, c)
385+
seedEdge(cfg, b, -bsign, a, -asign, c)
314386
}
315387

316388
/**
@@ -339,7 +411,7 @@ module RangeAnalysis {
339411
* This means negative-weight cycles (contradictions) can be detected using simple cycle detection.
340412
*/
341413
private predicate extendedEdge(ControlFlowNode cfg, DataFlow::Node a, int asign, DataFlow::Node b, int bsign, int c) {
342-
seedEdge(cfg, a, asign, b, bsign, c)
414+
seedEdgeWithDual(cfg, a, asign, b, bsign, c)
343415
or
344416
exists (DataFlow::Node mid, int midx, ControlFlowNode cfg1, int c1, ControlFlowNode cfg2, int c2 |
345417
extendedEdge(cfg1, a, asign, mid, midx, c1) and
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
function increasing(start, end) {
2+
for (var i = start; i < end; ++i) {
3+
if (i >= start) {} // NOT OK - always true
4+
if (i < start) {} // NOT OK - always false
5+
6+
if (i < end) {} // NOT OK - always true
7+
if (i >= end) {} // NOT OK - always false
8+
9+
if (i + 1 > start) {} // NOT OK - always true
10+
if (i - 1 < start) {} // OK
11+
}
12+
}
13+
14+
function decreasing(start, end) {
15+
for (var i = start; i > end; --i) {
16+
if (i <= start) {} // NOT OK - always true
17+
if (i > start) {} // NOT OK - always false
18+
19+
if (i + 1 > start) {} // OK
20+
if (i - 1 < start) {} // NOT OK - always true
21+
}
22+
}
23+
24+
function middleIncrement(start, end) {
25+
for (var i = start; i < end;) {
26+
if (i >= start) {} // OK - always true but we don't catch it
27+
if (i < start) {} // OK - always false but we don't catch it
28+
29+
if (i < end) {} // NOT OK - always true
30+
if (i >= end) {} // NOT OK - always false
31+
32+
if (something()) {
33+
i += 2;
34+
}
35+
if (i >= start) {} // OK - always true but we don't catch it
36+
if (i < start) {} // OK - always false but we don't catch it
37+
if (i < end) {} // OK
38+
if (i >= end) {} // OK
39+
}
40+
}

0 commit comments

Comments
 (0)