Skip to content

Commit e6cd27a

Browse files
committed
C++: Skip non-Boolean instructions in the new inference step.
1 parent e67b6d6 commit e6cd27a

File tree

5 files changed

+329
-20
lines changed

5 files changed

+329
-20
lines changed

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

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,77 @@ private module Cached {
936936
ValueNumber getUnary() { result.getAnInstruction() = instr.getUnary() }
937937
}
938938

939+
signature predicate sinkSig(Instruction instr);
940+
941+
private module BooleanInstruction<sinkSig/1 isSink> {
942+
/**
943+
* Holds if `i1` flows to `i2` in a single step and `i2` is not an
944+
* instruction that produces a value of Boolean type.
945+
*/
946+
private predicate stepToNonBoolean(Instruction i1, Instruction i2) {
947+
not i2.getResultIRType() instanceof IRBooleanType and
948+
(
949+
i2.(CopyInstruction).getSourceValue() = i1
950+
or
951+
i2.(ConvertInstruction).getUnary() = i1
952+
or
953+
i2.(BuiltinExpectCallInstruction).getArgument(0) = i1
954+
)
955+
}
956+
957+
private predicate rev(Instruction instr) {
958+
isSink(instr)
959+
or
960+
exists(Instruction instr1 |
961+
rev(instr1) and
962+
stepToNonBoolean(instr, instr1)
963+
)
964+
}
965+
966+
private predicate hasBooleanType(Instruction instr) {
967+
instr.getResultIRType() instanceof IRBooleanType
968+
}
969+
970+
private predicate fwd(Instruction instr) {
971+
rev(instr) and
972+
(
973+
hasBooleanType(instr)
974+
or
975+
exists(Instruction instr0 |
976+
fwd(instr0) and
977+
stepToNonBoolean(instr0, instr)
978+
)
979+
)
980+
}
981+
982+
private predicate prunedStep(Instruction i1, Instruction i2) {
983+
fwd(i1) and
984+
fwd(i2) and
985+
stepToNonBoolean(i1, i2)
986+
}
987+
988+
private predicate stepsPlus(Instruction i1, Instruction i2) =
989+
doublyBoundedFastTC(prunedStep/2, hasBooleanType/1, isSink/1)(i1, i2)
990+
991+
/**
992+
* Gets the Boolean-typed instruction that defines `instr` before any
993+
* integer conversions are applied, if any.
994+
*/
995+
Instruction get(Instruction instr) {
996+
isSink(instr) and
997+
(
998+
result = instr
999+
or
1000+
stepsPlus(result, instr)
1001+
) and
1002+
hasBooleanType(result)
1003+
}
1004+
}
1005+
1006+
private predicate isUnaryComparesEqLeft(Instruction instr) {
1007+
unary_compares_eq(_, instr.getAUse(), 0, _, _)
1008+
}
1009+
9391010
/**
9401011
* Holds if `left == right + k` is `areEqual` given that test is `testIsTrue`.
9411012
*
@@ -971,7 +1042,8 @@ private module Cached {
9711042
// 1. test = value -> int(l) = 0 is !bv
9721043
unary_compares_eq(test, l, 0, bv.getValue().booleanNot(), value) and
9731044
// 2. l = bv -> left + right is areEqual
974-
compares_eq(valueNumberOfOperand(l), left, right, k, areEqual, bv)
1045+
compares_eq(valueNumber(BooleanInstruction<isUnaryComparesEqLeft/1>::get(l.getDef())), left,
1046+
right, k, areEqual, bv)
9751047
// We want this to hold:
9761048
// `test = value -> left + right is areEqual`
9771049
// Applying 2 we need to show:
@@ -1021,7 +1093,8 @@ private module Cached {
10211093
// See argument for why this is correct in compares_eq
10221094
exists(Operand l, BooleanValue bv |
10231095
unary_compares_eq(test, l, 0, bv.getValue().booleanNot(), value) and
1024-
unary_compares_eq(valueNumberOfOperand(l), op, k, areEqual, bv)
1096+
unary_compares_eq(valueNumber(BooleanInstruction<isUnaryComparesEqLeft/1>::get(l.getDef())),
1097+
op, k, areEqual, bv)
10251098
)
10261099
or
10271100
unary_compares_eq(test.(BuiltinExpectCallValueNumber).getCondition(), op, k, areEqual, value)
@@ -1119,17 +1192,17 @@ private module Cached {
11191192
)
11201193
}
11211194

1195+
private predicate isBuiltInExpectArg(Instruction instr) {
1196+
instr = any(BuiltinExpectCallInstruction buildinExpect).getArgument(0)
1197+
}
1198+
11221199
/** A call to the builtin operation `__builtin_expect`. */
11231200
private class BuiltinExpectCallInstruction extends CallInstruction {
11241201
BuiltinExpectCallInstruction() { this.getStaticCallTarget().hasName("__builtin_expect") }
11251202

11261203
/** Gets the condition of this call. */
1127-
Instruction getCondition() { result = this.getConditionOperand().getDef() }
1128-
1129-
Operand getConditionOperand() {
1130-
// The first parameter of `__builtin_expect` has type `long`. So we skip
1131-
// the conversion when inferring guards.
1132-
result = this.getArgument(0).(ConvertInstruction).getUnaryOperand()
1204+
Instruction getCondition() {
1205+
result = BooleanInstruction<isBuiltInExpectArg/1>::get(this.getArgument(0))
11331206
}
11341207
}
11351208

@@ -1222,7 +1295,8 @@ private module Cached {
12221295
// See argument for why this is correct in compares_eq
12231296
exists(Operand l, BooleanValue bv |
12241297
unary_compares_eq(test, l, 0, bv.getValue().booleanNot(), value) and
1225-
compares_lt(valueNumberOfOperand(l), left, right, k, isLt, bv)
1298+
compares_lt(valueNumber(BooleanInstruction<isUnaryComparesEqLeft/1>::get(l.getDef())), left,
1299+
right, k, isLt, bv)
12261300
)
12271301
}
12281302

@@ -1247,7 +1321,8 @@ private module Cached {
12471321
// See argument for why this is correct in compares_eq
12481322
exists(Operand l, BooleanValue bv |
12491323
unary_compares_eq(test, l, 0, bv.getValue().booleanNot(), value) and
1250-
compares_lt(valueNumberOfOperand(l), op, k, isLt, bv)
1324+
compares_lt(valueNumber(BooleanInstruction<isUnaryComparesEqLeft/1>::get(l.getDef())), op, k,
1325+
isLt, bv)
12511326
)
12521327
}
12531328

0 commit comments

Comments
 (0)