Skip to content

Commit

Permalink
C++: Support 'if(!p)' for C programs in IRGuards.
Browse files Browse the repository at this point in the history
  • Loading branch information
MathiasVP committed May 20, 2024
1 parent 398b90a commit a722283
Showing 1 changed file with 92 additions and 36 deletions.
128 changes: 92 additions & 36 deletions cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ class IRGuardCondition extends Instruction {
/** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `value`. */
cached
predicate comparesEq(Operand op, int k, boolean areEqual, AbstractValue value) {
unary_compares_eq(this, op, k, areEqual, value)
unary_compares_eq(this, op, k, areEqual, false, value)
}

/**
Expand All @@ -586,7 +586,7 @@ 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, value) and this.valueControls(block, value)
unary_compares_eq(this, op, k, areEqual, false, value) and this.valueControls(block, value)
)
}

Expand All @@ -611,7 +611,7 @@ class IRGuardCondition extends Instruction {
cached
predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) {
exists(AbstractValue value |
unary_compares_eq(this, op, k, areEqual, value) and
unary_compares_eq(this, op, k, areEqual, false, value) and
this.valueControlsEdge(pred, succ, value)
)
}
Expand Down Expand Up @@ -739,24 +739,30 @@ private predicate compares_eq(

/** Holds if `op == k` is `areEqual` given that `test` is equal to `value`. */
private predicate unary_compares_eq(
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
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, op, k, v) |
exists(AbstractValue v | unary_simple_comparison_eq(test, op, k, inNonZeroCase, v) |
areEqual = true and value = v
or
areEqual = false and value = v.getDualValue()
)
or
unary_complex_eq(test, op, k, areEqual, value)
unary_complex_eq(test, op, k, areEqual, inNonZeroCase, value)
or
/* (x is true => (op == k)) => (!x is false => (op == k)) */
exists(AbstractValue dual | value = dual.getDualValue() |
unary_compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, areEqual, dual)
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
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
Expand All @@ -781,37 +787,84 @@ private predicate simple_comparison_eq(
value.(BooleanValue).getValue() = false
}

/** Rearrange various simple comparisons into `op == k` form. */
/**
* Holds if `test` is an instruction that is part of test that eventually is
* used in a conditional branch.
*/
private predicate relevantUnaryComparison(Instruction test) {
not test instanceof CompareInstruction and
exists(IRType type, ConditionalBranchInstruction branch |
type instanceof IRAddressType or type instanceof IRIntegerType
|
type = test.getResultIRType() and
branch.getCondition() = test
)
or
exists(LogicalNotInstruction logicalNot |
relevantUnaryComparison(logicalNot) and
test = logicalNot.getUnary()
)
}

/**
* Rearrange various simple comparisons into `op == k` form.
*
* The boolean `inNonZeroCase` tracks whether the value of `k` is restricted
* to the range `[0, 1]`.
*
* If `inNonZeroCase` is `true`, then any non-zero value of `k` should be
* interpreted as "we only know that `k` is non-zero".
*
* Ideally, this would be represented as an `Option<int>` type, but QL does
* not yet allow infinite IPA branches (which is what `Option` uses under the
* hood).
*
* To see why `inNonZeroCase` is needed consider the following C program:
* ```c
* char* p = ...;
* if(!p) {
* use(p);
* }
* ```
* in C++ there would be an int-to-bool conversion on `p` before it is
* passed to `!`. However, since C does not have booleans there is no
* int-to-bool conversion. We should be able to conclude that `p` is non-zero
* in the true branch, so we need to give `k` some value. However, simply
* setting `k = 1` would make the rest of the analysis think that `k == 1`
* holds inside the branch. So we distinquish between the above case and
* ```c
* if(p == 1) {
* use(p)
* }
* ```
* by setting `inNonZeroCase` to `true` in the former case, but not in the
* latter.
*/
private predicate unary_simple_comparison_eq(
Instruction test, Operand op, int k, AbstractValue value
Instruction test, Operand op, int k, boolean inNonZeroCase, 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
case.getValue().toInt() = k and
inNonZeroCase = false
)
or
// There's no implicit CompareInstruction in files compiled as C since C
// doesn't have implicit boolean conversions. So instead we check whether
// there's a branch on a value of pointer or integer type.
exists(ConditionalBranchInstruction branch, IRType type |
not test instanceof CompareInstruction and
type = test.getResultIRType() and
(type instanceof IRAddressType or type instanceof IRIntegerType) and
test = branch.getCondition() and
op.getDef() = test
|
// We'd like to also include a case such as:
// ```
// k = 1 and
// value.(BooleanValue).getValue() = true
// ```
// but all we know is that the value is non-zero in the true branch.
// So we can only conclude something in the false branch.
relevantUnaryComparison(test) and
op.getDef() = test and
(
k = 1 and
value.(BooleanValue).getValue() = true and
inNonZeroCase = true
or
k = 0 and
value.(BooleanValue).getValue() = false
value.(BooleanValue).getValue() = false and
inNonZeroCase = false
)
}

Expand All @@ -824,11 +877,11 @@ private predicate complex_eq(
}

private predicate unary_complex_eq(
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
) {
unary_sub_eq(test, op, k, areEqual, value)
unary_sub_eq(test, op, k, areEqual, inNonZeroCase, value)
or
unary_add_eq(test, op, k, areEqual, value)
unary_add_eq(test, op, k, areEqual, inNonZeroCase, value)
}

/*
Expand Down Expand Up @@ -1093,17 +1146,19 @@ private predicate sub_eq(

// op - x == c => op == (c+x)
private predicate unary_sub_eq(
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
) {
inNonZeroCase = false and
exists(SubInstruction sub, int c, int x |
unary_compares_eq(test, sub.getAUse(), c, areEqual, value) and
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
op = sub.getLeftOperand() and
x = int_value(sub.getRight()) and
k = c + x
)
or
inNonZeroCase = false and
exists(PointerSubInstruction sub, int c, int x |
unary_compares_eq(test, sub.getAUse(), c, areEqual, value) and
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
op = sub.getLeftOperand() and
x = int_value(sub.getRight()) and
k = c + x
Expand Down Expand Up @@ -1158,11 +1213,12 @@ private predicate add_eq(

// left + x == right + c => left == right + (c-x)
private predicate unary_add_eq(
Instruction test, Operand left, int k, boolean areEqual,
Instruction test, Operand left, int k, boolean areEqual, boolean inNonZeroCase,
AbstractValue value
) {
inNonZeroCase = false and
exists(AddInstruction lhs, int c, int x |
unary_compares_eq(test, lhs.getAUse(), c, areEqual, value) and
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
Expand All @@ -1171,9 +1227,9 @@ private predicate unary_add_eq(
k = c - x
)
or
inNonZeroCase = false and
exists(PointerAddInstruction lhs, int c, int x |

unary_compares_eq(test, lhs.getAUse(), c, areEqual, value) and
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
(
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
or
Expand Down

0 comments on commit a722283

Please sign in to comment.