Skip to content

Commit 442968c

Browse files
committed
C++: Properly restrict 'unary_simple_comparison_eq'.
1 parent db38069 commit 442968c

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
@@ -923,6 +923,55 @@ private predicate simple_comparison_eq(
923923
value.(BooleanValue).getValue() = false
924924
}
925925

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

9891005
/** Gets the condition of this call. */
990-
Instruction getCondition() {
1006+
Instruction getCondition() { result = this.getConditionOperand().getDef() }
1007+
1008+
Operand getConditionOperand() {
9911009
// The first parameter of `__builtin_expect` has type `long`. So we skip
9921010
// the conversion when inferring guards.
993-
result = this.getArgument(0).(ConvertInstruction).getUnary()
1011+
result = this.getArgument(0).(ConvertInstruction).getUnaryOperand()
9941012
}
9951013
}
9961014

0 commit comments

Comments
 (0)