Skip to content

Commit 4d430d5

Browse files
authored
Merge pull request github#15037 from aschackmull/range/prunebounds
Rangeanalysis: Prune range calculation.
2 parents 42a6309 + 3bf6c0f commit 4d430d5

File tree

4 files changed

+175
-69
lines changed

4 files changed

+175
-69
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ module CppLangImplConstant implements LangSig<Sem, FloatDelta> {
2525
* Holds if `e2 >= e1 + delta` (if `upper = false`) or `e2 <= e1 + delta` (if `upper = true`).
2626
*/
2727
predicate additionalBoundFlowStep(SemExpr e2, SemExpr e1, float delta, boolean upper) { none() }
28+
29+
predicate includeConstantBounds() { any() }
30+
31+
predicate includeRelativeBounds() { none() }
2832
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,11 @@ private module ModulusAnalysisInstantiated implements ModulusAnalysisSig<Sem> {
173173
}
174174

175175
module ConstantStage =
176-
RangeStage<SemLocation, Sem, FloatDelta, ConstantBounds, FloatOverflow, CppLangImplConstant,
176+
RangeStage<SemLocation, Sem, FloatDelta, AllBounds, FloatOverflow, CppLangImplConstant,
177177
SignAnalysis, ModulusAnalysisInstantiated>;
178178

179179
module RelativeStage =
180-
RangeStage<SemLocation, Sem, FloatDelta, RelativeBounds, FloatOverflow, CppLangImplRelative,
180+
RangeStage<SemLocation, Sem, FloatDelta, AllBounds, FloatOverflow, CppLangImplRelative,
181181
SignAnalysis, ModulusAnalysisInstantiated>;
182182

183183
private newtype TSemReason =

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,8 @@ module CppLangImplRelative implements LangSig<Sem, FloatDelta> {
5757
* Holds if `e2 >= e1 + delta` (if `upper = false`) or `e2 <= e1 + delta` (if `upper = true`).
5858
*/
5959
predicate additionalBoundFlowStep(SemExpr e2, SemExpr e1, float delta, boolean upper) { none() }
60+
61+
predicate includeConstantBounds() { none() }
62+
63+
predicate includeRelativeBounds() { any() }
6064
}

shared/rangeanalysis/codeql/rangeanalysis/RangeAnalysis.qll

Lines changed: 165 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ signature module LangSig<Semantic Sem, DeltaSig D> {
289289
predicate ignoreExprBound(Sem::Expr e);
290290

291291
default predicate javaCompatibility() { none() }
292+
293+
default predicate includeConstantBounds() { any() }
294+
295+
default predicate includeRelativeBounds() { any() }
292296
}
293297

294298
signature module BoundSig<LocationSig Location, Semantic Sem, DeltaSig D> {
@@ -678,60 +682,65 @@ module RangeStage<
678682
* - `upper = false` : `e2 >= e1 + delta`
679683
*/
680684
private predicate boundFlowStep(Sem::Expr e2, Sem::Expr e1, D::Delta delta, boolean upper) {
681-
valueFlowStep(e2, e1, delta) and
682-
(upper = true or upper = false)
683-
or
684-
e2.(SafeCastExpr).getOperand() = e1 and
685-
delta = D::fromInt(0) and
686-
(upper = true or upper = false)
687-
or
688-
javaCompatibility() and
689-
exists(Sem::Expr x, Sem::SubExpr sub |
690-
e2 = sub and
691-
sub.getLeftOperand() = e1 and
692-
sub.getRightOperand() = x
693-
|
694-
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
695-
not x instanceof Sem::ConstantIntegerExpr and
696-
if strictlyPositiveIntegralExpr(x)
697-
then upper = true and delta = D::fromInt(-1)
698-
else
699-
if semPositive(x)
700-
then upper = true and delta = D::fromInt(0)
685+
// Constants have easy, base-case bounds, so let's not infer any recursive bounds.
686+
not e2 instanceof Sem::ConstantIntegerExpr and
687+
(
688+
valueFlowStep(e2, e1, delta) and
689+
upper = [true, false]
690+
or
691+
e2.(SafeCastExpr).getOperand() = e1 and
692+
delta = D::fromInt(0) and
693+
upper = [true, false]
694+
or
695+
javaCompatibility() and
696+
exists(Sem::Expr x, Sem::SubExpr sub |
697+
e2 = sub and
698+
sub.getLeftOperand() = e1 and
699+
sub.getRightOperand() = x
700+
|
701+
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
702+
not x instanceof Sem::ConstantIntegerExpr and
703+
if strictlyPositiveIntegralExpr(x)
704+
then upper = true and delta = D::fromInt(-1)
701705
else
702-
if strictlyNegativeIntegralExpr(x)
703-
then upper = false and delta = D::fromInt(1)
706+
if semPositive(x)
707+
then upper = true and delta = D::fromInt(0)
704708
else
705-
if semNegative(x)
706-
then upper = false and delta = D::fromInt(0)
707-
else none()
709+
if strictlyNegativeIntegralExpr(x)
710+
then upper = false and delta = D::fromInt(1)
711+
else
712+
if semNegative(x)
713+
then upper = false and delta = D::fromInt(0)
714+
else none()
715+
)
716+
or
717+
e2.(Sem::RemExpr).getRightOperand() = e1 and
718+
semPositive(e1) and
719+
delta = D::fromInt(-1) and
720+
upper = true
721+
or
722+
e2.(Sem::RemExpr).getLeftOperand() = e1 and
723+
semPositive(e1) and
724+
delta = D::fromInt(0) and
725+
upper = true
726+
or
727+
e2.(Sem::BitAndExpr).getAnOperand() = e1 and
728+
semPositive(e1) and
729+
delta = D::fromInt(0) and
730+
upper = true
731+
or
732+
e2.(Sem::BitOrExpr).getAnOperand() = e1 and
733+
semPositive(e2) and
734+
delta = D::fromInt(0) and
735+
upper = false
736+
or
737+
additionalBoundFlowStep(e2, e1, delta, upper)
708738
)
709-
or
710-
e2.(Sem::RemExpr).getRightOperand() = e1 and
711-
semPositive(e1) and
712-
delta = D::fromInt(-1) and
713-
upper = true
714-
or
715-
e2.(Sem::RemExpr).getLeftOperand() = e1 and
716-
semPositive(e1) and
717-
delta = D::fromInt(0) and
718-
upper = true
719-
or
720-
e2.(Sem::BitAndExpr).getAnOperand() = e1 and
721-
semPositive(e1) and
722-
delta = D::fromInt(0) and
723-
upper = true
724-
or
725-
e2.(Sem::BitOrExpr).getAnOperand() = e1 and
726-
semPositive(e2) and
727-
delta = D::fromInt(0) and
728-
upper = false
729-
or
730-
additionalBoundFlowStep(e2, e1, delta, upper)
731739
}
732740

733741
/** Holds if `e2 = e1 * factor` and `factor > 0`. */
734742
private predicate boundFlowStepMul(Sem::Expr e2, Sem::Expr e1, D::Delta factor) {
743+
not e2 instanceof Sem::ConstantIntegerExpr and
735744
exists(Sem::ConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
736745
e2.(Sem::MulExpr).hasOperands(e1, c) and factor = D::fromInt(k)
737746
or
@@ -751,6 +760,7 @@ module RangeStage<
751760
* therefore only valid for non-negative numbers.
752761
*/
753762
private predicate boundFlowStepDiv(Sem::Expr e2, Sem::Expr e1, D::Delta factor) {
763+
not e2 instanceof Sem::ConstantIntegerExpr and
754764
Sem::getExprType(e2) instanceof Sem::IntegerType and
755765
exists(Sem::ConstantIntegerExpr c, D::Delta k |
756766
k = D::fromInt(c.getIntValue()) and D::toFloat(k) > 0
@@ -983,16 +993,31 @@ module RangeStage<
983993
)
984994
}
985995

996+
private predicate includeBound(SemBound b) {
997+
// always include phi bounds
998+
b.(SemSsaBound).getVariable() instanceof Sem::SsaPhiNode
999+
or
1000+
if b instanceof SemZeroBound then includeConstantBounds() else includeRelativeBounds()
1001+
}
1002+
9861003
/**
987-
* Holds if `e` has an upper (for `upper = true`) or lower
988-
* (for `upper = false`) bound of `b`.
1004+
* Holds if `e` has an intrinsic upper (for `upper = true`) or lower
1005+
* (for `upper = false`) bound of `b + delta` as a base case for range analysis.
9891006
*/
990-
private predicate baseBound(Sem::Expr e, D::Delta b, boolean upper) {
991-
hasConstantBound(e, b, upper)
992-
or
993-
upper = false and
994-
b = D::fromInt(0) and
995-
semPositive(e.(Sem::BitAndExpr).getAnOperand())
1007+
private predicate baseBound(Sem::Expr e, SemBound b, D::Delta delta, boolean upper) {
1008+
includeBound(b) and
1009+
(
1010+
e = b.getExpr(delta) and
1011+
upper = [true, false]
1012+
or
1013+
hasConstantBound(e, delta, upper) and
1014+
b instanceof SemZeroBound
1015+
or
1016+
upper = false and
1017+
delta = D::fromInt(0) and
1018+
semPositive(e.(Sem::BitAndExpr).getAnOperand()) and
1019+
b instanceof SemZeroBound
1020+
)
9961021
}
9971022

9981023
/**
@@ -1107,6 +1132,84 @@ module RangeStage<
11071132
bindingset[x, y]
11081133
private float truncatingDiv(float x, float y) { result = (x - (x % y)) / y }
11091134

1135+
/**
1136+
* Holds if `e1 + delta` is a valid bound for `e2`.
1137+
* - `upper = true` : `e2 <= e1 + delta`
1138+
* - `upper = false` : `e2 >= e1 + delta`
1139+
*
1140+
* This is restricted to simple forward-flowing steps and disregards phi-nodes.
1141+
*/
1142+
private predicate preBoundStep(Sem::Expr e2, Sem::Expr e1, D::Delta delta, boolean upper) {
1143+
boundFlowStep(e2, e1, delta, upper)
1144+
or
1145+
exists(Sem::SsaVariable v, SsaReadPositionBlock bb |
1146+
boundFlowStepSsa(v, bb, e1, delta, upper, _) and
1147+
bb.getAnSsaRead(v) = e2
1148+
)
1149+
}
1150+
1151+
/**
1152+
* Holds if simple forward-flowing steps from `e` can reach an expression that
1153+
* has multiple incoming bound-flow edges, that is, it has multiple ways to
1154+
* get a valid bound.
1155+
*/
1156+
private predicate reachesBoundMergepoint(Sem::Expr e, boolean upper) {
1157+
2 <= strictcount(Sem::Expr mid | preBoundStep(e, mid, _, upper))
1158+
or
1159+
exists(Sem::SsaPhiNode v, SsaReadPositionBlock bb |
1160+
boundFlowStepSsa(v, bb, _, _, upper, _) and
1161+
bb.getAnSsaRead(v) = e
1162+
)
1163+
or
1164+
exists(Sem::Expr e2 |
1165+
preBoundStep(e2, e, _, upper) and
1166+
reachesBoundMergepoint(e2, upper)
1167+
)
1168+
}
1169+
1170+
pragma[nomagic]
1171+
private predicate relevantPreBoundStep(Sem::Expr e2, Sem::Expr e1, D::Delta delta, boolean upper) {
1172+
preBoundStep(e2, e1, delta, upper) and
1173+
reachesBoundMergepoint(e2, upper)
1174+
}
1175+
1176+
/**
1177+
* Holds if `b + delta` is a valid bound for `e` that can be found using only
1178+
* simple forward-flowing steps and disregarding phi-nodes.
1179+
* - `upper = true` : `e <= b + delta`
1180+
* - `upper = false` : `e >= b + delta`
1181+
*
1182+
* This predicate is used as a fast approximation for `bounded` to avoid
1183+
* excessive computation in certain cases. In particular, this applies to
1184+
* loop-unrolled code like
1185+
* ```
1186+
* if (..) x+=1; else x+=100;
1187+
* x &= 7;
1188+
* if (..) x+=1; else x+=100;
1189+
* x &= 7;
1190+
* if (..) x+=1; else x+=100;
1191+
* x &= 7;
1192+
* ...
1193+
* ```
1194+
*/
1195+
private predicate preBounded(Sem::Expr e, SemBound b, D::Delta delta, boolean upper) {
1196+
baseBound(e, b, delta, upper)
1197+
or
1198+
exists(Sem::Expr mid, D::Delta d1, D::Delta d2 |
1199+
relevantPreBoundStep(e, mid, d1, upper) and
1200+
preBounded(mid, b, d2, upper) and
1201+
delta = D::fromFloat(D::toFloat(d1) + D::toFloat(d2))
1202+
)
1203+
}
1204+
1205+
private predicate bestPreBound(Sem::Expr e, SemBound b, D::Delta delta, boolean upper) {
1206+
delta = min(D::Delta d | preBounded(e, b, d, upper) | d order by D::toFloat(d)) and
1207+
upper = true
1208+
or
1209+
delta = max(D::Delta d | preBounded(e, b, d, upper) | d order by D::toFloat(d)) and
1210+
upper = false
1211+
}
1212+
11101213
/**
11111214
* Holds if `b + delta` is a valid bound for `e`.
11121215
* - `upper = true` : `e <= b + delta`
@@ -1117,15 +1220,14 @@ module RangeStage<
11171220
D::Delta origdelta, SemReason reason
11181221
) {
11191222
not ignoreExprBound(e) and
1120-
(
1121-
e = b.getExpr(delta) and
1122-
(upper = true or upper = false) and
1123-
fromBackEdge = false and
1124-
origdelta = delta and
1125-
reason = TSemNoReason()
1223+
// ignore poor bounds
1224+
not exists(D::Delta d | bestPreBound(e, b, d, upper) |
1225+
D::toFloat(delta) > D::toFloat(d) and upper = true
11261226
or
1127-
baseBound(e, delta, upper) and
1128-
b instanceof SemZeroBound and
1227+
D::toFloat(delta) < D::toFloat(d) and upper = false
1228+
) and
1229+
(
1230+
baseBound(e, b, delta, upper) and
11291231
fromBackEdge = false and
11301232
origdelta = delta and
11311233
reason = TSemNoReason()
@@ -1137,8 +1239,6 @@ module RangeStage<
11371239
or
11381240
exists(Sem::Expr mid, D::Delta d1, D::Delta d2 |
11391241
boundFlowStep(e, mid, d1, upper) and
1140-
// Constants have easy, base-case bounds, so let's not infer any recursive bounds.
1141-
not e instanceof Sem::ConstantIntegerExpr and
11421242
bounded(mid, b, d2, upper, fromBackEdge, origdelta, reason) and
11431243
// upper = true: e <= mid + d1 <= b + d1 + d2 = b + delta
11441244
// upper = false: e >= mid + d1 >= b + d1 + d2 = b + delta
@@ -1152,15 +1252,13 @@ module RangeStage<
11521252
or
11531253
exists(Sem::Expr mid, D::Delta factor, D::Delta d |
11541254
boundFlowStepMul(e, mid, factor) and
1155-
not e instanceof Sem::ConstantIntegerExpr and
11561255
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
11571256
b instanceof SemZeroBound and
11581257
delta = D::fromFloat(D::toFloat(d) * D::toFloat(factor))
11591258
)
11601259
or
11611260
exists(Sem::Expr mid, D::Delta factor, D::Delta d |
11621261
boundFlowStepDiv(e, mid, factor) and
1163-
not e instanceof Sem::ConstantIntegerExpr and
11641262
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
11651263
b instanceof SemZeroBound and
11661264
D::toFloat(d) >= 0 and

0 commit comments

Comments
 (0)