diff --git a/cpp/ql/lib/change-notes/2024-03-19-predicates-for-switches-as-guards.md b/cpp/ql/lib/change-notes/2024-03-19-predicates-for-switches-as-guards.md new file mode 100644 index 000000000000..3dde8805599f --- /dev/null +++ b/cpp/ql/lib/change-notes/2024-03-19-predicates-for-switches-as-guards.md @@ -0,0 +1,5 @@ +--- +category: feature +--- +* Added a predicate `GuardCondition.comparesEq/4` to query whether an expression is compared to a constant. +* Added a predicate `GuardCondition.ensuresEq/4` to query whether a basic block is guarded by an expression being equal to a constant. \ No newline at end of file diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll index e7762fc9fa85..ab67d77f5cd0 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll @@ -137,6 +137,17 @@ class GuardCondition extends Expr { */ cached predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { none() } + + /** Holds if (determined by this guard) `e == k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */ + cached + predicate comparesEq(Expr e, int k, boolean areEqual, boolean testIsTrue) { none() } + + /** + * Holds if (determined by this guard) `e == k` must be `areEqual` in `block`. + * If `areEqual = false` then this implies `e != k`. + */ + cached + predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) { none() } } /** @@ -184,6 +195,20 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition { this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) ) } + + override predicate comparesEq(Expr e, int k, boolean areEqual, boolean testIsTrue) { + exists(boolean partIsTrue, GuardCondition part | + this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue) + | + part.comparesEq(e, k, areEqual, partIsTrue) + ) + } + + override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) { + exists(boolean testIsTrue | + this.comparesEq(e, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) + ) + } } /** @@ -245,6 +270,21 @@ private class GuardConditionFromIR extends GuardCondition { ) } + override predicate comparesEq(Expr e, int k, boolean areEqual, boolean testIsTrue) { + exists(Instruction i | + i.getUnconvertedResultExpression() = e and + ir.comparesEq(i.getAUse(), k, areEqual, testIsTrue) + ) + } + + override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) { + exists(Instruction i, boolean testIsTrue | + i.getUnconvertedResultExpression() = e and + ir.comparesEq(i.getAUse(), k, areEqual, testIsTrue) and + this.controls(block, testIsTrue) + ) + } + /** * Holds if this condition controls `block`, meaning that `block` is only * entered if the value of this condition is `v`. This helper @@ -446,7 +486,25 @@ class IRGuardCondition extends Instruction { /** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */ cached predicate comparesEq(Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue) { - compares_eq(this, left, right, k, areEqual, testIsTrue) + exists(BooleanValue value | + compares_eq(this, left, right, k, areEqual, value) and + value.getValue() = testIsTrue + ) + } + + /** 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 + ) } /** @@ -455,8 +513,19 @@ class IRGuardCondition extends Instruction { */ cached predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) { - exists(boolean testIsTrue | - compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue) + exists(AbstractValue value | + compares_eq(this, left, right, k, areEqual, value) and this.valueControls(block, value) + ) + } + + /** + * 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) ) } @@ -468,9 +537,21 @@ class IRGuardCondition extends Instruction { predicate ensuresEqEdge( Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean areEqual ) { - exists(boolean testIsTrue | - compares_eq(this, left, right, k, areEqual, testIsTrue) and - this.controlsEdge(pred, succ, testIsTrue) + exists(AbstractValue value | + compares_eq(this, left, right, k, areEqual, value) and + this.valueControlsEdge(pred, succ, value) + ) + } + + /** + * 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) ) } @@ -572,52 +653,98 @@ private Instruction getBranchForCondition(Instruction guard) { * Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`. */ private predicate compares_eq( - Instruction test, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue + Instruction test, Operand left, Operand right, int k, boolean areEqual, AbstractValue value ) { /* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */ - exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) | - areEqual = true and testIsTrue = eq + exists(AbstractValue v | simple_comparison_eq(test, left, right, k, v) | + areEqual = true and value = v or - areEqual = false and testIsTrue = eq.booleanNot() + areEqual = false and value = v.getDualValue() ) or // I think this is handled by forwarding in controlsBlock. //or //logical_comparison_eq(test, left, right, k, areEqual, testIsTrue) /* a == b + k => b == a - k */ - exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, testIsTrue)) + exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, value)) or - complex_eq(test, left, right, k, areEqual, testIsTrue) + complex_eq(test, left, right, k, areEqual, value) or /* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */ - exists(boolean isFalse | testIsTrue = isFalse.booleanNot() | - compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, isFalse) + exists(AbstractValue dual | value = dual.getDualValue() | + compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, dual) + ) +} + +/** 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, boolean areEqual + CompareInstruction cmp, Operand left, Operand right, int k, AbstractValue value ) { left = cmp.getLeftOperand() and cmp instanceof CompareEQInstruction and right = cmp.getRightOperand() and k = 0 and - areEqual = true + value.(BooleanValue).getValue() = true or left = cmp.getLeftOperand() and cmp instanceof CompareNEInstruction and right = cmp.getRightOperand() and k = 0 and - areEqual = false + 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, CaseEdge case | + test = switch.getExpression() and + op.getDef() = test and + case = value.(MatchValue).getCase() and + exists(switch.getSuccessor(case)) and + case.getValue().toInt() = k + ) +} + +private predicate complex_eq( + CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value +) { + sub_eq(cmp, left, right, k, areEqual, value) + or + add_eq(cmp, left, right, k, areEqual, value) } private predicate complex_eq( - CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue + Instruction test, Operand op, int k, boolean areEqual, AbstractValue value ) { - sub_eq(cmp, left, right, k, areEqual, testIsTrue) + sub_eq(test, op, k, areEqual, value) or - add_eq(cmp, left, right, k, areEqual, testIsTrue) + add_eq(test, op, k, areEqual, value) } /* @@ -768,44 +895,61 @@ private predicate add_lt( // left - x == right + c => left == right + (c+x) // left == (right - x) + c => left == right + (c-x) private predicate sub_eq( - CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue + CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value ) { exists(SubInstruction lhs, int c, int x | - compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and + compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and left = lhs.getLeftOperand() and x = int_value(lhs.getRight()) and k = c + x ) or exists(SubInstruction rhs, int c, int x | - compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and + compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and right = rhs.getLeftOperand() and x = int_value(rhs.getRight()) and k = c - x ) or exists(PointerSubInstruction lhs, int c, int x | - compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and + compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and left = lhs.getLeftOperand() and x = int_value(lhs.getRight()) and k = c + x ) or exists(PointerSubInstruction rhs, int c, int x | - compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and + compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and right = rhs.getLeftOperand() and x = int_value(rhs.getRight()) and k = c - x ) } +// 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( - CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue + CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value ) { exists(AddInstruction lhs, int c, int x | - compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and + compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and ( left = lhs.getLeftOperand() and x = int_value(lhs.getRight()) or @@ -815,7 +959,7 @@ private predicate add_eq( ) or exists(AddInstruction rhs, int c, int x | - compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and + compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and ( right = rhs.getLeftOperand() and x = int_value(rhs.getRight()) or @@ -825,7 +969,7 @@ private predicate add_eq( ) or exists(PointerAddInstruction lhs, int c, int x | - compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and + compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and ( left = lhs.getLeftOperand() and x = int_value(lhs.getRight()) or @@ -835,7 +979,7 @@ private predicate add_eq( ) or exists(PointerAddInstruction rhs, int c, int x | - compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and + compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and ( right = rhs.getLeftOperand() and x = int_value(rhs.getRight()) or @@ -845,5 +989,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() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll index 91e1fe03e233..81db183fa63e 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll @@ -90,6 +90,15 @@ class CaseEdge extends EdgeKind, TCaseEdge { * Gets the largest value of the switch expression for which control will flow along this edge. */ final string getMaxValue() { result = maxValue } + + /** + * Gets the unique value of the switch expression for which control will + * flow along this edge, if any. + */ + final string getValue() { + minValue = maxValue and + result = minValue + } } /** diff --git a/cpp/ql/src/Critical/ScanfChecks.qll b/cpp/ql/src/Critical/ScanfChecks.qll index b2464ecc9f48..403df4715f3e 100644 --- a/cpp/ql/src/Critical/ScanfChecks.qll +++ b/cpp/ql/src/Critical/ScanfChecks.qll @@ -11,7 +11,7 @@ private predicate exprInBooleanContext(Expr e) { exists(IRGuardCondition gc | exists(Instruction i | i.getUnconvertedResultExpression() = e and - gc.comparesEq(valueNumber(i).getAUse(), zero(), 0, _, _) + gc.comparesEq(valueNumber(i).getAUse(), 0, _, _) ) or gc.getUnconvertedResultExpression() = e @@ -36,10 +36,6 @@ private string getEofValue() { ) } -private ConstantInstruction getEofInstruction() { result.getValue() = getEofValue() } - -private Operand eof() { result.getDef() = getEofInstruction() } - /** * Holds if the value of `call` has been checked to not equal `EOF`. */ @@ -47,7 +43,7 @@ private predicate checkedForEof(ScanfFunctionCall call) { exists(IRGuardCondition gc | exists(Instruction i | i.getUnconvertedResultExpression() = call | // call == EOF - gc.comparesEq(valueNumber(i).getAUse(), eof(), 0, _, _) + gc.comparesEq(valueNumber(i).getAUse(), getEofValue().toInt(), _, _) or // call < 0 (EOF is guaranteed to be negative) gc.comparesLt(valueNumber(i).getAUse(), zero(), 0, true, _) diff --git a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected index ac8068e768dc..2eb749580b4c 100644 --- a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected +++ b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected @@ -62,7 +62,9 @@ astGuardsCompare | 26 | x >= 0+1 when ... > ... is true | | 31 | - ... != x+0 when ... == ... is false | | 31 | - ... == x+0 when ... == ... is true | +| 31 | x != -1 when ... == ... is false | | 31 | x != - ...+0 when ... == ... is false | +| 31 | x == -1 when ... == ... is true | | 31 | x == - ...+0 when ... == ... is true | | 34 | 10 < j+1 when ... < ... is false | | 34 | 10 >= j+1 when ... < ... is true | @@ -86,15 +88,20 @@ astGuardsCompare | 58 | 0 < y+1 when ... \|\| ... is false | | 58 | 0 == x+0 when ... == ... is true | | 58 | 0 >= y+1 when ... < ... is true | +| 58 | x != 0 when ... == ... is false | +| 58 | x != 0 when ... \|\| ... is false | | 58 | x != 0+0 when ... == ... is false | | 58 | x != 0+0 when ... \|\| ... is false | +| 58 | x == 0 when ... == ... is true | | 58 | x == 0+0 when ... == ... is true | | 58 | y < 0+0 when ... < ... is true | | 58 | y >= 0+0 when ... < ... is false | | 58 | y >= 0+0 when ... \|\| ... is false | | 75 | 0 != x+0 when ... == ... is false | | 75 | 0 == x+0 when ... == ... is true | +| 75 | x != 0 when ... == ... is false | | 75 | x != 0+0 when ... == ... is false | +| 75 | x == 0 when ... == ... is true | | 75 | x == 0+0 when ... == ... is true | | 85 | 0 != x+0 when ... == ... is false | | 85 | 0 != y+0 when ... != ... is true | @@ -102,15 +109,23 @@ astGuardsCompare | 85 | 0 == x+0 when ... && ... is true | | 85 | 0 == x+0 when ... == ... is true | | 85 | 0 == y+0 when ... != ... is false | +| 85 | x != 0 when ... == ... is false | | 85 | x != 0+0 when ... == ... is false | +| 85 | x == 0 when ... && ... is true | +| 85 | x == 0 when ... == ... is true | | 85 | x == 0+0 when ... && ... is true | | 85 | x == 0+0 when ... == ... is true | +| 85 | y != 0 when ... != ... is true | +| 85 | y != 0 when ... && ... is true | | 85 | y != 0+0 when ... != ... is true | | 85 | y != 0+0 when ... && ... is true | +| 85 | y == 0 when ... != ... is false | | 85 | y == 0+0 when ... != ... is false | | 94 | 0 != x+0 when ... != ... is true | | 94 | 0 == x+0 when ... != ... is false | +| 94 | x != 0 when ... != ... is true | | 94 | x != 0+0 when ... != ... is true | +| 94 | x == 0 when ... != ... is false | | 94 | x == 0+0 when ... != ... is false | | 102 | 10 < j+1 when ... < ... is false | | 102 | 10 >= j+1 when ... < ... is true | @@ -122,8 +137,11 @@ astGuardsCompare | 109 | 0 < y+1 when ... \|\| ... is false | | 109 | 0 == x+0 when ... == ... is true | | 109 | 0 >= y+1 when ... < ... is true | +| 109 | x != 0 when ... == ... is false | +| 109 | x != 0 when ... \|\| ... is false | | 109 | x != 0+0 when ... == ... is false | | 109 | x != 0+0 when ... \|\| ... is false | +| 109 | x == 0 when ... == ... is true | | 109 | x == 0+0 when ... == ... is true | | 109 | y < 0+0 when ... < ... is true | | 109 | y >= 0+0 when ... < ... is false | @@ -162,7 +180,9 @@ astGuardsCompare | 165 | y >= x+43 when ... < ... is true | | 175 | 0 != call to foo+0 when ... == ... is false | | 175 | 0 == call to foo+0 when ... == ... is true | +| 175 | call to foo != 0 when ... == ... is false | | 175 | call to foo != 0+0 when ... == ... is false | +| 175 | call to foo == 0 when ... == ... is true | | 175 | call to foo == 0+0 when ... == ... is true | astGuardsControl | test.c:7:9:7:13 | ... > ... | false | 10 | 11 | @@ -443,6 +463,34 @@ astGuardsEnsure | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 34 | 34 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 30 | 30 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 | +astGuardsEnsure_const +| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 58 | 58 | +| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 | +| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 | +| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | != | 0 | 78 | 79 | +| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 75 | 77 | +| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 85 | 85 | +| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 | +| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 | +| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 | +| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | != | 0 | 94 | 96 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 70 | 70 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 99 | 102 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 102 | 102 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 107 | 109 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 109 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 117 | +| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 113 | 113 | +| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 109 | 109 | +| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 | +| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 | +| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | != | 0 | 175 | 175 | +| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | == | 0 | 175 | 175 | +| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 30 | 30 | +| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 34 | 34 | +| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 30 | 30 | +| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 31 | 32 | irGuards | test.c:7:9:7:13 | CompareGT: ... > ... | | test.c:17:8:17:12 | CompareLT: ... < ... | @@ -497,7 +545,9 @@ irGuardsCompare | 26 | x >= 0+1 when CompareGT: ... > ... is true | | 31 | - ... != x+0 when CompareEQ: ... == ... is false | | 31 | - ... == x+0 when CompareEQ: ... == ... is true | +| 31 | x != -1 when CompareEQ: ... == ... is false | | 31 | x != - ...+0 when CompareEQ: ... == ... is false | +| 31 | x == -1 when CompareEQ: ... == ... is true | | 31 | x == - ...+0 when CompareEQ: ... == ... is true | | 34 | 10 < j+1 when CompareLT: ... < ... is false | | 34 | 10 >= j+1 when CompareLT: ... < ... is true | @@ -519,25 +569,35 @@ irGuardsCompare | 58 | 0 < y+1 when CompareLT: ... < ... is false | | 58 | 0 == x+0 when CompareEQ: ... == ... is true | | 58 | 0 >= y+1 when CompareLT: ... < ... is true | +| 58 | x != 0 when CompareEQ: ... == ... is false | | 58 | x != 0+0 when CompareEQ: ... == ... is false | +| 58 | x == 0 when CompareEQ: ... == ... is true | | 58 | x == 0+0 when CompareEQ: ... == ... is true | | 58 | y < 0+0 when CompareLT: ... < ... is true | | 58 | y >= 0+0 when CompareLT: ... < ... is false | | 75 | 0 != x+0 when CompareEQ: ... == ... is false | | 75 | 0 == x+0 when CompareEQ: ... == ... is true | +| 75 | x != 0 when CompareEQ: ... == ... is false | | 75 | x != 0+0 when CompareEQ: ... == ... is false | +| 75 | x == 0 when CompareEQ: ... == ... is true | | 75 | x == 0+0 when CompareEQ: ... == ... is true | | 85 | 0 != x+0 when CompareEQ: ... == ... is false | | 85 | 0 != y+0 when CompareNE: ... != ... is true | | 85 | 0 == x+0 when CompareEQ: ... == ... is true | | 85 | 0 == y+0 when CompareNE: ... != ... is false | +| 85 | x != 0 when CompareEQ: ... == ... is false | | 85 | x != 0+0 when CompareEQ: ... == ... is false | +| 85 | x == 0 when CompareEQ: ... == ... is true | | 85 | x == 0+0 when CompareEQ: ... == ... is true | +| 85 | y != 0 when CompareNE: ... != ... is true | | 85 | y != 0+0 when CompareNE: ... != ... is true | +| 85 | y == 0 when CompareNE: ... != ... is false | | 85 | y == 0+0 when CompareNE: ... != ... is false | | 94 | 0 != x+0 when CompareNE: ... != ... is true | | 94 | 0 == x+0 when CompareNE: ... != ... is false | +| 94 | x != 0 when CompareNE: ... != ... is true | | 94 | x != 0+0 when CompareNE: ... != ... is true | +| 94 | x == 0 when CompareNE: ... != ... is false | | 94 | x == 0+0 when CompareNE: ... != ... is false | | 102 | 10 < j+1 when CompareLT: ... < ... is false | | 102 | 10 >= j+1 when CompareLT: ... < ... is true | @@ -547,7 +607,9 @@ irGuardsCompare | 109 | 0 < y+1 when CompareLT: ... < ... is false | | 109 | 0 == x+0 when CompareEQ: ... == ... is true | | 109 | 0 >= y+1 when CompareLT: ... < ... is true | +| 109 | x != 0 when CompareEQ: ... == ... is false | | 109 | x != 0+0 when CompareEQ: ... == ... is false | +| 109 | x == 0 when CompareEQ: ... == ... is true | | 109 | x == 0+0 when CompareEQ: ... == ... is true | | 109 | y < 0+0 when CompareLT: ... < ... is true | | 109 | y >= 0+0 when CompareLT: ... < ... is false | @@ -585,7 +647,9 @@ irGuardsCompare | 165 | y >= x+43 when CompareLT: ... < ... is true | | 175 | 0 != call to foo+0 when CompareEQ: ... == ... is false | | 175 | 0 == call to foo+0 when CompareEQ: ... == ... is true | +| 175 | call to foo != 0 when CompareEQ: ... == ... is false | | 175 | call to foo != 0+0 when CompareEQ: ... == ... is false | +| 175 | call to foo == 0 when CompareEQ: ... == ... is true | | 175 | call to foo == 0+0 when CompareEQ: ... == ... is true | irGuardsControl | test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 | @@ -841,3 +905,27 @@ irGuardsEnsure | test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | != | test.cpp:31:7:31:7 | Load: x | 0 | 34 | 34 | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 30 | 30 | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 32 | 32 | +irGuardsEnsure_const +| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | 0 | 58 | 58 | +| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | 0 | 62 | 62 | +| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | != | 0 | 79 | 79 | +| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | 0 | 76 | 76 | +| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 85 | 85 | +| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 86 | 86 | +| test.c:85:18:85:23 | CompareNE: ... != ... | test.c:85:18:85:18 | Load: y | != | 0 | 86 | 86 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | != | 0 | 95 | 95 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 70 | 70 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 99 | 99 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 102 | 102 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 103 | 103 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 107 | 107 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 109 | 109 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 110 | 110 | +| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 113 | 113 | +| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | 0 | 109 | 109 | +| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | 0 | 113 | 113 | +| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | != | 0 | 175 | 175 | +| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | == | 0 | 175 | 175 | +| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | -1 | 34 | 34 | +| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 30 | 30 | +| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 32 | 32 | diff --git a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.ql b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.ql index fe3d92d2c2b5..263c30c2fec5 100644 --- a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.ql +++ b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.ql @@ -4,22 +4,32 @@ import semmle.code.cpp.controlflow.IRGuards query predicate astGuards(GuardCondition guard) { any() } query predicate astGuardsCompare(int startLine, string msg) { - exists(GuardCondition guard, Expr left, Expr right, int k, string which, string op | + exists(GuardCondition guard, Expr left, int k, string which, string op | exists(boolean sense | sense = true and which = "true" or sense = false and which = "false" | - guard.comparesLt(left, right, k, true, sense) and op = " < " + exists(Expr right | + guard.comparesLt(left, right, k, true, sense) and op = " < " + or + guard.comparesLt(left, right, k, false, sense) and op = " >= " + or + guard.comparesEq(left, right, k, true, sense) and op = " == " + or + guard.comparesEq(left, right, k, false, sense) and op = " != " + | + msg = left + op + right + "+" + k + " when " + guard + " is " + which + ) or - guard.comparesLt(left, right, k, false, sense) and op = " >= " - or - guard.comparesEq(left, right, k, true, sense) and op = " == " - or - guard.comparesEq(left, right, k, false, sense) and op = " != " + ( + guard.comparesEq(left, k, true, sense) and op = " == " + or + guard.comparesEq(left, k, false, sense) and op = " != " + ) and + msg = left + op + k + " when " + guard + " is " + which ) and - startLine = guard.getLocation().getStartLine() and - msg = left + op + right + "+" + k + " when " + guard + " is " + which + startLine = guard.getLocation().getStartLine() ) } @@ -46,28 +56,52 @@ query predicate astGuardsEnsure( ) } +query predicate astGuardsEnsure_const( + GuardCondition guard, Expr left, string op, int k, int start, int end +) { + exists(BasicBlock block | + guard.ensuresEq(left, k, block, true) and op = "==" + or + guard.ensuresEq(left, k, block, false) and op = "!=" + | + block.hasLocationInfo(_, start, _, end, _) + ) +} + query predicate irGuards(IRGuardCondition guard) { any() } query predicate irGuardsCompare(int startLine, string msg) { - exists(IRGuardCondition guard, Operand left, Operand right, int k, string which, string op | + exists(IRGuardCondition guard, Operand left, int k, string which, string op | exists(boolean sense | sense = true and which = "true" or sense = false and which = "false" | - guard.comparesLt(left, right, k, true, sense) and op = " < " + exists(Operand right | + guard.comparesLt(left, right, k, true, sense) and op = " < " + or + guard.comparesLt(left, right, k, false, sense) and op = " >= " + or + guard.comparesEq(left, right, k, true, sense) and op = " == " + or + guard.comparesEq(left, right, k, false, sense) and op = " != " + | + msg = + left.getAnyDef().getUnconvertedResultExpression() + op + + right.getAnyDef().getUnconvertedResultExpression() + "+" + k + " when " + guard + " is " + + which + ) or - guard.comparesLt(left, right, k, false, sense) and op = " >= " - or - guard.comparesEq(left, right, k, true, sense) and op = " == " - or - guard.comparesEq(left, right, k, false, sense) and op = " != " + ( + guard.comparesEq(left, k, true, sense) and op = " == " + or + guard.comparesEq(left, k, false, sense) and op = " != " + ) and + msg = + left.getAnyDef().getUnconvertedResultExpression() + op + k + " when " + guard + " is " + + which ) and - startLine = guard.getLocation().getStartLine() and - msg = - left.getAnyDef().getUnconvertedResultExpression() + op + - right.getAnyDef().getUnconvertedResultExpression() + "+" + k + " when " + guard + " is " + - which + startLine = guard.getLocation().getStartLine() ) } @@ -95,3 +129,16 @@ query predicate irGuardsEnsure( block.getLocation().hasLocationInfo(_, start, _, end, _) ) } + +query predicate irGuardsEnsure_const( + IRGuardCondition guard, Instruction left, string op, int k, int start, int end +) { + exists(IRBlock block, Operand leftOp | + guard.ensuresEq(leftOp, k, block, true) and op = "==" + or + guard.ensuresEq(leftOp, k, block, false) and op = "!=" + | + leftOp = left.getAUse() and + block.getLocation().hasLocationInfo(_, start, _, end, _) + ) +} diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected index 58068f3991df..1057e8e10466 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected @@ -20,7 +20,9 @@ | 26 | x >= 0+1 when ... > ... is true | | 31 | - ... != x+0 when ... == ... is false | | 31 | - ... == x+0 when ... == ... is true | +| 31 | x != -1 when ... == ... is false | | 31 | x != - ...+0 when ... == ... is false | +| 31 | x == -1 when ... == ... is true | | 31 | x == - ...+0 when ... == ... is true | | 34 | 10 < j+1 when ... < ... is false | | 34 | 10 >= j+1 when ... < ... is true | @@ -44,15 +46,23 @@ | 58 | 0 < y+1 when ... \|\| ... is false | | 58 | 0 == x+0 when ... == ... is true | | 58 | 0 >= y+1 when ... < ... is true | +| 58 | x != 0 when ... == ... is false | +| 58 | x != 0 when ... \|\| ... is false | | 58 | x != 0+0 when ... == ... is false | | 58 | x != 0+0 when ... \|\| ... is false | +| 58 | x == 0 when ... == ... is true | | 58 | x == 0+0 when ... == ... is true | | 58 | y < 0+0 when ... < ... is true | | 58 | y >= 0+0 when ... < ... is false | | 58 | y >= 0+0 when ... \|\| ... is false | +| 61 | i == 0 when i is true | +| 61 | i == 1 when i is true | +| 61 | i == 2 when i is true | | 75 | 0 != x+0 when ... == ... is false | | 75 | 0 == x+0 when ... == ... is true | +| 75 | x != 0 when ... == ... is false | | 75 | x != 0+0 when ... == ... is false | +| 75 | x == 0 when ... == ... is true | | 75 | x == 0+0 when ... == ... is true | | 85 | 0 != x+0 when ... == ... is false | | 85 | 0 != y+0 when ... != ... is true | @@ -60,15 +70,23 @@ | 85 | 0 == x+0 when ... && ... is true | | 85 | 0 == x+0 when ... == ... is true | | 85 | 0 == y+0 when ... != ... is false | +| 85 | x != 0 when ... == ... is false | | 85 | x != 0+0 when ... == ... is false | +| 85 | x == 0 when ... && ... is true | +| 85 | x == 0 when ... == ... is true | | 85 | x == 0+0 when ... && ... is true | | 85 | x == 0+0 when ... == ... is true | +| 85 | y != 0 when ... != ... is true | +| 85 | y != 0 when ... && ... is true | | 85 | y != 0+0 when ... != ... is true | | 85 | y != 0+0 when ... && ... is true | +| 85 | y == 0 when ... != ... is false | | 85 | y == 0+0 when ... != ... is false | | 94 | 0 != x+0 when ... != ... is true | | 94 | 0 == x+0 when ... != ... is false | +| 94 | x != 0 when ... != ... is true | | 94 | x != 0+0 when ... != ... is true | +| 94 | x == 0 when ... != ... is false | | 94 | x == 0+0 when ... != ... is false | | 102 | 10 < j+1 when ... < ... is false | | 102 | 10 >= j+1 when ... < ... is true | @@ -80,8 +98,11 @@ | 109 | 0 < y+1 when ... \|\| ... is false | | 109 | 0 == x+0 when ... == ... is true | | 109 | 0 >= y+1 when ... < ... is true | +| 109 | x != 0 when ... == ... is false | +| 109 | x != 0 when ... \|\| ... is false | | 109 | x != 0+0 when ... == ... is false | | 109 | x != 0+0 when ... \|\| ... is false | +| 109 | x == 0 when ... == ... is true | | 109 | x == 0+0 when ... == ... is true | | 109 | y < 0+0 when ... < ... is true | | 109 | y >= 0+0 when ... < ... is false | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.ql b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.ql index a1373b2923b2..17d4fcaae941 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.ql +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.ql @@ -7,20 +7,30 @@ import cpp import semmle.code.cpp.controlflow.Guards -from GuardCondition guard, Expr left, Expr right, int k, string which, string op, string msg +from GuardCondition guard, Expr left, int k, string which, string op, string msg where exists(boolean sense | sense = true and which = "true" or sense = false and which = "false" | - guard.comparesLt(left, right, k, true, sense) and op = " < " + exists(Expr right | + guard.comparesLt(left, right, k, true, sense) and op = " < " + or + guard.comparesLt(left, right, k, false, sense) and op = " >= " + or + guard.comparesEq(left, right, k, true, sense) and op = " == " + or + guard.comparesEq(left, right, k, false, sense) and op = " != " + | + msg = left + op + right + "+" + k + " when " + guard + " is " + which + ) or - guard.comparesLt(left, right, k, false, sense) and op = " >= " - or - guard.comparesEq(left, right, k, true, sense) and op = " == " - or - guard.comparesEq(left, right, k, false, sense) and op = " != " - ) and - msg = left + op + right + "+" + k + " when " + guard + " is " + which + ( + guard.comparesEq(left, k, true, sense) and op = " == " + or + guard.comparesEq(left, k, false, sense) and op = " != " + ) and + msg = left + op + k + " when " + guard + " is " + which + ) select guard.getLocation().getStartLine(), msg