Skip to content
Closed
56 changes: 48 additions & 8 deletions cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,48 @@ 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) {
// The instruction itself
result = instr
or
// or the GVN definition for the current instruction
result = valueNumber(instr).getAnInstruction().(StoreInstruction).getSourceValue()
or
// Special handling for conversions
result =
getDerivedInstruction(valueNumber(instr).getAnInstruction().(CompareNEInstruction).getLeft())
result = derivedThroughConversion(valueNumber(instr))
}

private Instruction derivedThroughConversion(ValueNumber vn) {
// GVN for the ConvertInstruction conversion
result = getDerivedInstruction(vn.getAnInstruction().(ConvertInstruction).getUnary())
or
result =
getDerivedInstruction(valueNumber(instr).getAnInstruction().(ConvertInstruction).getUnary())
// 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
)
}

/**
Expand Down Expand Up @@ -832,7 +858,7 @@ private predicate unary_compares_eq(
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() = test
unary_simple_comparison_eq(derived, k, inNonZeroCase, v) and op.getDef() = derived
|
areEqual = true and value = v
or
Expand Down Expand Up @@ -897,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
Comment on lines +926 to +936
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to do the pruning like this. We really want to avoid depending on the specific syntax this deep inside the libraries.

And as you say yourself: This also still misses some cases. Once this is done properly and completely, we can avoid these ad-hoc conjuncts below:

  not test.isGLValue() and
  not simple_comparison_eq(test, _, _, _, _) and
  not simple_comparison_lt(test, _, _, _) and
  not test = any(SwitchInstruction switch).getExpression() and
  (
    test.getResultIRType() instanceof IRAddressType or
    test.getResultIRType() instanceof IRIntegerType or
    test.getResultIRType() instanceof IRBooleanType
  )

) 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
Expand Down Expand Up @@ -941,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`. */
Expand Down