Skip to content

Commit a78080c

Browse files
committed
C++: Implement less-than logic for guard conditions when comparing to constants.
1 parent 8b85735 commit a78080c

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,20 @@ class GuardCondition extends Expr {
125125
cached
126126
predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { none() }
127127

128+
/**
129+
* Holds if (determined by this guard) `e < k` evaluates to `isLessThan` if
130+
* this expression evaluates to `value`.
131+
*/
132+
cached
133+
predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) { none() }
134+
135+
/**
136+
* Holds if (determined by this guard) `e < k` must be `isLessThan` in `block`.
137+
* If `isLessThan = false` then this implies `left >= k`.
138+
*/
139+
cached
140+
predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) { none() }
141+
128142
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
129143
cached
130144
predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
@@ -176,12 +190,27 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
176190
)
177191
}
178192

193+
override predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) {
194+
exists(BooleanValue partValue, GuardCondition part |
195+
this.(BinaryLogicalOperation)
196+
.impliesValue(part, partValue.getValue(), value.(BooleanValue).getValue())
197+
|
198+
part.comparesLt(e, k, isLessThan, partValue)
199+
)
200+
}
201+
179202
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
180203
exists(boolean testIsTrue |
181204
this.comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
182205
)
183206
}
184207

208+
override predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) {
209+
exists(AbstractValue value |
210+
this.comparesLt(e, k, isLessThan, value) and this.valueControls(block, value)
211+
)
212+
}
213+
185214
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
186215
exists(boolean partIsTrue, GuardCondition part |
187216
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
@@ -236,6 +265,17 @@ private class GuardConditionFromIR extends GuardCondition {
236265
)
237266
}
238267

268+
/**
269+
* Holds if (determined by this guard) `e < k` evaluates to `isLessThan` if
270+
* this expression evaluates to `value`.
271+
*/
272+
override predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) {
273+
exists(Instruction i |
274+
i.getUnconvertedResultExpression() = e and
275+
ir.comparesLt(i.getAUse(), k, isLessThan, value)
276+
)
277+
}
278+
239279
/**
240280
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
241281
* If `isLessThan = false` then this implies `left >= right + k`.
@@ -249,6 +289,18 @@ private class GuardConditionFromIR extends GuardCondition {
249289
)
250290
}
251291

292+
/**
293+
* Holds if (determined by this guard) `e < k` must be `isLessThan` in `block`.
294+
* If `isLessThan = false` then this implies `e >= k`.
295+
*/
296+
override predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) {
297+
exists(Instruction i, AbstractValue value |
298+
i.getUnconvertedResultExpression() = e and
299+
ir.comparesLt(i.getAUse(), k, isLessThan, value) and
300+
this.valueControls(block, value)
301+
)
302+
}
303+
252304
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
253305
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
254306
exists(Instruction li, Instruction ri |
@@ -462,6 +514,15 @@ class IRGuardCondition extends Instruction {
462514
)
463515
}
464516

517+
/**
518+
* Holds if (determined by this guard) `op < k` evaluates to `isLessThan` if
519+
* this expression evaluates to `value`.
520+
*/
521+
cached
522+
predicate comparesLt(Operand op, int k, boolean isLessThan, AbstractValue value) {
523+
compares_lt(this, op, k, isLessThan, value)
524+
}
525+
465526
/**
466527
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
467528
* If `isLessThan = false` then this implies `left >= right + k`.
@@ -473,6 +534,17 @@ class IRGuardCondition extends Instruction {
473534
)
474535
}
475536

537+
/**
538+
* Holds if (determined by this guard) `op < k` must be `isLessThan` in `block`.
539+
* If `isLessThan = false` then this implies `op >= k`.
540+
*/
541+
cached
542+
predicate ensuresLt(Operand op, int k, IRBlock block, boolean isLessThan) {
543+
exists(AbstractValue value |
544+
compares_lt(this, op, k, isLessThan, value) and this.valueControls(block, value)
545+
)
546+
}
547+
476548
/**
477549
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` on the edge from
478550
* `pred` to `succ`. If `isLessThan = false` then this implies `left >= right + k`.
@@ -487,6 +559,18 @@ class IRGuardCondition extends Instruction {
487559
)
488560
}
489561

562+
/**
563+
* Holds if (determined by this guard) `op < k` must be `isLessThan` on the edge from
564+
* `pred` to `succ`. If `isLessThan = false` then this implies `op >= k`.
565+
*/
566+
cached
567+
predicate ensuresLtEdge(Operand left, int k, IRBlock pred, IRBlock succ, boolean isLessThan) {
568+
exists(AbstractValue value |
569+
compares_lt(this, left, k, isLessThan, value) and
570+
this.valueControlsEdge(pred, succ, value)
571+
)
572+
}
573+
490574
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
491575
cached
492576
predicate comparesEq(Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue) {
@@ -766,6 +850,24 @@ private predicate compares_lt(
766850
)
767851
}
768852

853+
/** Holds if `op < k` evaluates to `isLt` given that `test` evaluates to `value`. */
854+
private predicate compares_lt(Instruction test, Operand op, int k, boolean isLt, AbstractValue value) {
855+
simple_comparison_lt(test, op, k, isLt, value)
856+
or
857+
complex_lt(test, op, k, isLt, value)
858+
or
859+
/* (x is true => (op < k)) => (!x is false => (op < k)) */
860+
exists(AbstractValue dual | value = dual.getDualValue() |
861+
compares_lt(test.(LogicalNotInstruction).getUnary(), op, k, isLt, dual)
862+
)
863+
or
864+
exists(int k1, int k2, ConstantInstruction const |
865+
compares_lt(test, op, const.getAUse(), k2, isLt, value) and
866+
int_value(const) = k1 and
867+
k = k1 + k2
868+
)
869+
}
870+
769871
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
770872
private predicate compares_ge(
771873
Instruction test, Operand left, Operand right, int k, boolean isGe, AbstractValue value
@@ -796,6 +898,26 @@ private predicate simple_comparison_lt(CompareInstruction cmp, Operand left, Ope
796898
k = 1
797899
}
798900

901+
/** Rearrange various simple comparisons into `op < k` form. */
902+
private predicate simple_comparison_lt(
903+
Instruction test, Operand op, int k, boolean isLt, AbstractValue value
904+
) {
905+
exists(SwitchInstruction switch, CaseEdge case |
906+
test = switch.getExpression() and
907+
op.getDef() = test and
908+
case = value.(MatchValue).getCase() and
909+
exists(switch.getSuccessor(case)) and
910+
case.getMaxValue() > case.getMinValue()
911+
|
912+
// op <= k => op < k - 1
913+
isLt = true and
914+
case.getMaxValue().toInt() = k - 1
915+
or
916+
isLt = false and
917+
case.getMinValue().toInt() = k
918+
)
919+
}
920+
799921
private predicate complex_lt(
800922
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, AbstractValue value
801923
) {
@@ -804,6 +926,14 @@ private predicate complex_lt(
804926
add_lt(cmp, left, right, k, isLt, value)
805927
}
806928

929+
private predicate complex_lt(
930+
Instruction test, Operand left, int k, boolean isLt, AbstractValue value
931+
) {
932+
sub_lt(test, left, k, isLt, value)
933+
or
934+
add_lt(test, left, k, isLt, value)
935+
}
936+
807937
// left - x < right + c => left < right + (c+x)
808938
// left < (right - x) + c => left < right + (c-x)
809939
private predicate sub_lt(
@@ -838,6 +968,22 @@ private predicate sub_lt(
838968
)
839969
}
840970

971+
private predicate sub_lt(Instruction test, Operand left, int k, boolean isLt, AbstractValue value) {
972+
exists(SubInstruction lhs, int c, int x |
973+
compares_lt(test, lhs.getAUse(), c, isLt, value) and
974+
left = lhs.getLeftOperand() and
975+
x = int_value(lhs.getRight()) and
976+
k = c + x
977+
)
978+
or
979+
exists(PointerSubInstruction lhs, int c, int x |
980+
compares_lt(test, lhs.getAUse(), c, isLt, value) and
981+
left = lhs.getLeftOperand() and
982+
x = int_value(lhs.getRight()) and
983+
k = c + x
984+
)
985+
}
986+
841987
// left + x < right + c => left < right + (c-x)
842988
// left < (right + x) + c => left < right + (c+x)
843989
private predicate add_lt(
@@ -884,6 +1030,28 @@ private predicate add_lt(
8841030
)
8851031
}
8861032

1033+
private predicate add_lt(Instruction test, Operand left, int k, boolean isLt, AbstractValue value) {
1034+
exists(AddInstruction lhs, int c, int x |
1035+
compares_lt(test, lhs.getAUse(), c, isLt, value) and
1036+
(
1037+
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
1038+
or
1039+
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
1040+
) and
1041+
k = c - x
1042+
)
1043+
or
1044+
exists(PointerAddInstruction lhs, int c, int x |
1045+
compares_lt(test, lhs.getAUse(), c, isLt, value) and
1046+
(
1047+
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
1048+
or
1049+
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
1050+
) and
1051+
k = c - x
1052+
)
1053+
}
1054+
8871055
// left - x == right + c => left == right + (c+x)
8881056
// left == (right - x) + c => left == right + (c-x)
8891057
private predicate sub_eq(

0 commit comments

Comments
 (0)