diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll index 9b4d28430ff3..054672741644 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll @@ -5,9 +5,54 @@ import cpp import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.ValueNumbering private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag +/** + * Variant of valueNumber that accounts for conversions. + * Note: this predicate is defined in IRGuards to take advantage of + * IRGuards definition of int_value. + */ +ValueNumber valueNumberWithConversions(Instruction instr) { + exists(ValueNumber vn | vn = valueNumber(instr) | + // GVN using valueNumber directly + result = vn + or + // GVN for the ConvertInstruction conversion + result = valueNumberWithConversions(vn.getAnInstruction().(ConvertInstruction).getUnary()) + or + // GVN for the CompareNEInstruction conversion for a comparison with 0 (conversion of value to boolean in c) + exists(CompareNEInstruction cmp | cmp = vn.getAnInstruction() | + result = valueNumberWithConversions(cmp.getLeft()) and + int_value(cmp.getRight()) = 0 + ) + ) +} + +/** + * Returns `instr` or any instruction used to define `instr`. + */ +pragma[inline] +private Instruction getDerivedInstruction(Instruction instr) { + result = instr + or + result = valueNumber(instr).getAnInstruction().(StoreInstruction).getSourceValue() + or + result = derivedThroughConversion(valueNumber(instr)) +} + +private Instruction derivedThroughConversion(ValueNumber vn) { + // GVN for the ConvertInstruction conversion + result = getDerivedInstruction(vn.getAnInstruction().(ConvertInstruction).getUnary()) + or + // GVN for the CompareNEInstruction conversion for a comparison with 0 (conversion of value to boolean in c) + exists(CompareNEInstruction comp | comp = vn.getAnInstruction() | + result = getDerivedInstruction(comp.getLeft()) and + int_value(comp.getRight()) = 0 + ) +} + /** * Holds if `block` consists of an `UnreachedInstruction`. * @@ -538,7 +583,8 @@ class IRGuardCondition extends Instruction { cached predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean isLessThan) { exists(AbstractValue value | - compares_lt(this, left, right, k, isLessThan, value) and this.valueControls(block, value) + compares_lt(this, left, right, k, isLessThan, value) and + this.valueControls(block, value) ) } @@ -549,7 +595,8 @@ class IRGuardCondition extends Instruction { cached predicate ensuresLt(Operand op, int k, IRBlock block, boolean isLessThan) { exists(AbstractValue value | - compares_lt(this, op, k, isLessThan, value) and this.valueControls(block, value) + compares_lt(this, op, k, isLessThan, value) and + this.valueControls(block, value) ) } @@ -601,7 +648,8 @@ class IRGuardCondition extends Instruction { cached predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) { exists(AbstractValue value | - compares_eq(this, left, right, k, areEqual, value) and this.valueControls(block, value) + compares_eq(this, left, right, k, areEqual, value) and + this.valueControls(block, value) ) } @@ -612,7 +660,8 @@ class IRGuardCondition extends Instruction { cached predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) { exists(AbstractValue value | - unary_compares_eq(this, op, k, areEqual, false, value) and this.valueControls(block, value) + unary_compares_eq(this, op, k, areEqual, false, value) and + this.valueControls(block, value) ) } @@ -742,27 +791,30 @@ private Instruction getBranchForCondition(Instruction guard) { private predicate compares_eq( 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(AbstractValue v | simple_comparison_eq(test, left, right, k, v) | - areEqual = true and value = v + exists(Instruction derived | derived = getDerivedInstruction(test) | + /* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */ + exists(AbstractValue v | simple_comparison_eq(derived, left, right, k, v) | + areEqual = true and value = v + or + areEqual = false and value = v.getDualValue() + ) or - 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, value)) - or - complex_eq(test, left, right, k, areEqual, value) - or - /* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */ - exists(AbstractValue dual | value = dual.getDualValue() | - compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, dual) + // 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(derived, right, left, mk, areEqual, value)) + or + complex_eq(derived, left, right, k, areEqual, value) + or + /* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */ + exists(AbstractValue dual | value = dual.getDualValue() | + compares_eq(derived.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, dual) + ) + or + compares_eq(derived.(BuiltinExpectCallInstruction).getCondition(), left, right, k, areEqual, + value) ) - or - compares_eq(test.(BuiltinExpectCallInstruction).getCondition(), left, right, k, areEqual, value) } /** @@ -803,38 +855,41 @@ private predicate compares_eq( private predicate unary_compares_eq( Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value ) { - /* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */ - exists(AbstractValue v | - unary_simple_comparison_eq(test, k, inNonZeroCase, v) and op.getDef() = test - | - areEqual = true and value = v + exists(Instruction derived | derived = getDerivedInstruction(test) | + /* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */ + exists(AbstractValue v | + unary_simple_comparison_eq(derived, k, inNonZeroCase, v) and op.getDef() = derived + | + areEqual = true and value = v + or + areEqual = false and value = v.getDualValue() + ) or - areEqual = false and value = v.getDualValue() - ) - or - unary_complex_eq(test, op, k, areEqual, inNonZeroCase, value) - or - /* (x is true => (op == k)) => (!x is false => (op == k)) */ - exists(AbstractValue dual, boolean inNonZeroCase0 | - value = dual.getDualValue() and - unary_compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, inNonZeroCase0, areEqual, dual) - | - k = 0 and inNonZeroCase = inNonZeroCase0 + unary_complex_eq(derived, op, k, areEqual, inNonZeroCase, value) or - k != 0 and inNonZeroCase = true - ) - or - // ((test is `areEqual` => op == const + k2) and const == `k1`) => - // test is `areEqual` => op == k1 + k2 - inNonZeroCase = false and - 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 + /* (x is true => (op == k)) => (!x is false => (op == k)) */ + exists(AbstractValue dual, boolean inNonZeroCase0 | + value = dual.getDualValue() and + unary_compares_eq(derived.(LogicalNotInstruction).getUnary(), op, k, inNonZeroCase0, areEqual, + dual) + | + k = 0 and inNonZeroCase = inNonZeroCase0 + or + k != 0 and inNonZeroCase = true + ) + or + // ((test is `areEqual` => op == const + k2) and const == `k1`) => + // test is `areEqual` => op == k1 + k2 + inNonZeroCase = false and + exists(int k1, int k2, ConstantInstruction const | + compares_eq(derived, op, const.getAUse(), k2, areEqual, value) and + int_value(const) = k1 and + k = k1 + k2 + ) + or + unary_compares_eq(derived.(BuiltinExpectCallInstruction).getCondition(), op, k, areEqual, + inNonZeroCase, value) ) - or - unary_compares_eq(test.(BuiltinExpectCallInstruction).getCondition(), op, k, areEqual, - inNonZeroCase, value) } /** Rearrange various simple comparisons into `left == right + k` form. */ @@ -868,6 +923,18 @@ private predicate unary_simple_comparison_eq( inNonZeroCase = false ) or + ( + exists(BinaryLogicalOperation e | e.getAnOperand() = test.getUnconvertedResultExpression()) + or + exists(UnaryLogicalOperation e | e.getAnOperand() = test.getUnconvertedResultExpression()) + or + exists(IfStmt i | i.getCondition() = test.getUnconvertedResultExpression()) + or + exists(Loop i | i.getCondition() = test.getUnconvertedResultExpression()) + or + exists(ConditionalExpr c | c.getCondition() = test.getUnconvertedResultExpression()) + // Todo recursive conditionalExpr in guard + ) and // Any instruction with an integral type could potentially be part of a // check for nullness when used in a guard. So we include all integral // typed instructions here. However, since some of these instructions are @@ -912,6 +979,8 @@ private predicate unary_simple_comparison_eq( value.(BooleanValue).getValue() = false and inNonZeroCase = false ) + // Has to be in a boolean operator (operanad of OR or AND) + // has to be a 'guard' (loop, if, switch, ternary) } /** A call to the builtin operation `__builtin_expect`. */ @@ -997,37 +1066,44 @@ private predicate unary_complex_eq( private predicate compares_lt( Instruction test, Operand left, Operand right, int k, boolean isLt, AbstractValue value ) { - /* In the simple case, the test is the comparison, so isLt = testIsTrue */ - simple_comparison_lt(test, left, right, k) and - value.(BooleanValue).getValue() = isLt - or - complex_lt(test, left, right, k, isLt, value) - or - /* (not (left < right + k)) => (left >= right + k) */ - exists(boolean isGe | isLt = isGe.booleanNot() | compares_ge(test, left, right, k, isGe, value)) - or - /* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */ - exists(AbstractValue dual | value = dual.getDualValue() | - compares_lt(test.(LogicalNotInstruction).getUnary(), left, right, k, isLt, dual) + exists(Instruction derived | derived = getDerivedInstruction(test) | + // exists(thing | thing = derived ... ) + /* In the simple case, the test is the comparison, so isLt = testIsTrue */ + simple_comparison_lt(derived, left, right, k) and + value.(BooleanValue).getValue() = isLt + or + complex_lt(derived, left, right, k, isLt, value) + or + /* (not (left < right + k)) => (left >= right + k) */ + exists(boolean isGe | isLt = isGe.booleanNot() | + compares_ge(derived, left, right, k, isGe, value) + ) + or + /* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */ + exists(AbstractValue dual | value = dual.getDualValue() | + compares_lt(derived.(LogicalNotInstruction).getUnary(), left, right, k, isLt, dual) + ) ) } /** Holds if `op < k` evaluates to `isLt` given that `test` evaluates to `value`. */ private predicate compares_lt(Instruction test, Operand op, int k, boolean isLt, AbstractValue value) { - unary_simple_comparison_lt(test, k, isLt, value) and - op.getDef() = test - or - complex_lt(test, op, k, isLt, value) - or - /* (x is true => (op < k)) => (!x is false => (op < k)) */ - exists(AbstractValue dual | value = dual.getDualValue() | - compares_lt(test.(LogicalNotInstruction).getUnary(), op, k, isLt, dual) - ) - or - exists(int k1, int k2, ConstantInstruction const | - compares_lt(test, op, const.getAUse(), k2, isLt, value) and - int_value(const) = k1 and - k = k1 + k2 + exists(Instruction derived | derived = getDerivedInstruction(test) | + unary_simple_comparison_lt(derived, k, isLt, value) and + op.getDef() = derived + or + complex_lt(derived, op, k, isLt, value) + or + /* (x is true => (op < k)) => (!x is false => (op < k)) */ + exists(AbstractValue dual | value = dual.getDualValue() | + compares_lt(derived.(LogicalNotInstruction).getUnary(), op, k, isLt, dual) + ) + or + exists(int k1, int k2, ConstantInstruction const | + compares_lt(derived, op, const.getAUse(), k2, isLt, value) and + int_value(const) = k1 and + k = k1 + k2 + ) ) } diff --git a/cpp/ql/test/library-tests/controlflow/guards-ir/test.c b/cpp/ql/test/library-tests/controlflow/guards-ir/test.c index 140507237cf4..ee7f7243e183 100644 --- a/cpp/ql/test/library-tests/controlflow/guards-ir/test.c +++ b/cpp/ql/test/library-tests/controlflow/guards-ir/test.c @@ -184,3 +184,14 @@ int abort_test(int x) { abort(); } } + +void boolean_flow(int x){ + int b = x > 5; + + if(!b){ + } + + if(b&&x<100){ + + } +} \ No newline at end of file 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 d097fa7dfa67..51ceef81009c 100644 --- a/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected +++ b/cpp/ql/test/library-tests/controlflow/guards-ir/tests.expected @@ -36,6 +36,11 @@ astGuards | test.c:165:9:165:18 | ... < ... | | test.c:175:13:175:32 | ... == ... | | test.c:181:9:181:9 | x | +| test.c:191:8:191:9 | ! ... | +| test.c:191:9:191:9 | b | +| test.c:194:8:194:8 | b | +| test.c:194:8:194:15 | ... && ... | +| test.c:194:11:194:15 | ... < ... | | test.cpp:18:8:18:10 | call to get | | test.cpp:31:7:31:13 | ... == ... | | test.cpp:42:13:42:20 | call to getABool | @@ -211,6 +216,34 @@ astGuardsCompare | 175 | call to foo == 0+0 when ... == ... is true | | 181 | x != 0 when x is true | | 181 | x == 0 when x is false | +| 191 | 5 < x+0 when ! ... is false | +| 191 | 5 < x+0 when b is true | +| 191 | 5 >= x+0 when ! ... is true | +| 191 | 5 >= x+0 when b is false | +| 191 | ! ... != 0 when ! ... is true | +| 191 | ! ... == 0 when ! ... is false | +| 191 | b != 0 when ! ... is false | +| 191 | b != 0 when b is true | +| 191 | b == 0 when b is false | +| 191 | x < 5+1 when ! ... is true | +| 191 | x < 5+1 when b is false | +| 191 | x >= 5+1 when ! ... is false | +| 191 | x >= 5+1 when b is true | +| 194 | 5 < x+0 when ... && ... is true | +| 194 | 5 < x+0 when b is true | +| 194 | 5 >= x+0 when b is false | +| 194 | 100 < x+1 when ... < ... is false | +| 194 | 100 >= x+1 when ... && ... is true | +| 194 | 100 >= x+1 when ... < ... is true | +| 194 | b != 0 when ... && ... is true | +| 194 | b != 0 when b is true | +| 194 | b == 0 when b is false | +| 194 | x < 5+1 when b is false | +| 194 | x < 100+0 when ... && ... is true | +| 194 | x < 100+0 when ... < ... is true | +| 194 | x >= 5+1 when ... && ... is true | +| 194 | x >= 5+1 when b is true | +| 194 | x >= 100+0 when ... < ... is false | astGuardsControl | test.c:7:9:7:13 | ... > ... | false | 10 | 11 | | test.c:7:9:7:13 | ... > ... | true | 7 | 9 | @@ -306,6 +339,12 @@ astGuardsControl | test.c:181:9:181:9 | x | false | 183 | 184 | | test.c:181:9:181:9 | x | true | 181 | 182 | | test.c:181:9:181:9 | x | true | 186 | 180 | +| test.c:191:8:191:9 | ! ... | true | 191 | 192 | +| test.c:191:9:191:9 | b | false | 191 | 192 | +| test.c:194:8:194:8 | b | true | 194 | 194 | +| test.c:194:8:194:8 | b | true | 194 | 196 | +| test.c:194:8:194:15 | ... && ... | true | 194 | 196 | +| test.c:194:11:194:15 | ... < ... | true | 194 | 196 | | test.cpp:18:8:18:10 | call to get | true | 19 | 19 | | test.cpp:31:7:31:13 | ... == ... | false | 30 | 30 | | test.cpp:31:7:31:13 | ... == ... | false | 34 | 34 | @@ -482,6 +521,20 @@ astGuardsEnsure | test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | == | test.c:175:32:175:32 | 0 | 0 | 175 | 175 | | test.c:175:13:175:32 | ... == ... | test.c:175:32:175:32 | 0 | != | test.c:175:13:175:15 | call to foo | 0 | 175 | 175 | | test.c:175:13:175:32 | ... == ... | test.c:175:32:175:32 | 0 | == | test.c:175:13:175:15 | call to foo | 0 | 175 | 175 | +| test.c:191:8:191:9 | ! ... | test.c:189:13:189:13 | x | < | test.c:189:17:189:17 | 5 | 1 | 191 | 192 | +| test.c:191:8:191:9 | ! ... | test.c:189:17:189:17 | 5 | >= | test.c:189:13:189:13 | x | 0 | 191 | 192 | +| test.c:191:9:191:9 | b | test.c:189:13:189:13 | x | < | test.c:189:17:189:17 | 5 | 1 | 191 | 192 | +| test.c:191:9:191:9 | b | test.c:189:17:189:17 | 5 | >= | test.c:189:13:189:13 | x | 0 | 191 | 192 | +| test.c:194:8:194:8 | b | test.c:189:13:189:13 | x | >= | test.c:189:17:189:17 | 5 | 1 | 194 | 194 | +| test.c:194:8:194:8 | b | test.c:189:13:189:13 | x | >= | test.c:189:17:189:17 | 5 | 1 | 194 | 196 | +| test.c:194:8:194:8 | b | test.c:189:17:189:17 | 5 | < | test.c:189:13:189:13 | x | 0 | 194 | 194 | +| test.c:194:8:194:8 | b | test.c:189:17:189:17 | 5 | < | test.c:189:13:189:13 | x | 0 | 194 | 196 | +| test.c:194:8:194:15 | ... && ... | test.c:189:13:189:13 | x | >= | test.c:189:17:189:17 | 5 | 1 | 194 | 196 | +| test.c:194:8:194:15 | ... && ... | test.c:189:17:189:17 | 5 | < | test.c:189:13:189:13 | x | 0 | 194 | 196 | +| test.c:194:8:194:15 | ... && ... | test.c:194:11:194:11 | x | < | test.c:194:13:194:15 | 100 | 0 | 194 | 196 | +| test.c:194:8:194:15 | ... && ... | test.c:194:13:194:15 | 100 | >= | test.c:194:11:194:11 | x | 1 | 194 | 196 | +| test.c:194:11:194:15 | ... < ... | test.c:194:11:194:11 | x | < | test.c:194:13:194:15 | 100 | 0 | 194 | 196 | +| test.c:194:11:194:15 | ... < ... | test.c:194:13:194:15 | 100 | >= | test.c:194:11:194:11 | x | 1 | 194 | 196 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 34 | 34 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 | @@ -534,6 +587,11 @@ astGuardsEnsure_const | test.c:181:9:181:9 | x | test.c:181:9:181:9 | x | != | 0 | 181 | 182 | | test.c:181:9:181:9 | x | test.c:181:9:181:9 | x | != | 0 | 186 | 180 | | test.c:181:9:181:9 | x | test.c:181:9:181:9 | x | == | 0 | 183 | 184 | +| test.c:191:8:191:9 | ! ... | test.c:191:8:191:9 | ! ... | != | 0 | 191 | 192 | +| test.c:191:9:191:9 | b | test.c:191:9:191:9 | b | == | 0 | 191 | 192 | +| test.c:194:8:194:8 | b | test.c:194:8:194:8 | b | != | 0 | 194 | 194 | +| test.c:194:8:194:8 | b | test.c:194:8:194:8 | b | != | 0 | 194 | 196 | +| test.c:194:8:194:15 | ... && ... | test.c:194:8:194:8 | b | != | 0 | 194 | 196 | | test.cpp:18:8:18:10 | call to get | test.cpp:18:8:18:10 | call to get | != | 0 | 19 | 19 | | 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 | @@ -573,6 +631,10 @@ irGuards | test.c:165:9:165:18 | CompareLT: ... < ... | | test.c:175:13:175:32 | CompareEQ: ... == ... | | test.c:181:9:181:9 | Load: x | +| test.c:191:8:191:9 | LogicalNot: ! ... | +| test.c:191:9:191:9 | Load: b | +| test.c:194:8:194:8 | Load: b | +| test.c:194:11:194:15 | CompareLT: ... < ... | | test.cpp:18:8:18:12 | CompareNE: (bool)... | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | | test.cpp:42:13:42:20 | Call: call to getABool | @@ -746,6 +808,37 @@ irGuardsCompare | 175 | call to foo == 0+0 when CompareEQ: ... == ... is true | | 181 | x != 0 when Load: x is true | | 181 | x == 0 when Load: x is false | +| 191 | 5 < x+0 when Load: b is true | +| 191 | 5 < x+0 when LogicalNot: ! ... is false | +| 191 | 5 >= x+0 when Load: b is false | +| 191 | 5 >= x+0 when LogicalNot: ! ... is true | +| 191 | ! ... != 0 when LogicalNot: ! ... is true | +| 191 | ! ... == 0 when LogicalNot: ! ... is false | +| 191 | b != 0 when Load: b is true | +| 191 | b != 0 when LogicalNot: ! ... is false | +| 191 | b == 0 when Load: b is false | +| 191 | x < 5+1 when Load: b is false | +| 191 | x < 5+1 when LogicalNot: ! ... is true | +| 191 | x < 6 when Load: b is false | +| 191 | x < 6 when LogicalNot: ! ... is true | +| 191 | x >= 5+1 when Load: b is true | +| 191 | x >= 5+1 when LogicalNot: ! ... is false | +| 191 | x >= 6 when Load: b is true | +| 191 | x >= 6 when LogicalNot: ! ... is false | +| 194 | 5 < x+0 when Load: b is true | +| 194 | 5 >= x+0 when Load: b is false | +| 194 | 100 < x+1 when CompareLT: ... < ... is false | +| 194 | 100 >= x+1 when CompareLT: ... < ... is true | +| 194 | b != 0 when Load: b is true | +| 194 | b == 0 when Load: b is false | +| 194 | x < 5+1 when Load: b is false | +| 194 | x < 6 when Load: b is false | +| 194 | x < 100 when CompareLT: ... < ... is true | +| 194 | x < 100+0 when CompareLT: ... < ... is true | +| 194 | x >= 5+1 when Load: b is true | +| 194 | x >= 6 when Load: b is true | +| 194 | x >= 100 when CompareLT: ... < ... is false | +| 194 | x >= 100+0 when CompareLT: ... < ... is false | irGuardsControl | test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 | | test.c:7:9:7:13 | CompareGT: ... > ... | true | 8 | 8 | @@ -833,6 +926,11 @@ irGuardsControl | test.c:175:13:175:32 | CompareEQ: ... == ... | true | 175 | 175 | | test.c:181:9:181:9 | Load: x | false | 184 | 184 | | test.c:181:9:181:9 | Load: x | true | 182 | 182 | +| test.c:191:8:191:9 | LogicalNot: ! ... | true | 191 | 192 | +| test.c:191:9:191:9 | Load: b | false | 191 | 192 | +| test.c:194:8:194:8 | Load: b | true | 194 | 194 | +| test.c:194:8:194:8 | Load: b | true | 194 | 196 | +| test.c:194:11:194:15 | CompareLT: ... < ... | true | 194 | 196 | | test.cpp:18:8:18:12 | CompareNE: (bool)... | true | 19 | 19 | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | false | 34 | 34 | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | true | 30 | 30 | @@ -992,6 +1090,16 @@ irGuardsEnsure | test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | == | test.c:175:32:175:32 | Constant: 0 | 0 | 175 | 175 | | test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:32:175:32 | Constant: 0 | != | test.c:175:13:175:15 | Call: call to foo | 0 | 175 | 175 | | test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:32:175:32 | Constant: 0 | == | test.c:175:13:175:15 | Call: call to foo | 0 | 175 | 175 | +| test.c:191:8:191:9 | LogicalNot: ! ... | test.c:189:13:189:13 | Load: x | < | test.c:189:17:189:17 | Constant: 5 | 1 | 191 | 192 | +| test.c:191:8:191:9 | LogicalNot: ! ... | test.c:189:17:189:17 | Constant: 5 | >= | test.c:189:13:189:13 | Load: x | 0 | 191 | 192 | +| test.c:191:9:191:9 | Load: b | test.c:189:13:189:13 | Load: x | < | test.c:189:17:189:17 | Constant: 5 | 1 | 191 | 192 | +| test.c:191:9:191:9 | Load: b | test.c:189:17:189:17 | Constant: 5 | >= | test.c:189:13:189:13 | Load: x | 0 | 191 | 192 | +| test.c:194:8:194:8 | Load: b | test.c:189:13:189:13 | Load: x | >= | test.c:189:17:189:17 | Constant: 5 | 1 | 194 | 194 | +| test.c:194:8:194:8 | Load: b | test.c:189:13:189:13 | Load: x | >= | test.c:189:17:189:17 | Constant: 5 | 1 | 194 | 196 | +| test.c:194:8:194:8 | Load: b | test.c:189:17:189:17 | Constant: 5 | < | test.c:189:13:189:13 | Load: x | 0 | 194 | 194 | +| test.c:194:8:194:8 | Load: b | test.c:189:17:189:17 | Constant: 5 | < | test.c:189:13:189:13 | Load: x | 0 | 194 | 196 | +| test.c:194:11:194:15 | CompareLT: ... < ... | test.c:194:11:194:11 | Load: x | < | test.c:194:13:194:15 | Constant: 100 | 0 | 194 | 196 | +| test.c:194:11:194:15 | CompareLT: ... < ... | test.c:194:13:194:15 | Constant: 100 | >= | test.c:194:11:194:11 | Load: x | 1 | 194 | 196 | | test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:10 | Call: call to get | != | test.cpp:18:8:18:12 | Constant: (bool)... | 0 | 19 | 19 | | test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:12 | Constant: (bool)... | != | test.cpp:18:8:18:10 | Call: call to get | 0 | 19 | 19 | | test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | test.cpp:31:12:31:13 | Constant: - ... | 0 | 34 | 34 | @@ -1083,6 +1191,15 @@ irGuardsEnsure_const | test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | == | 0 | 175 | 175 | | test.c:181:9:181:9 | Load: x | test.c:181:9:181:9 | Load: x | != | 0 | 182 | 182 | | test.c:181:9:181:9 | Load: x | test.c:181:9:181:9 | Load: x | == | 0 | 184 | 184 | +| test.c:191:8:191:9 | LogicalNot: ! ... | test.c:189:13:189:13 | Load: x | < | 6 | 191 | 192 | +| test.c:191:8:191:9 | LogicalNot: ! ... | test.c:191:8:191:9 | LogicalNot: ! ... | != | 0 | 191 | 192 | +| test.c:191:9:191:9 | Load: b | test.c:189:13:189:13 | Load: x | < | 6 | 191 | 192 | +| test.c:191:9:191:9 | Load: b | test.c:191:9:191:9 | Load: b | == | 0 | 191 | 192 | +| test.c:194:8:194:8 | Load: b | test.c:189:13:189:13 | Load: x | >= | 6 | 194 | 194 | +| test.c:194:8:194:8 | Load: b | test.c:189:13:189:13 | Load: x | >= | 6 | 194 | 196 | +| test.c:194:8:194:8 | Load: b | test.c:194:8:194:8 | Load: b | != | 0 | 194 | 194 | +| test.c:194:8:194:8 | Load: b | test.c:194:8:194:8 | Load: b | != | 0 | 194 | 196 | +| test.c:194:11:194:15 | CompareLT: ... < ... | test.c:194:11:194:11 | Load: x | < | 100 | 194 | 196 | | test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:10 | Call: call to get | != | 0 | 19 | 19 | | 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 | diff --git a/cpp/ql/test/library-tests/controlflow/guards/Guards.expected b/cpp/ql/test/library-tests/controlflow/guards/Guards.expected index 757356c247cd..4f86180ec7f0 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/Guards.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/Guards.expected @@ -32,6 +32,11 @@ | test.c:164:8:164:8 | s | | test.c:170:8:170:9 | ! ... | | test.c:170:9:170:9 | s | +| test.c:178:8:178:9 | ! ... | +| test.c:178:9:178:9 | b | +| test.c:181:8:181:8 | b | +| test.c:181:8:181:15 | ... && ... | +| test.c:181:11:181:15 | ... < ... | | test.cpp:18:8:18:10 | call to get | | test.cpp:31:7:31:13 | ... == ... | | test.cpp:42:13:42:20 | call to getABool | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected index 8480a1f86138..ae2b4a10e680 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsCompare.expected @@ -223,3 +223,41 @@ | 170 | s != 0 when ! ... is false | | 170 | s != 0 when s is true | | 170 | s == 0 when s is false | +| 178 | 5 < x+0 when ! ... is false | +| 178 | 5 < x+0 when b is true | +| 178 | 5 >= x+0 when ! ... is true | +| 178 | 5 >= x+0 when b is false | +| 178 | ! ... != 0 when ! ... is true | +| 178 | ! ... == 0 when ! ... is false | +| 178 | b != 0 when ! ... is false | +| 178 | b != 0 when b is true | +| 178 | b == 0 when b is false | +| 178 | x < 5+1 when ! ... is true | +| 178 | x < 5+1 when b is false | +| 178 | x < 6 when ! ... is true | +| 178 | x < 6 when b is false | +| 178 | x >= 5+1 when ! ... is false | +| 178 | x >= 5+1 when b is true | +| 178 | x >= 6 when ! ... is false | +| 178 | x >= 6 when b is true | +| 181 | 5 < x+0 when ... && ... is true | +| 181 | 5 < x+0 when b is true | +| 181 | 5 >= x+0 when b is false | +| 181 | 100 < x+1 when ... < ... is false | +| 181 | 100 >= x+1 when ... && ... is true | +| 181 | 100 >= x+1 when ... < ... is true | +| 181 | b != 0 when ... && ... is true | +| 181 | b != 0 when b is true | +| 181 | b == 0 when b is false | +| 181 | x < 5+1 when b is false | +| 181 | x < 6 when b is false | +| 181 | x < 100 when ... && ... is true | +| 181 | x < 100 when ... < ... is true | +| 181 | x < 100+0 when ... && ... is true | +| 181 | x < 100+0 when ... < ... is true | +| 181 | x >= 5+1 when ... && ... is true | +| 181 | x >= 5+1 when b is true | +| 181 | x >= 6 when ... && ... is true | +| 181 | x >= 6 when b is true | +| 181 | x >= 100 when ... < ... is false | +| 181 | x >= 100+0 when ... < ... is false | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected index 83275c8011f9..865df598da21 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsControl.expected @@ -85,6 +85,12 @@ | test.c:164:8:164:8 | s | true | 164 | 166 | | test.c:170:8:170:9 | ! ... | true | 170 | 172 | | test.c:170:9:170:9 | s | false | 170 | 172 | +| test.c:178:8:178:9 | ! ... | true | 178 | 179 | +| test.c:178:9:178:9 | b | false | 178 | 179 | +| test.c:181:8:181:8 | b | true | 181 | 181 | +| test.c:181:8:181:8 | b | true | 181 | 183 | +| test.c:181:8:181:15 | ... && ... | true | 181 | 183 | +| test.c:181:11:181:15 | ... < ... | true | 181 | 183 | | test.cpp:18:8:18:10 | call to get | true | 19 | 19 | | test.cpp:31:7:31:13 | ... == ... | false | 30 | 30 | | test.cpp:31:7:31:13 | ... == ... | false | 34 | 34 | diff --git a/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected b/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected index c520b48f94e4..3c2af3b88bff 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected +++ b/cpp/ql/test/library-tests/controlflow/guards/GuardsEnsure.expected @@ -147,6 +147,20 @@ binary | test.c:109:9:109:23 | ... \|\| ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 | | test.c:109:19:109:23 | ... < ... | test.c:109:19:109:19 | y | >= | test.c:109:23:109:23 | 0 | 0 | 113 | 113 | | test.c:109:19:109:23 | ... < ... | test.c:109:23:109:23 | 0 | < | test.c:109:19:109:19 | y | 1 | 113 | 113 | +| test.c:178:8:178:9 | ! ... | test.c:176:13:176:13 | x | < | test.c:176:17:176:17 | 5 | 1 | 178 | 179 | +| test.c:178:8:178:9 | ! ... | test.c:176:17:176:17 | 5 | >= | test.c:176:13:176:13 | x | 0 | 178 | 179 | +| test.c:178:9:178:9 | b | test.c:176:13:176:13 | x | < | test.c:176:17:176:17 | 5 | 1 | 178 | 179 | +| test.c:178:9:178:9 | b | test.c:176:17:176:17 | 5 | >= | test.c:176:13:176:13 | x | 0 | 178 | 179 | +| test.c:181:8:181:8 | b | test.c:176:13:176:13 | x | >= | test.c:176:17:176:17 | 5 | 1 | 181 | 181 | +| test.c:181:8:181:8 | b | test.c:176:13:176:13 | x | >= | test.c:176:17:176:17 | 5 | 1 | 181 | 183 | +| test.c:181:8:181:8 | b | test.c:176:17:176:17 | 5 | < | test.c:176:13:176:13 | x | 0 | 181 | 181 | +| test.c:181:8:181:8 | b | test.c:176:17:176:17 | 5 | < | test.c:176:13:176:13 | x | 0 | 181 | 183 | +| test.c:181:8:181:15 | ... && ... | test.c:176:13:176:13 | x | >= | test.c:176:17:176:17 | 5 | 1 | 181 | 183 | +| test.c:181:8:181:15 | ... && ... | test.c:176:17:176:17 | 5 | < | test.c:176:13:176:13 | x | 0 | 181 | 183 | +| test.c:181:8:181:15 | ... && ... | test.c:181:11:181:11 | x | < | test.c:181:13:181:15 | 100 | 0 | 181 | 183 | +| test.c:181:8:181:15 | ... && ... | test.c:181:13:181:15 | 100 | >= | test.c:181:11:181:11 | x | 1 | 181 | 183 | +| test.c:181:11:181:15 | ... < ... | test.c:181:11:181:11 | x | < | test.c:181:13:181:15 | 100 | 0 | 181 | 183 | +| test.c:181:11:181:15 | ... < ... | test.c:181:13:181:15 | 100 | >= | test.c:181:11:181:11 | x | 1 | 181 | 183 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | test.cpp:31:12:31:13 | - ... | 0 | 34 | 34 | | test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | test.cpp:31:12:31:13 | - ... | 0 | 30 | 30 | @@ -264,6 +278,18 @@ unary | test.c:164:8:164:8 | s | test.c:164:8:164:8 | s | != | 0 | 164 | 166 | | test.c:170:8:170:9 | ! ... | test.c:170:8:170:9 | ! ... | != | 0 | 170 | 172 | | test.c:170:9:170:9 | s | test.c:170:9:170:9 | s | == | 0 | 170 | 172 | +| test.c:178:8:178:9 | ! ... | test.c:176:13:176:13 | x | < | 6 | 178 | 179 | +| test.c:178:8:178:9 | ! ... | test.c:178:8:178:9 | ! ... | != | 0 | 178 | 179 | +| test.c:178:9:178:9 | b | test.c:176:13:176:13 | x | < | 6 | 178 | 179 | +| test.c:178:9:178:9 | b | test.c:178:9:178:9 | b | == | 0 | 178 | 179 | +| test.c:181:8:181:8 | b | test.c:176:13:176:13 | x | >= | 6 | 181 | 181 | +| test.c:181:8:181:8 | b | test.c:176:13:176:13 | x | >= | 6 | 181 | 183 | +| test.c:181:8:181:8 | b | test.c:181:8:181:8 | b | != | 0 | 181 | 181 | +| test.c:181:8:181:8 | b | test.c:181:8:181:8 | b | != | 0 | 181 | 183 | +| test.c:181:8:181:15 | ... && ... | test.c:176:13:176:13 | x | >= | 6 | 181 | 183 | +| test.c:181:8:181:15 | ... && ... | test.c:181:8:181:8 | b | != | 0 | 181 | 183 | +| test.c:181:8:181:15 | ... && ... | test.c:181:11:181:11 | x | < | 100 | 181 | 183 | +| test.c:181:11:181:15 | ... < ... | test.c:181:11:181:11 | x | < | 100 | 181 | 183 | | test.cpp:18:8:18:10 | call to get | test.cpp:18:8:18:10 | call to get | != | 0 | 19 | 19 | | 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 | diff --git a/cpp/ql/test/library-tests/controlflow/guards/test.c b/cpp/ql/test/library-tests/controlflow/guards/test.c index 207e23baa0e3..9fb226bbf59f 100644 --- a/cpp/ql/test/library-tests/controlflow/guards/test.c +++ b/cpp/ql/test/library-tests/controlflow/guards/test.c @@ -169,5 +169,16 @@ void test8(short s) { void test9(short s) { if(!s) { + } +} + +void boolean_flow(int x){ + int b = x > 5; + + if(!b){ + } + + if(b&&x<100){ + } } \ No newline at end of file diff --git a/cpp/ql/test/query-tests/Critical/MissingCheckScanf/MissingCheckScanf.expected b/cpp/ql/test/query-tests/Critical/MissingCheckScanf/MissingCheckScanf.expected index dac8afd3fd31..09a24003d729 100644 --- a/cpp/ql/test/query-tests/Critical/MissingCheckScanf/MissingCheckScanf.expected +++ b/cpp/ql/test/query-tests/Critical/MissingCheckScanf/MissingCheckScanf.expected @@ -52,6 +52,9 @@ edges | test.cpp:541:39:541:40 | sscanf output argument | test.cpp:549:8:549:8 | e | provenance | | | test.cpp:541:43:541:44 | sscanf output argument | test.cpp:545:8:545:8 | f | provenance | | | test.cpp:541:43:541:44 | sscanf output argument | test.cpp:550:8:550:8 | f | provenance | | +| test.cpp:560:30:560:31 | scanf output argument | test.cpp:562:9:562:9 | i | provenance | | +| test.cpp:568:35:568:36 | scanf output argument | test.cpp:570:9:570:9 | i | provenance | | +| test.cpp:576:30:576:31 | scanf output argument | test.cpp:578:9:578:9 | i | provenance | | nodes | test.cpp:34:15:34:16 | scanf output argument | semmle.label | scanf output argument | | test.cpp:35:7:35:7 | i | semmle.label | i | @@ -154,6 +157,12 @@ nodes | test.cpp:548:8:548:8 | d | semmle.label | d | | test.cpp:549:8:549:8 | e | semmle.label | e | | test.cpp:550:8:550:8 | f | semmle.label | f | +| test.cpp:560:30:560:31 | scanf output argument | semmle.label | scanf output argument | +| test.cpp:562:9:562:9 | i | semmle.label | i | +| test.cpp:568:35:568:36 | scanf output argument | semmle.label | scanf output argument | +| test.cpp:570:9:570:9 | i | semmle.label | i | +| test.cpp:576:30:576:31 | scanf output argument | semmle.label | scanf output argument | +| test.cpp:578:9:578:9 | i | semmle.label | i | subpaths #select | test.cpp:35:7:35:7 | i | test.cpp:34:15:34:16 | scanf output argument | test.cpp:35:7:35:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:34:3:34:7 | call to scanf | call to scanf | @@ -177,3 +186,5 @@ subpaths | test.cpp:484:9:484:9 | i | test.cpp:480:25:480:26 | scanf output argument | test.cpp:484:9:484:9 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:480:13:480:17 | call to scanf | call to scanf | | test.cpp:495:8:495:8 | i | test.cpp:491:25:491:26 | scanf output argument | test.cpp:495:8:495:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:491:13:491:17 | call to scanf | call to scanf | | test.cpp:545:8:545:8 | f | test.cpp:541:43:541:44 | sscanf output argument | test.cpp:545:8:545:8 | f | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 3. | test.cpp:541:10:541:15 | call to sscanf | call to sscanf | +| test.cpp:570:9:570:9 | i | test.cpp:568:35:568:36 | scanf output argument | test.cpp:570:9:570:9 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:568:23:568:27 | call to scanf | call to scanf | +| test.cpp:578:9:578:9 | i | test.cpp:576:30:576:31 | scanf output argument | test.cpp:578:9:578:9 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:576:18:576:22 | call to scanf | call to scanf | diff --git a/cpp/ql/test/query-tests/Critical/MissingCheckScanf/test.cpp b/cpp/ql/test/query-tests/Critical/MissingCheckScanf/test.cpp index efc37060a554..9338bb84a5e5 100644 --- a/cpp/ql/test/query-tests/Critical/MissingCheckScanf/test.cpp +++ b/cpp/ql/test/query-tests/Critical/MissingCheckScanf/test.cpp @@ -553,3 +553,28 @@ void switch_cases(const char *data) { break; } } + + +void test_scanf_compared_right_away() { + int i; + bool success = scanf("%d", &i) == 1; + if(success) { + use(i); // GOOD + } +} + +void test_scanf_compared_in_conjunct_right(bool b) { + int i; + bool success = b && scanf("%d", &i) == 1; + if(success) { + use(i); // GOOD [FALSE POSITIVE] + } +} + +void test_scanf_compared_in_conjunct_left(bool b) { + int i; + bool success = scanf("%d", &i) == 1 && b; + if(success) { + use(i); // GOOD [FALSE POSITIVE] + } +} \ No newline at end of file