Skip to content

Commit 44a54cc

Browse files
committed
C++: Properly restrict 'unary_simple_comparison_eq'.
1 parent f68d7a4 commit 44a54cc

File tree

1 file changed

+55
-37
lines changed

1 file changed

+55
-37
lines changed

cpp/ql/lib/semmle/code/cpp/controlflow/IRGuards.qll

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,55 @@ private predicate simple_comparison_eq(
924924
value.(BooleanValue).getValue() = false
925925
}
926926

927+
/**
928+
* Holds if `op` is an operand that is eventually used in a unary comparison
929+
* with a constant.
930+
*/
931+
private predicate isRelevantUnaryComparisonOperand(Operand op) {
932+
// Base case: `op` is an operand of a `CompareEQInstruction` or `CompareNEInstruction`,
933+
// and the other operand is a constant.
934+
exists(CompareInstruction eq, Instruction instr |
935+
eq.hasOperands(op, instr.getAUse()) and
936+
exists(int_value(instr))
937+
|
938+
eq instanceof CompareEQInstruction
939+
or
940+
eq instanceof CompareNEInstruction
941+
)
942+
or
943+
// C doesn't have int-to-bool conversions, so `if(x)` will just generate:
944+
// r2_1(glval<int>) = VariableAddress[x]
945+
// r2_2(int) = Load[x] : &:r2_1, m1_6
946+
// v2_3(void) = ConditionalBranch : r2_2
947+
exists(ConditionalBranchInstruction branch | branch.getConditionOperand() = op)
948+
or
949+
// If `!x` is a relevant unary comparison then so is `x`.
950+
exists(LogicalNotInstruction logicalNot |
951+
isRelevantUnaryComparisonOperand(unique( | | logicalNot.getAUse())) and
952+
logicalNot.getUnaryOperand() = op
953+
)
954+
or
955+
// If `y` is a relevant unary comparison and `y = x` then so is `x`.
956+
not op.isDefinitionInexact() and
957+
exists(CopyInstruction copy |
958+
isRelevantUnaryComparisonOperand(unique( | | copy.getAUse())) and
959+
op = copy.getSourceValueOperand()
960+
)
961+
or
962+
// If phi(x1, x2) is a relevant unary comparison then so is `x1` and `x2`.
963+
not op.isDefinitionInexact() and
964+
exists(PhiInstruction phi |
965+
isRelevantUnaryComparisonOperand(unique( | | phi.getAUse())) and
966+
op = phi.getAnInputOperand()
967+
)
968+
or
969+
// If `__builtin_expect(x)` is a relevant unary comparison then so is `x`.
970+
exists(BuiltinExpectCallInstruction call |
971+
isRelevantUnaryComparisonOperand(unique( | | call.getAUse())) and
972+
op = call.getConditionOperand()
973+
)
974+
}
975+
927976
/** Rearrange various simple comparisons into `op == k` form. */
928977
private predicate unary_simple_comparison_eq(
929978
ValueNumber test, Operand op, int k, boolean inNonZeroCase, AbstractValue value
@@ -937,41 +986,8 @@ private predicate unary_simple_comparison_eq(
937986
inNonZeroCase = false
938987
)
939988
or
940-
// Any instruction with an integral type could potentially be part of a
941-
// check for nullness when used in a guard. So we include all integral
942-
// typed instructions here. However, since some of these instructions are
943-
// already included as guards in other cases, we exclude those here.
944-
// These are instructions that compute a binary equality or inequality
945-
// relation. For example, the following:
946-
// ```cpp
947-
// if(a == b + 42) { ... }
948-
// ```
949-
// generates the following IR:
950-
// ```
951-
// r1(glval<int>) = VariableAddress[a] :
952-
// r2(int) = Load[a] : &:r1, m1
953-
// r3(glval<int>) = VariableAddress[b] :
954-
// r4(int) = Load[b] : &:r3, m2
955-
// r5(int) = Constant[42] :
956-
// r6(int) = Add : r4, r5
957-
// r7(bool) = CompareEQ : r2, r6
958-
// v1(void) = ConditionalBranch : r7
959-
// ```
960-
// and since `r7` is an integral typed instruction this predicate could
961-
// include a case for when `r7` evaluates to true (in which case we would
962-
// infer that `r6` was non-zero, and a case for when `r7` evaluates to false
963-
// (in which case we would infer that `r6` was zero).
964-
// However, since `a == b + 42` is already supported when reasoning about
965-
// binary equalities we exclude those cases here.
966-
not test.isGLValue() and
967-
not simple_comparison_eq(test, _, _, _, _) and
968-
not simple_comparison_lt(test, _, _, _) and
969-
op = test.getExpressionOperand() and
970-
(
971-
test.getResultIRType() instanceof IRAddressType or
972-
test.getResultIRType() instanceof IRIntegerType or
973-
test.getResultIRType() instanceof IRBooleanType
974-
) and
989+
isRelevantUnaryComparisonOperand(op) and
990+
op.getDef() = test.getAnInstruction() and
975991
(
976992
k = 1 and
977993
value.(BooleanValue).getValue() = true and
@@ -988,10 +1004,12 @@ private class BuiltinExpectCallInstruction extends CallInstruction {
9881004
BuiltinExpectCallInstruction() { this.getStaticCallTarget().hasName("__builtin_expect") }
9891005

9901006
/** Gets the condition of this call. */
991-
Instruction getCondition() {
1007+
Instruction getCondition() { result = this.getConditionOperand().getDef() }
1008+
1009+
Operand getConditionOperand() {
9921010
// The first parameter of `__builtin_expect` has type `long`. So we skip
9931011
// the conversion when inferring guards.
994-
result = this.getArgument(0).(ConvertInstruction).getUnary()
1012+
result = this.getArgument(0).(ConvertInstruction).getUnaryOperand()
9951013
}
9961014
}
9971015

0 commit comments

Comments
 (0)