diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll index d714c1ecf30..60de332f847 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll @@ -452,6 +452,21 @@ class IRGuardCondition extends Instruction { ) } + /** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */ + cached + predicate comparesEq(Operand op, int k, boolean areEqual, boolean testIsTrue) { + exists(MatchValue mv | + compares_eq(this, op, k, areEqual, mv) and + // A match value cannot be dualized, so `testIsTrue` is always true + testIsTrue = true + ) + or + exists(BooleanValue bv | + compares_eq(this, op, k, areEqual, bv) and + bv.getValue() = testIsTrue + ) + } + /** * Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`. * If `areEqual = false` then this implies `left != right + k`. @@ -463,6 +478,17 @@ class IRGuardCondition extends Instruction { ) } + /** + * Holds if (determined by this guard) `op == k` must be `areEqual` in `block`. + * If `areEqual = false` then this implies `op != k`. + */ + cached + predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) { + exists(AbstractValue value | + compares_eq(this, op, k, areEqual, value) and this.valueControls(block, value) + ) + } + /** * Holds if (determined by this guard) `left == right + k` must be `areEqual` on the edge from * `pred` to `succ`. If `areEqual = false` then this implies `left != right + k`. @@ -477,6 +503,18 @@ class IRGuardCondition extends Instruction { ) } + /** + * Holds if (determined by this guard) `op == k` must be `areEqual` on the edge from + * `pred` to `succ`. If `areEqual = false` then this implies `op != k`. + */ + cached + predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) { + exists(AbstractValue value | + compares_eq(this, op, k, areEqual, value) and + this.valueControlsEdge(pred, succ, value) + ) + } + /** * Holds if this condition controls `block`, meaning that `block` is only * entered if the value of this condition is `v`. This helper @@ -598,6 +636,33 @@ private predicate compares_eq( ) } +/** Holds if `op == k` is `areEqual` given that `test` is equal to `value`. */ +private predicate compares_eq( + Instruction test, Operand op, int k, boolean areEqual, AbstractValue value +) { + /* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */ + exists(AbstractValue v | simple_comparison_eq(test, op, k, v) | + areEqual = true and value = v + or + areEqual = false and value = v.getDualValue() + ) + or + complex_eq(test, op, k, areEqual, value) + or + /* (x is true => (op == k)) => (!x is false => (op == k)) */ + exists(AbstractValue dual | value = dual.getDualValue() | + compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, areEqual, dual) + ) + or + // ((test is `areEqual` => op == const + k2) and const == `k1`) => + // test is `areEqual` => op == k1 + k2 + exists(int k1, int k2, ConstantInstruction const | + compares_eq(test, op, const.getAUse(), k2, areEqual, value) and + int_value(const) = k1 and + k = k1 + k2 + ) +} + /** Rearrange various simple comparisons into `left == right + k` form. */ private predicate simple_comparison_eq( CompareInstruction cmp, Operand left, Operand right, int k, AbstractValue value @@ -615,6 +680,15 @@ private predicate simple_comparison_eq( value.(BooleanValue).getValue() = false } +/** Rearrange various simple comparisons into `op == k` form. */ +private predicate simple_comparison_eq(Instruction test, Operand op, int k, AbstractValue value) { + exists(SwitchInstruction switch | + test = switch.getExpression() and + op.getDef() = test and + value.(MatchValue).getCase().getValue().toInt() = k + ) +} + private predicate complex_eq( CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value ) { @@ -623,6 +697,14 @@ private predicate complex_eq( add_eq(cmp, left, right, k, areEqual, value) } +private predicate complex_eq( + Instruction test, Operand op, int k, boolean areEqual, AbstractValue value +) { + sub_eq(test, op, k, areEqual, value) + or + add_eq(test, op, k, areEqual, value) +} + /* * Simplification of inequality expressions * Simplify conditions in the source to the canonical form l < r + k. @@ -802,6 +884,23 @@ private predicate sub_eq( ) } +// op - x == c => op == (c+x) +private predicate sub_eq(Instruction test, Operand op, int k, boolean areEqual, AbstractValue value) { + exists(SubInstruction sub, int c, int x | + compares_eq(test, sub.getAUse(), c, areEqual, value) and + op = sub.getLeftOperand() and + x = int_value(sub.getRight()) and + k = c + x + ) + or + exists(PointerSubInstruction sub, int c, int x | + compares_eq(test, sub.getAUse(), c, areEqual, value) and + op = sub.getLeftOperand() and + x = int_value(sub.getRight()) and + k = c + x + ) +} + // left + x == right + c => left == right + (c-x) // left == (right + x) + c => left == right + (c+x) private predicate add_eq( @@ -848,5 +947,30 @@ private predicate add_eq( ) } +// left + x == right + c => left == right + (c-x) +private predicate add_eq( + Instruction test, Operand left, int k, boolean areEqual, AbstractValue value +) { + exists(AddInstruction lhs, int c, int x | + compares_eq(test, lhs.getAUse(), c, areEqual, value) and + ( + left = lhs.getLeftOperand() and x = int_value(lhs.getRight()) + or + left = lhs.getRightOperand() and x = int_value(lhs.getLeft()) + ) and + k = c - x + ) + or + exists(PointerAddInstruction lhs, int c, int x | + compares_eq(test, lhs.getAUse(), c, areEqual, value) and + ( + left = lhs.getLeftOperand() and x = int_value(lhs.getRight()) + or + left = lhs.getRightOperand() and x = int_value(lhs.getLeft()) + ) and + k = c - x + ) +} + /** The int value of integer constant expression. */ private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }