Skip to content

Commit 0040a2d

Browse files
author
Robert Marsh
committed
C++: respond to further PR comments
1 parent 8c9c316 commit 0040a2d

File tree

3 files changed

+61
-28
lines changed

3 files changed

+61
-28
lines changed

cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
/**
22
* Provides classes and predicates for range analysis.
33
*
4-
* An inferred bound can either be a specific integer or the abstract value of
5-
* an IR `Instruction`.
4+
* An inferred bound can either be a specific integer or a `ValueNumber`
5+
* representing the abstract value of a set of `Instruction`s.
66
*
77
* If an inferred bound relies directly on a condition, then this condition is
88
* reported as the reason for the bound.
99
*/
1010

11-
// TODO: update the following comment
1211
/*
1312
* This library tackles range analysis as a flow problem. Consider e.g.:
1413
* ```
@@ -21,24 +20,29 @@
2120
* arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y
2221
* ```
2322
*
24-
* In its simplest form the step relation `E1 --> E2` relates two expressions
25-
* such that `E1 <= B` implies `E2 <= B` for any `B` (with a second separate
23+
* In its simplest form the step relation `I1 --> I2` relates two `Instruction`s
24+
* such that `I1 <= B` implies `I2 <= B` for any `B` (with a second separate
2625
* step relation handling lower bounds). Examples of such steps include
27-
* assignments `E2 = E1` and conditions `x <= E1` where `E2` is a use of `x`
26+
* assignments `I2 = I1` and conditions `x <= I1` where `I2` is a use of `x`
2827
* guarded by the condition.
2928
*
3029
* In order to handle subtractions and additions with constants, and strict
3130
* comparisons, the step relation is augmented with an integer delta. With this
32-
* generalization `E1 --(delta)--> E2` relates two expressions and an integer
33-
* such that `E1 <= B` implies `E2 <= B + delta` for any `B`. This corresponds
31+
* generalization `I1 --(delta)--> I2` relates two `Instruction`s and an integer
32+
* such that `I1 <= B` implies `I2 <= B + delta` for any `B`. This corresponds
3433
* to the predicate `boundFlowStep`.
3534
*
3635
* The complete range analysis is then implemented as the transitive closure of
37-
* the step relation summing the deltas along the way. If `E1` transitively
38-
* steps to `E2`, `delta` is the sum of deltas along the path, and `B` is an
39-
* interesting bound equal to the value of `E1` then `E2 <= B + delta`. This
40-
* corresponds to the predicate `bounded`.
36+
* the step relation summing the deltas along the way. If `I1` transitively
37+
* steps to `I2`, `delta` is the sum of deltas along the path, and `B` is an
38+
* interesting bound equal to the value of `I1` then `I2 <= B + delta`. This
39+
* corresponds to the predicate `boundedInstruction`.
4140
*
41+
* Bounds come in two forms: either they are relative to zero (and thus provide
42+
* a constant bound), or they are relative to some program value. This value is
43+
* represented by the `ValueNumber` class, each instance of which represents a
44+
* set of `Instructions` that must have the same value.
45+
*
4246
* Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`.
4347
* There are essentially two cases:
4448
* - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`.
@@ -121,8 +125,8 @@ import RangeAnalysisPublic
121125
* Gets a condition that tests whether `vn` equals `bound + delta`.
122126
*
123127
* If the condition evaluates to `testIsTrue`:
124-
* - `isEq = true` : `i == bound + delta`
125-
* - `isEq = false` : `i != bound + delta`
128+
* - `isEq = true` : `vn == bound + delta`
129+
* - `isEq = false` : `vn != bound + delta`
126130
*/
127131
private IRGuardCondition eqFlowCond(ValueNumber vn, Operand bound, int delta,
128132
boolean isEq, boolean testIsTrue)
@@ -138,11 +142,6 @@ private IRGuardCondition eqFlowCond(ValueNumber vn, Operand bound, int delta,
138142
private predicate boundFlowStepSsa(
139143
NonPhiOperand op2, Operand op1, int delta, boolean upper, Reason reason
140144
) {
141-
/*op2.getDefinitionInstruction().getAnOperand().(CopySourceOperand) = op1 and
142-
(upper = true or upper = false) and
143-
reason = TNoReason() and
144-
delta = 0
145-
or*/
146145
exists(IRGuardCondition guard, boolean testIsTrue |
147146
guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and
148147
guard.controls(op2.getInstruction().getBlock(), testIsTrue) and
@@ -170,7 +169,6 @@ private IRGuardCondition boundFlowCond(ValueNumber vn, NonPhiOperand bound, int
170169
or
171170
result = eqFlowCond(vn, bound, delta, true, testIsTrue) and
172171
(upper = true or upper = false)
173-
// TODO: strengthening through modulus library
174172
}
175173

176174
private newtype TReason =
@@ -213,7 +211,6 @@ private predicate safeCast(IntegralType fromtyp, IntegralType totyp) {
213211
fromtyp.isUnsigned() and
214212
totyp.isUnsigned()
215213
)
216-
// TODO: infer safety using sign analysis?
217214
}
218215

219216
private class SafeCastInstruction extends ConvertInstruction {
@@ -357,7 +354,7 @@ private predicate boundedNonPhiOperand(NonPhiOperand op, Bound b, int delta, boo
357354
boundedInstruction(op.getDefinitionInstruction(), b, delta, upper, fromBackEdge, origdelta, reason)
358355
or
359356
exists(int d, Reason r1, Reason r2 |
360-
boundedInstruction(op.getDefinitionInstruction(), b, d, upper, fromBackEdge, origdelta, r2)
357+
boundedNonPhiOperand(op, b, d, upper, fromBackEdge, origdelta, r2)
361358
|
362359
unequalOperand(op, b, d, r1) and
363360
(
@@ -426,13 +423,13 @@ private predicate boundedPhiOperand(
426423
)
427424
}
428425

429-
/** Holds if `v != e + delta` at `pos`. */
426+
/** Holds if `op2 != op1 + delta` at `pos`. */
430427
private predicate unequalFlowStep(
431-
Operand op1, Operand op2, int delta, Reason reason
428+
Operand op2, Operand op1, int delta, Reason reason
432429
) {
433430
exists(IRGuardCondition guard, boolean testIsTrue |
434-
guard = eqFlowCond(valueNumberOfOperand(op1), op2, delta, false, testIsTrue) and
435-
guard.controls(op1.getInstruction().getBlock(), testIsTrue) and
431+
guard = eqFlowCond(valueNumberOfOperand(op2), op1, delta, false, testIsTrue) and
432+
guard.controls(op2.getInstruction().getBlock(), testIsTrue) and
436433
reason = TCondReason(guard)
437434
)
438435
}
@@ -442,9 +439,9 @@ private predicate unequalFlowStep(
442439
*/
443440
private predicate unequalOperand(Operand op, Bound b, int delta, Reason reason) {
444441
exists(Operand op2, int d1, int d2 |
445-
unequalFlowStep(op, op2, delta, reason) and
446-
boundedNonPhiOperand(op2, b, d2, true, _, _, _) and
442+
unequalFlowStep(op, op2, d1, reason) and
447443
boundedNonPhiOperand(op2, b, d2, true, _, _, _) and
444+
boundedNonPhiOperand(op2, b, d2, false, _, _, _) and
448445
delta = d1 + d2
449446
)
450447
}

cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,17 @@
4343
| test.cpp:149:10:149:10 | Store: i | file://:0:0:0:0 | 0 | 1 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
4444
| test.cpp:149:10:149:10 | Store: i | test.cpp:147:5:147:5 | Phi: i | 1 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
4545
| test.cpp:149:10:149:10 | Store: i | test.cpp:147:5:147:5 | Phi: i | 1 | true | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
46+
| test.cpp:156:12:156:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | false | CompareEQ: ... == ... | test.cpp:155:9:155:16 | test.cpp:155:9:155:16 |
47+
| test.cpp:156:12:156:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | true | CompareEQ: ... == ... | test.cpp:155:9:155:16 | test.cpp:155:9:155:16 |
48+
| test.cpp:156:12:156:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | true | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 |
49+
| test.cpp:158:12:158:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -2 | true | CompareEQ: ... == ... | test.cpp:155:9:155:16 | test.cpp:155:9:155:16 |
50+
| test.cpp:158:12:158:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -2 | true | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 |
51+
| test.cpp:161:12:161:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -2 | true | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 |
52+
| test.cpp:161:12:161:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -2 | true | CompareNE: ... != ... | test.cpp:160:9:160:16 | test.cpp:160:9:160:16 |
53+
| test.cpp:163:12:163:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | false | CompareNE: ... != ... | test.cpp:160:9:160:16 | test.cpp:160:9:160:16 |
54+
| test.cpp:163:12:163:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | true | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 |
55+
| test.cpp:163:12:163:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | true | CompareNE: ... != ... | test.cpp:160:9:160:16 | test.cpp:160:9:160:16 |
56+
| test.cpp:167:12:167:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | 0 | false | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 |
57+
| test.cpp:167:12:167:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | 1 | false | CompareEQ: ... == ... | test.cpp:166:9:166:16 | test.cpp:166:9:166:16 |
58+
| test.cpp:167:12:167:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | 1 | true | CompareEQ: ... == ... | test.cpp:166:9:166:16 | test.cpp:166:9:166:16 |
59+
| test.cpp:169:12:169:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | 0 | false | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 |

cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,25 @@ int test13(int x) {
148148
} while(false);
149149
return i;
150150
}
151+
152+
// unequal bound narrowing
153+
int test14(int x, int y) {
154+
if(x < y) {
155+
if (x == y-1) {
156+
sink(x);
157+
} else {
158+
sink(x);
159+
}
160+
if (x != y-1) {
161+
sink(x);
162+
} else {
163+
sink(x);
164+
}
165+
} else {
166+
if (y == x-1) {
167+
sink(x);
168+
} else {
169+
sink(x);
170+
}
171+
}
172+
}

0 commit comments

Comments
 (0)