Skip to content

Commit 4df6a41

Browse files
committed
ModulusAnalysis shared between C# and Java
1 parent d2d8d00 commit 4df6a41

File tree

10 files changed

+879
-52
lines changed

10 files changed

+879
-52
lines changed

config/identical-files.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@
6666
"java/ql/src/semmle/code/java/dataflow/Bound.qll",
6767
"csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll"
6868
],
69+
"ModulusAnalysis Java/C#": [
70+
"java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll",
71+
"csharp/ql/src/semmle/code/csharp/dataflow/ModulusAnalysis.qll"
72+
],
6973
"C++ SubBasicBlocks": [
7074
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
7175
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
/**
2+
* Provides inferences of the form: `e` equals `b + v` modulo `m` where `e` is
3+
* an expression, `b` is a `Bound` (typically zero or the value of an SSA
4+
* variable), and `v` is an integer in the range `[0 .. m-1]`.
5+
*/
6+
7+
private import internal.rangeanalysis.ModulusAnalysisSpecific::Private
8+
private import Bound
9+
private import internal.rangeanalysis.SsaReadPositionCommon
10+
11+
/**
12+
* Holds if `e + delta` equals `v` at `pos`.
13+
*/
14+
private predicate valueFlowStepSsa(SsaVariable v, SsaReadPosition pos, Expr e, int delta) {
15+
ssaUpdateStep(v, e, delta) and pos.hasReadOfVar(v)
16+
or
17+
exists(Guard guard, boolean testIsTrue |
18+
pos.hasReadOfVar(v) and
19+
guard = eqFlowCond(v, e, delta, true, testIsTrue) and
20+
guardDirectlyControlsSsaRead(guard, pos, testIsTrue)
21+
)
22+
}
23+
24+
/**
25+
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
26+
* `ConstantIntegerExpr`s.
27+
*/
28+
private predicate nonConstAddition(Expr add, Expr larg, Expr rarg) {
29+
exists(AddExpr a | a = add |
30+
larg = a.getLhs() and
31+
rarg = a.getRhs()
32+
) and
33+
not larg instanceof ConstantIntegerExpr and
34+
not rarg instanceof ConstantIntegerExpr
35+
}
36+
37+
/**
38+
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
39+
* a `ConstantIntegerExpr`.
40+
*/
41+
private predicate nonConstSubtraction(Expr sub, Expr larg, Expr rarg) {
42+
exists(SubExpr s | s = sub |
43+
larg = s.getLhs() and
44+
rarg = s.getRhs()
45+
) and
46+
not rarg instanceof ConstantIntegerExpr
47+
}
48+
49+
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
50+
private Expr modExpr(Expr arg, int mod) {
51+
exists(RemExpr rem |
52+
result = rem and
53+
arg = rem.getLeftOperand() and
54+
rem.getRightOperand().(ConstantIntegerExpr).getIntValue() = mod and
55+
mod >= 2
56+
)
57+
or
58+
exists(ConstantIntegerExpr c |
59+
mod = 2.pow([1 .. 30]) and
60+
c.getIntValue() = mod - 1 and
61+
result.(BitwiseAndExpr).hasOperands(arg, c)
62+
)
63+
}
64+
65+
/**
66+
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
67+
* its `testIsTrue` branch.
68+
*/
69+
private Guard moduloCheck(SsaVariable v, int val, int mod, boolean testIsTrue) {
70+
exists(Expr rem, ConstantIntegerExpr c, int r, boolean polarity |
71+
result.isEquality(rem, c, polarity) and
72+
c.getIntValue() = r and
73+
rem = modExpr(v.getAUse(), mod) and
74+
(
75+
testIsTrue = polarity and val = r
76+
or
77+
testIsTrue = polarity.booleanNot() and
78+
mod = 2 and
79+
val = 1 - r and
80+
(r = 0 or r = 1)
81+
)
82+
)
83+
}
84+
85+
/**
86+
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
87+
*/
88+
private predicate moduloGuardedRead(SsaVariable v, SsaReadPosition pos, int val, int mod) {
89+
exists(Guard guard, boolean testIsTrue |
90+
pos.hasReadOfVar(v) and
91+
guard = moduloCheck(v, val, mod, testIsTrue) and
92+
guardControlsSsaRead(guard, pos, testIsTrue)
93+
)
94+
}
95+
96+
/** Holds if `factor` is a power of 2 that divides `mask`. */
97+
bindingset[mask]
98+
private predicate andmaskFactor(int mask, int factor) {
99+
mask % factor = 0 and
100+
factor = 2.pow([1 .. 30])
101+
}
102+
103+
/** Holds if `e` is evenly divisible by `factor`. */
104+
private predicate evenlyDivisibleExpr(Expr e, int factor) {
105+
exists(ConstantIntegerExpr c, int k | k = c.getIntValue() |
106+
e.(MulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
107+
or
108+
e.(LShiftExpr).getRhs() = c and factor = 2.pow(k) and k > 0
109+
or
110+
e.(BitwiseAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
111+
)
112+
}
113+
114+
/**
115+
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
116+
* in an arbitrary 1-based numbering of the input edges to `phi`.
117+
*/
118+
private predicate rankedPhiInput(
119+
SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge, int r
120+
) {
121+
edge.phiInput(phi, inp) and
122+
edge =
123+
rank[r](SsaReadPositionPhiInputEdge e | e.phiInput(phi, _) | e order by getId(e.getOrigBlock()))
124+
}
125+
126+
/**
127+
* Holds if `rix` is the number of input edges to `phi`.
128+
*/
129+
private predicate maxPhiInputRank(SsaPhiNode phi, int rix) {
130+
rix = max(int r | rankedPhiInput(phi, _, _, r))
131+
}
132+
133+
/**
134+
* Gets the remainder of `val` modulo `mod`.
135+
*
136+
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
137+
* the range `[0 .. mod-1]`.
138+
*/
139+
bindingset[val, mod]
140+
private int remainder(int val, int mod) {
141+
mod = 0 and result = val
142+
or
143+
mod > 1 and result = ((val % mod) + mod) % mod
144+
}
145+
146+
/**
147+
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
148+
*/
149+
private predicate phiSelfModulus(
150+
SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge, int mod
151+
) {
152+
exists(SsaBound phibound, int v, int m |
153+
edge.phiInput(phi, inp) and
154+
phibound.getSsa() = phi and
155+
ssaModulus(inp, edge, phibound, v, m) and
156+
mod = m.gcd(v) and
157+
mod != 1
158+
)
159+
}
160+
161+
/**
162+
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
163+
*/
164+
private predicate phiModulusInit(SsaPhiNode phi, Bound b, int val, int mod) {
165+
exists(SsaVariable inp, SsaReadPositionPhiInputEdge edge |
166+
edge.phiInput(phi, inp) and
167+
ssaModulus(inp, edge, b, val, mod)
168+
)
169+
}
170+
171+
/**
172+
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
173+
*/
174+
private predicate phiModulusRankStep(SsaPhiNode phi, Bound b, int val, int mod, int rix) {
175+
rix = 0 and
176+
phiModulusInit(phi, b, val, mod)
177+
or
178+
exists(SsaVariable inp, SsaReadPositionPhiInputEdge edge, int v1, int m1 |
179+
mod != 1 and
180+
val = remainder(v1, mod)
181+
|
182+
exists(int v2, int m2 |
183+
rankedPhiInput(phi, inp, edge, rix) and
184+
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
185+
ssaModulus(inp, edge, b, v2, m2) and
186+
mod = m1.gcd(m2).gcd(v1 - v2)
187+
)
188+
or
189+
exists(int m2 |
190+
rankedPhiInput(phi, inp, edge, rix) and
191+
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
192+
phiSelfModulus(phi, inp, edge, m2) and
193+
mod = m1.gcd(m2)
194+
)
195+
)
196+
}
197+
198+
/**
199+
* Holds if `phi` is equal to `b + val` modulo `mod`.
200+
*/
201+
private predicate phiModulus(SsaPhiNode phi, Bound b, int val, int mod) {
202+
exists(int r |
203+
maxPhiInputRank(phi, r) and
204+
phiModulusRankStep(phi, b, val, mod, r)
205+
)
206+
}
207+
208+
/**
209+
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
210+
*/
211+
private predicate ssaModulus(SsaVariable v, SsaReadPosition pos, Bound b, int val, int mod) {
212+
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
213+
or
214+
b.(SsaBound).getSsa() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
215+
or
216+
exists(Expr e, int val0, int delta |
217+
exprModulus(e, b, val0, mod) and
218+
valueFlowStepSsa(v, pos, e, delta) and
219+
val = remainder(val0 + delta, mod)
220+
)
221+
or
222+
moduloGuardedRead(v, pos, val, mod) and b instanceof ZeroBound
223+
}
224+
225+
/**
226+
* Holds if `e` is equal to `b + val` modulo `mod`.
227+
*
228+
* There are two cases for the modulus:
229+
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
230+
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
231+
*/
232+
cached
233+
predicate exprModulus(Expr e, Bound b, int val, int mod) {
234+
e = b.getExpr(val) and mod = 0
235+
or
236+
evenlyDivisibleExpr(e, mod) and val = 0 and b instanceof ZeroBound
237+
or
238+
exists(SsaVariable v, SsaReadPositionBlock bb |
239+
ssaModulus(v, bb, b, val, mod) and
240+
e = v.getAUse() and
241+
getABasicBlockExpr(bb.getBlock()) = e
242+
)
243+
or
244+
exists(Expr mid, int val0, int delta |
245+
exprModulus(mid, b, val0, mod) and
246+
valueFlowStep(e, mid, delta) and
247+
val = remainder(val0 + delta, mod)
248+
)
249+
or
250+
exists(ConditionalExpr cond, int v1, int v2, int m1, int m2 |
251+
cond = e and
252+
condExprBranchModulus(cond, true, b, v1, m1) and
253+
condExprBranchModulus(cond, false, b, v2, m2) and
254+
mod = m1.gcd(m2).gcd(v1 - v2) and
255+
mod != 1 and
256+
val = remainder(v1, mod)
257+
)
258+
or
259+
exists(Bound b1, Bound b2, int v1, int v2, int m1, int m2 |
260+
addModulus(e, true, b1, v1, m1) and
261+
addModulus(e, false, b2, v2, m2) and
262+
mod = m1.gcd(m2) and
263+
mod != 1 and
264+
val = remainder(v1 + v2, mod)
265+
|
266+
b = b1 and b2 instanceof ZeroBound
267+
or
268+
b = b2 and b1 instanceof ZeroBound
269+
)
270+
or
271+
exists(int v1, int v2, int m1, int m2 |
272+
subModulus(e, true, b, v1, m1) and
273+
subModulus(e, false, any(ZeroBound zb), v2, m2) and
274+
mod = m1.gcd(m2) and
275+
mod != 1 and
276+
val = remainder(v1 - v2, mod)
277+
)
278+
}
279+
280+
private predicate condExprBranchModulus(
281+
ConditionalExpr cond, boolean branch, Bound b, int val, int mod
282+
) {
283+
exprModulus(cond.getTrueExpr(), b, val, mod) and branch = true
284+
or
285+
exprModulus(cond.getFalseExpr(), b, val, mod) and branch = false
286+
}
287+
288+
private predicate addModulus(Expr add, boolean isLeft, Bound b, int val, int mod) {
289+
exists(Expr larg, Expr rarg | nonConstAddition(add, larg, rarg) |
290+
exprModulus(larg, b, val, mod) and isLeft = true
291+
or
292+
exprModulus(rarg, b, val, mod) and isLeft = false
293+
)
294+
}
295+
296+
private predicate subModulus(Expr sub, boolean isLeft, Bound b, int val, int mod) {
297+
exists(Expr larg, Expr rarg | nonConstSubtraction(sub, larg, rarg) |
298+
exprModulus(larg, b, val, mod) and isLeft = true
299+
or
300+
exprModulus(rarg, b, val, mod) and isLeft = false
301+
)
302+
}

0 commit comments

Comments
 (0)