Skip to content

Commit 109e45e

Browse files
committed
C++: Add precise range analysis for subtraction.
1 parent 3fb0241 commit 109e45e

File tree

1 file changed

+56
-31
lines changed

1 file changed

+56
-31
lines changed

cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/analysis/RangeAnalysisStage.qll

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -574,16 +574,6 @@ module RangeStage<
574574
)
575575
}
576576

577-
/** Holds if `e >= 1` as determined by sign analysis. */
578-
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
579-
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
580-
}
581-
582-
/** Holds if `e <= -1` as determined by sign analysis. */
583-
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
584-
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
585-
}
586-
587577
/**
588578
* Holds if `e1 + delta` is a valid bound for `e2`.
589579
* - `upper = true` : `e2 <= e1 + delta`
@@ -597,27 +587,6 @@ module RangeStage<
597587
delta = D::fromInt(0) and
598588
(upper = true or upper = false)
599589
or
600-
exists(SemExpr x, SemSubExpr sub |
601-
e2 = sub and
602-
sub.getLeftOperand() = e1 and
603-
sub.getRightOperand() = x
604-
|
605-
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
606-
not x instanceof SemConstantIntegerExpr and
607-
if strictlyPositiveIntegralExpr(x)
608-
then upper = true and delta = D::fromInt(-1)
609-
else
610-
if semPositive(x)
611-
then upper = true and delta = D::fromInt(0)
612-
else
613-
if strictlyNegativeIntegralExpr(x)
614-
then upper = false and delta = D::fromInt(1)
615-
else
616-
if semNegative(x)
617-
then upper = false and delta = D::fromInt(0)
618-
else none()
619-
)
620-
or
621590
e2.(SemRemExpr).getRightOperand() = e1 and
622591
semPositive(e1) and
623592
delta = D::fromInt(-1) and
@@ -1137,6 +1106,30 @@ module RangeStage<
11371106
b = bRight and origdelta = odRight and reason = rRight and bLeft instanceof SemZeroBound
11381107
)
11391108
or
1109+
exists(
1110+
D::Delta dLeft, D::Delta dRight, boolean fbeLeft, boolean fbeRight, D::Delta odLeft,
1111+
D::Delta odRight, SemReason rLeft, SemReason rRight
1112+
|
1113+
boundedSubOperandLeft(e, upper, b, dLeft, fbeLeft, odLeft, rLeft) and
1114+
boundedSubOperandRight(e, upper, dRight, fbeRight, odRight, rRight) and
1115+
// when `upper` is `true` we have:
1116+
// left <= b + dLeft
1117+
// right >= 0 + dRight
1118+
// left - right <= b + dLeft - (0 + dRight)
1119+
// = b + (dLeft - dRight)
1120+
// and when `upper` is `false` we have:
1121+
// left >= b + dLeft
1122+
// right <= 0 + dRight
1123+
// left - right >= b + dLeft - (0 + dRight)
1124+
// = b + (dLeft - dRight)
1125+
delta = D::fromFloat(D::toFloat(dLeft) - D::toFloat(dRight)) and
1126+
fromBackEdge = fbeLeft.booleanOr(fbeRight)
1127+
|
1128+
origdelta = odLeft and reason = rLeft
1129+
or
1130+
origdelta = odRight and reason = rRight
1131+
)
1132+
or
11401133
exists(
11411134
SemRemExpr rem, D::Delta d_max, D::Delta d1, D::Delta d2, boolean fbe1, boolean fbe2,
11421135
D::Delta od1, D::Delta od2, SemReason r1, SemReason r2
@@ -1201,6 +1194,38 @@ module RangeStage<
12011194
)
12021195
}
12031196

1197+
/**
1198+
* Holds if `sub = left - right` and `left <= b + delta` if `upper` is `true`
1199+
* and `left >= b + delta` is `upper` is `false`.
1200+
*/
1201+
pragma[nomagic]
1202+
private predicate boundedSubOperandLeft(
1203+
SemSubExpr sub, boolean upper, SemBound b, D::Delta delta, boolean fromBackEdge,
1204+
D::Delta origdelta, SemReason reason
1205+
) {
1206+
// `semValueFlowStep` already handles the case where one of the operands is a constant.
1207+
not semValueFlowStep(sub, _, _) and
1208+
bounded(sub.getLeftOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
1209+
}
1210+
1211+
/**
1212+
* Holds if `sub = left - right` and `right <= 0 + delta` if `upper` is `false`
1213+
* and `right >= 0 + delta` is `upper` is `true`.
1214+
*
1215+
* Note that the boolean value of `upper` is flipped compared to many other predicates in
1216+
* this file. This ensures a clean join at the call-site.
1217+
*/
1218+
pragma[nomagic]
1219+
private predicate boundedSubOperandRight(
1220+
SemSubExpr sub, boolean upper, D::Delta delta, boolean fromBackEdge, D::Delta origdelta,
1221+
SemReason reason
1222+
) {
1223+
// `semValueFlowStep` already handles the case where one of the operands is a constant.
1224+
not semValueFlowStep(sub, _, _) and
1225+
bounded(sub.getRightOperand(), any(SemZeroBound zb), delta, upper.booleanNot(), fromBackEdge,
1226+
origdelta, reason)
1227+
}
1228+
12041229
pragma[nomagic]
12051230
private predicate boundedRemExpr(
12061231
SemRemExpr rem, boolean upper, D::Delta delta, boolean fromBackEdge, D::Delta origdelta,

0 commit comments

Comments
 (0)