Skip to content

Commit b3029ec

Browse files
committed
C++: Properly restrict 'unary_simple_comparison_eq'.
1 parent 7ea8cb0 commit b3029ec

File tree

1 file changed

+49
-37
lines changed

1 file changed

+49
-37
lines changed

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

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

927+
private predicate isRelevantUnaryComparisonOperand(Operand op) {
928+
exists(CompareInstruction eq, Instruction instr |
929+
eq.hasOperands(op, instr.getAUse()) and
930+
exists(int_value(instr))
931+
|
932+
eq instanceof CompareEQInstruction
933+
or
934+
eq instanceof CompareNEInstruction
935+
)
936+
or
937+
// C doesn't have int-to-bool conversions, so `if(x)` will just generate:
938+
// r2_1(glval<int>) = VariableAddress[x]
939+
// r2_2(int) = Load[x] : &:r2_1, m1_6
940+
// v2_3(void) = ConditionalBranch : r2_2
941+
exists(ConditionalBranchInstruction branch | branch.getConditionOperand() = op)
942+
or
943+
// If `!x` is a relevant unary comparison then so is `x`.
944+
exists(LogicalNotInstruction logicalNot |
945+
isRelevantUnaryComparisonOperand(unique( | | logicalNot.getAUse())) and
946+
logicalNot.getUnaryOperand() = op
947+
)
948+
or
949+
// If `y` is a relevant unary comparison and `y = x` then so is `x`.
950+
not op.isDefinitionInexact() and
951+
exists(CopyInstruction copy |
952+
isRelevantUnaryComparisonOperand(unique( | | copy.getAUse())) and
953+
op = copy.getSourceValueOperand()
954+
)
955+
or
956+
// If phi(x1, x2) is a relevant unary comparison then so is `x1` and `x2`.
957+
not op.isDefinitionInexact() and
958+
exists(PhiInstruction phi |
959+
isRelevantUnaryComparisonOperand(unique( | | phi.getAUse())) and
960+
op = phi.getAnInputOperand()
961+
)
962+
or
963+
// If `__builtin_expect(x)` is a relevant unary comparison then so is `x`.
964+
exists(BuiltinExpectCallInstruction call |
965+
isRelevantUnaryComparisonOperand(unique( | | call.getAUse())) and
966+
op = call.getConditionOperand()
967+
)
968+
}
969+
927970
/** Rearrange various simple comparisons into `op == k` form. */
928971
private predicate unary_simple_comparison_eq(
929972
ValueNumber test, Operand op, int k, boolean inNonZeroCase, AbstractValue value
@@ -937,41 +980,8 @@ private predicate unary_simple_comparison_eq(
937980
inNonZeroCase = false
938981
)
939982
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
983+
isRelevantUnaryComparisonOperand(op) and
984+
op.getDef() = test.getAnInstruction() and
975985
(
976986
k = 1 and
977987
value.(BooleanValue).getValue() = true and
@@ -988,10 +998,12 @@ private class BuiltinExpectCallInstruction extends CallInstruction {
988998
BuiltinExpectCallInstruction() { this.getStaticCallTarget().hasName("__builtin_expect") }
989999

9901000
/** Gets the condition of this call. */
991-
Instruction getCondition() {
1001+
Instruction getCondition() { result = this.getConditionOperand().getDef() }
1002+
1003+
Operand getConditionOperand() {
9921004
// The first parameter of `__builtin_expect` has type `long`. So we skip
9931005
// the conversion when inferring guards.
994-
result = this.getArgument(0).(ConvertInstruction).getUnary()
1006+
result = this.getArgument(0).(ConvertInstruction).getUnaryOperand()
9951007
}
9961008
}
9971009

0 commit comments

Comments
 (0)