Skip to content

Commit 3e9d9e7

Browse files
authored
Merge pull request github#13880 from MathiasVP/type-bounds-preparation
C++: Add a type-based `SemReason`.
2 parents 877ee70 + 98e670f commit 3e9d9e7

File tree

5 files changed

+195
-51
lines changed

5 files changed

+195
-51
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,24 @@
33
*/
44

55
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
6+
private import codeql.util.Unit
7+
private import Reason as Reason
68
private import RangeAnalysisStage
79
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
810

911
module CppLangImplConstant implements LangSig<FloatDelta> {
12+
private module Param implements Reason::ParamSig {
13+
class TypeReasonImpl = Unit;
14+
}
15+
16+
class SemReason = Reason::Make<Param>::SemReason;
17+
18+
class SemNoReason = Reason::Make<Param>::SemNoReason;
19+
20+
class SemCondReason = Reason::Make<Param>::SemCondReason;
21+
22+
class SemTypeReason = Reason::Make<Param>::SemTypeReason;
23+
1024
/**
1125
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
1226
*

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,23 @@ private newtype TSemReason =
6161
guard = any(ConstantStage::SemCondReason reason).getCond()
6262
or
6363
guard = any(RelativeStage::SemCondReason reason).getCond()
64-
}
64+
} or
65+
TSemTypeReason()
6566

66-
ConstantStage::SemReason constantReason(SemReason reason) {
67+
private ConstantStage::SemReason constantReason(SemReason reason) {
6768
result instanceof ConstantStage::SemNoReason and reason instanceof SemNoReason
6869
or
6970
result.(ConstantStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
71+
or
72+
result instanceof ConstantStage::SemTypeReason and reason instanceof SemTypeReason
7073
}
7174

72-
RelativeStage::SemReason relativeReason(SemReason reason) {
75+
private RelativeStage::SemReason relativeReason(SemReason reason) {
7376
result instanceof RelativeStage::SemNoReason and reason instanceof SemNoReason
7477
or
7578
result.(RelativeStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
79+
or
80+
result instanceof RelativeStage::SemTypeReason and reason instanceof SemTypeReason
7681
}
7782

7883
import Public
@@ -111,4 +116,12 @@ module Public {
111116

112117
override string toString() { result = this.getCond().toString() }
113118
}
119+
120+
/**
121+
* A reason for an inferred bound that indicates that the bound is inferred
122+
* based on type-information.
123+
*/
124+
class SemTypeReason extends SemReason, TSemTypeReason {
125+
override string toString() { result = "TypeReason" }
126+
}
114127
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,25 @@ private import RangeAnalysisStage
77
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
88
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.IntDelta
99
private import RangeAnalysisImpl
10+
private import codeql.util.Unit
11+
private import Reason as Reason
1012
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
1113

1214
module CppLangImplRelative implements LangSig<FloatDelta> {
15+
private module Param implements Reason::ParamSig {
16+
class TypeReasonImpl extends Unit {
17+
TypeReasonImpl() { none() }
18+
}
19+
}
20+
21+
class SemReason = Reason::Make<Param>::SemReason;
22+
23+
class SemNoReason = Reason::Make<Param>::SemNoReason;
24+
25+
class SemCondReason = Reason::Make<Param>::SemCondReason;
26+
27+
class SemTypeReason = Reason::Make<Param>::SemTypeReason;
28+
1329
/**
1430
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
1531
*

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

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,37 @@ signature module DeltaSig {
113113
}
114114

115115
signature module LangSig<DeltaSig D> {
116+
/** A reason for an inferred bound. */
117+
class SemReason {
118+
/**
119+
* Returns `this` if `reason` is not a `SemTypeReason`. Otherwise,
120+
* this predicate returns `SemTypeReason`.
121+
*
122+
* This predicate ensures that we propagate `SemTypeReason` all the way
123+
* to the top-level of a call to `semBounded` if the inferred bound is
124+
* based on type-information.
125+
*/
126+
bindingset[this, reason]
127+
SemReason combineWith(SemReason reason);
128+
}
129+
130+
/**
131+
* A reason for an inferred bound that indicates that the bound is inferred
132+
* without going through a bounding condition.
133+
*/
134+
class SemNoReason extends SemReason;
135+
136+
/** A reason for an inferred bound pointing to a condition. */
137+
class SemCondReason extends SemReason {
138+
SemGuard getCond();
139+
}
140+
141+
/**
142+
* A reason for an inferred bound that indicates that the bound is inferred
143+
* based on type-information.
144+
*/
145+
class SemTypeReason extends SemReason;
146+
116147
/**
117148
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
118149
*
@@ -249,6 +280,14 @@ module RangeStage<
249280
DeltaSig D, BoundSig<D> Bounds, OverflowSig<D> OverflowParam, LangSig<D> LangParam,
250281
UtilSig<D> UtilParam>
251282
{
283+
class SemReason = LangParam::SemReason;
284+
285+
class SemCondReason = LangParam::SemCondReason;
286+
287+
class SemNoReason = LangParam::SemNoReason;
288+
289+
class SemTypeReason = LangParam::SemTypeReason;
290+
252291
private import Bounds
253292
private import LangParam
254293
private import UtilParam
@@ -509,36 +548,6 @@ module RangeStage<
509548
)
510549
}
511550

512-
private newtype TSemReason =
513-
TSemNoReason() or
514-
TSemCondReason(SemGuard guard) { possibleReason(guard) }
515-
516-
/**
517-
* A reason for an inferred bound. This can either be `CondReason` if the bound
518-
* is due to a specific condition, or `NoReason` if the bound is inferred
519-
* without going through a bounding condition.
520-
*/
521-
abstract class SemReason extends TSemReason {
522-
/** Gets a textual representation of this reason. */
523-
abstract string toString();
524-
}
525-
526-
/**
527-
* A reason for an inferred bound that indicates that the bound is inferred
528-
* without going through a bounding condition.
529-
*/
530-
class SemNoReason extends SemReason, TSemNoReason {
531-
override string toString() { result = "NoReason" }
532-
}
533-
534-
/** A reason for an inferred bound pointing to a condition. */
535-
class SemCondReason extends SemReason, TSemCondReason {
536-
/** Gets the condition that is the reason for the bound. */
537-
SemGuard getCond() { this = TSemCondReason(result) }
538-
539-
override string toString() { result = this.getCond().toString() }
540-
}
541-
542551
/**
543552
* Holds if `e + delta` is a valid bound for `v` at `pos`.
544553
* - `upper = true` : `v <= e + delta`
@@ -551,13 +560,13 @@ module RangeStage<
551560
semSsaUpdateStep(v, e, delta) and
552561
pos.hasReadOfVar(v) and
553562
(upper = true or upper = false) and
554-
reason = TSemNoReason()
563+
reason instanceof SemNoReason
555564
or
556565
exists(SemGuard guard, boolean testIsTrue |
557566
pos.hasReadOfVar(v) and
558567
guard = boundFlowCond(v, e, delta, upper, testIsTrue) and
559568
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
560-
reason = TSemCondReason(guard)
569+
reason.(SemCondReason).getCond() = guard
561570
)
562571
}
563572

@@ -570,7 +579,7 @@ module RangeStage<
570579
pos.hasReadOfVar(v) and
571580
guard = semEqFlowCond(v, e, delta, false, testIsTrue) and
572581
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
573-
reason = TSemCondReason(guard)
582+
reason.(SemCondReason).getCond() = guard
574583
)
575584
}
576585

@@ -700,7 +709,7 @@ module RangeStage<
700709
// upper = true: v <= mid + d1 <= b + d1 + d2 = b + delta
701710
// upper = false: v >= mid + d1 >= b + d1 + d2 = b + delta
702711
delta = D::fromFloat(D::toFloat(d1) + D::toFloat(d2)) and
703-
(if r1 instanceof SemNoReason then reason = r2 else reason = r1)
712+
(if r1 instanceof SemNoReason then reason = r2 else reason = r1.combineWith(r2))
704713
)
705714
or
706715
exists(D::Delta d, SemReason r1, SemReason r2 |
@@ -714,9 +723,9 @@ module RangeStage<
714723
upper = false and delta = D::fromFloat(D::toFloat(d) + 1)
715724
) and
716725
(
717-
reason = r1
726+
reason = r1.combineWith(r2)
718727
or
719-
reason = r2 and not r2 instanceof SemNoReason
728+
reason = r2.combineWith(r1) and not r2 instanceof SemNoReason
720729
)
721730
)
722731
}
@@ -786,7 +795,7 @@ module RangeStage<
786795
(upper = true or upper = false) and
787796
fromBackEdge0 = false and
788797
origdelta = D::fromFloat(0) and
789-
reason = TSemNoReason()
798+
reason instanceof SemNoReason
790799
|
791800
if semBackEdge(phi, inp, edge)
792801
then
@@ -1044,13 +1053,13 @@ module RangeStage<
10441053
(upper = true or upper = false) and
10451054
fromBackEdge = false and
10461055
origdelta = delta and
1047-
reason = TSemNoReason()
1056+
reason instanceof SemNoReason
10481057
or
10491058
baseBound(e, delta, upper) and
10501059
b instanceof SemZeroBound and
10511060
fromBackEdge = false and
10521061
origdelta = delta and
1053-
reason = TSemNoReason()
1062+
reason instanceof SemNoReason
10541063
or
10551064
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
10561065
boundedSsa(v, bb, b, delta, upper, fromBackEdge, origdelta, reason) and
@@ -1104,9 +1113,9 @@ module RangeStage<
11041113
boundedConditionalExpr(cond, b, upper, true, d1, fbe1, od1, r1) and
11051114
boundedConditionalExpr(cond, b, upper, false, d2, fbe2, od2, r2) and
11061115
(
1107-
delta = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
1116+
delta = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1.combineWith(r2)
11081117
or
1109-
delta = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
1118+
delta = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2.combineWith(r1)
11101119
)
11111120
|
11121121
upper = true and delta = D::fromFloat(D::toFloat(d1).maximum(D::toFloat(d2)))
@@ -1132,9 +1141,15 @@ module RangeStage<
11321141
delta = D::fromFloat(D::toFloat(dLeft) + D::toFloat(dRight)) and
11331142
fromBackEdge = fbeLeft.booleanOr(fbeRight)
11341143
|
1135-
b = bLeft and origdelta = odLeft and reason = rLeft and bRight instanceof SemZeroBound
1144+
b = bLeft and
1145+
origdelta = odLeft and
1146+
reason = rLeft.combineWith(rRight) and
1147+
bRight instanceof SemZeroBound
11361148
or
1137-
b = bRight and origdelta = odRight and reason = rRight and bLeft instanceof SemZeroBound
1149+
b = bRight and
1150+
origdelta = odRight and
1151+
reason = rRight.combineWith(rLeft) and
1152+
bLeft instanceof SemZeroBound
11381153
)
11391154
or
11401155
exists(
@@ -1150,9 +1165,9 @@ module RangeStage<
11501165
(
11511166
if D::toFloat(d1).abs() > D::toFloat(d2).abs()
11521167
then (
1153-
d_max = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
1168+
d_max = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1.combineWith(r2)
11541169
) else (
1155-
d_max = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
1170+
d_max = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2.combineWith(r1)
11561171
)
11571172
)
11581173
|
@@ -1168,11 +1183,14 @@ module RangeStage<
11681183
boundedMulOperand(e, upper, true, dLeft, fbeLeft, odLeft, rLeft) and
11691184
boundedMulOperand(e, upper, false, dRight, fbeRight, odRight, rRight) and
11701185
delta = D::fromFloat(D::toFloat(dLeft) * D::toFloat(dRight)) and
1171-
fromBackEdge = fbeLeft.booleanOr(fbeRight)
1186+
fromBackEdge = fbeLeft.booleanOr(fbeRight) and
1187+
b instanceof SemZeroBound
11721188
|
1173-
b instanceof SemZeroBound and origdelta = odLeft and reason = rLeft
1189+
origdelta = odLeft and
1190+
reason = rLeft.combineWith(rRight)
11741191
or
1175-
b instanceof SemZeroBound and origdelta = odRight and reason = rRight
1192+
origdelta = odRight and
1193+
reason = rRight.combineWith(rLeft)
11761194
)
11771195
)
11781196
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* Provides a `Make` parameterized module for constructing a `Reason` type that is used
3+
* when implementing the `LangSig` module.
4+
*/
5+
6+
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
7+
8+
/** The necessary parameters that must be implemented to instantiate `Make`. */
9+
signature module ParamSig {
10+
class TypeReasonImpl;
11+
}
12+
13+
/**
14+
* The module that constructs a `Reason` type when provided with an implementation
15+
* of `ParamSig`.
16+
*/
17+
module Make<ParamSig Param> {
18+
private import Param
19+
20+
private newtype TSemReason =
21+
TSemNoReason() or
22+
TSemCondReason(SemGuard guard) or
23+
TSemTypeReason(TypeReasonImpl trc)
24+
25+
/**
26+
* A reason for an inferred bound. This can either be `CondReason` if the bound
27+
* is due to a specific condition, or `NoReason` if the bound is inferred
28+
* without going through a bounding condition.
29+
*/
30+
abstract class SemReason extends TSemReason {
31+
/** Gets a textual representation of this reason. */
32+
abstract string toString();
33+
34+
bindingset[this, reason]
35+
abstract SemReason combineWith(SemReason reason);
36+
}
37+
38+
/**
39+
* A reason for an inferred bound that indicates that the bound is inferred
40+
* without going through a bounding condition.
41+
*/
42+
class SemNoReason extends SemReason, TSemNoReason {
43+
override string toString() { result = "NoReason" }
44+
45+
override SemReason combineWith(SemReason reason) { result = reason }
46+
}
47+
48+
/** A reason for an inferred bound pointing to a condition. */
49+
class SemCondReason extends SemReason, TSemCondReason {
50+
/** Gets the condition that is the reason for the bound. */
51+
SemGuard getCond() { this = TSemCondReason(result) }
52+
53+
override string toString() { result = this.getCond().toString() }
54+
55+
bindingset[this, reason]
56+
override SemReason combineWith(SemReason reason) {
57+
// Since we end up reporting a `SemReason` for the inferred bound we often pick somewhat
58+
// arbitrarily between two `SemReason`s during the analysis. This isn't an issue for most reasons
59+
// since they're mainly used for constructing alert messages. However, the `SemTypeReason` is
60+
// supposed to be used in query logic to filter out bounds inferred by type-based analysis if
61+
// the query author chooses to do so. So we need to ensure that if _any_ of the bounds that
62+
// contribute to the final bound depends on type information then the `SemReason` we report must
63+
// be a `SemTypeReason`. So when we need to combine this `SemCondReason` with a `SemTypeReason`
64+
// the result should always be a `SemTypeReason`.
65+
if reason instanceof SemTypeReason then result instanceof SemTypeReason else result = this
66+
}
67+
}
68+
69+
/**
70+
* A reason for an inferred bound that indicates that the bound is inferred
71+
* based on type-information.
72+
*/
73+
class SemTypeReason extends SemReason, TSemTypeReason {
74+
TypeReasonImpl impl;
75+
76+
SemTypeReason() { this = TSemTypeReason(impl) }
77+
78+
override string toString() { result = "TypeReason" }
79+
80+
bindingset[this, reason]
81+
override SemReason combineWith(SemReason reason) { result = this and exists(reason) }
82+
}
83+
}

0 commit comments

Comments
 (0)