Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a3c6b8e

Browse files
author
Robert Marsh
committed
C++: port sign analysis library from Java
1 parent b130335 commit a3c6b8e

13 files changed

Lines changed: 1297 additions & 0 deletions

File tree

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
/**
2+
* Provides sign analysis to determine whether expression are always positive
3+
* or negative.
4+
*
5+
* The analysis is implemented as an abstract interpretation over the
6+
* three-valued domain `{negative, zero, positive}`.
7+
*/
8+
import cpp
9+
private import semmle.code.cpp.ir.IR
10+
private import semmle.code.cpp.controlflow.IRGuards
11+
12+
private newtype TSign = TNeg() or TZero() or TPos()
13+
private class Sign extends TSign {
14+
string toString() {
15+
result = "-" and this = TNeg() or
16+
result = "0" and this = TZero() or
17+
result = "+" and this = TPos()
18+
}
19+
Sign inc() {
20+
this = TNeg() and result = TNeg() or
21+
this = TNeg() and result = TZero() or
22+
this = TZero() and result = TPos() or
23+
this = TPos() and result = TPos()
24+
}
25+
Sign dec() {
26+
result.inc() = this
27+
}
28+
Sign neg() {
29+
this = TNeg() and result = TPos() or
30+
this = TZero() and result = TZero() or
31+
this = TPos() and result = TNeg()
32+
}
33+
Sign bitnot() {
34+
this = TNeg() and result = TPos() or
35+
this = TNeg() and result = TZero() or
36+
this = TZero() and result = TNeg() or
37+
this = TPos() and result = TNeg()
38+
}
39+
Sign add(Sign s) {
40+
this = TZero() and result = s or
41+
s = TZero() and result = this or
42+
this = s and this = result or
43+
this = TPos() and s = TNeg() or
44+
this = TNeg() and s = TPos()
45+
}
46+
Sign mul(Sign s) {
47+
result = TZero() and this = TZero() or
48+
result = TZero() and s = TZero() or
49+
result = TNeg() and this = TPos() and s = TNeg() or
50+
result = TNeg() and this = TNeg() and s = TPos() or
51+
result = TPos() and this = TPos() and s = TPos() or
52+
result = TPos() and this = TNeg() and s = TNeg()
53+
}
54+
Sign div(Sign s) {
55+
result = TZero() and s = TNeg() or
56+
result = TZero() and s = TPos() or
57+
result = TNeg() and this = TPos() and s = TNeg() or
58+
result = TNeg() and this = TNeg() and s = TPos() or
59+
result = TPos() and this = TPos() and s = TPos() or
60+
result = TPos() and this = TNeg() and s = TNeg()
61+
}
62+
Sign rem(Sign s) {
63+
result = TZero() and s = TNeg() or
64+
result = TZero() and s = TPos() or
65+
result = this and s = TNeg() or
66+
result = this and s = TPos()
67+
}
68+
Sign bitand(Sign s) {
69+
result = TZero() and this = TZero() or
70+
result = TZero() and s = TZero() or
71+
result = TZero() and this = TPos() or
72+
result = TZero() and s = TPos() or
73+
result = TNeg() and this = TNeg() and s = TNeg() or
74+
result = TPos() and this = TNeg() and s = TPos() or
75+
result = TPos() and this = TPos() and s = TNeg() or
76+
result = TPos() and this = TPos() and s = TPos()
77+
}
78+
Sign bitor(Sign s) {
79+
result = TZero() and this = TZero() and s = TZero() or
80+
result = TNeg() and this = TNeg() or
81+
result = TNeg() and s = TNeg() or
82+
result = TPos() and this = TPos() and s = TZero() or
83+
result = TPos() and this = TZero() and s = TPos() or
84+
result = TPos() and this = TPos() and s = TPos()
85+
}
86+
Sign bitxor(Sign s) {
87+
result = TZero() and this = s or
88+
result = this and s = TZero() or
89+
result = s and this = TZero() or
90+
result = TPos() and this = TPos() and s = TPos() or
91+
result = TNeg() and this = TNeg() and s = TPos() or
92+
result = TNeg() and this = TPos() and s = TNeg() or
93+
result = TPos() and this = TNeg() and s = TNeg()
94+
}
95+
Sign lshift(Sign s) {
96+
result = TZero() and this = TZero() or
97+
result = this and s = TZero() or
98+
this != TZero() and s != TZero()
99+
}
100+
Sign rshift(Sign s) {
101+
result = TZero() and this = TZero() or
102+
result = this and s = TZero() or
103+
result = TNeg() and this = TNeg() or
104+
result != TNeg() and this = TPos() and s != TZero()
105+
}
106+
Sign urshift(Sign s) {
107+
result = TZero() and this = TZero() or
108+
result = this and s = TZero() or
109+
result != TZero() and this = TNeg() and s != TZero() or
110+
result != TNeg() and this = TPos() and s != TZero()
111+
}
112+
}
113+
114+
private Sign certainInstructionSign(Instruction inst) {
115+
exists(int i | inst.(IntegerConstantInstruction).getValue().toInt() = i |
116+
i < 0 and result = TNeg() or
117+
i = 0 and result = TZero() or
118+
i > 0 and result = TPos()
119+
)
120+
or
121+
not inst instanceof IntegerConstantInstruction and
122+
exists(float f | f = inst.(FloatConstantInstruction).getValue().toFloat() |
123+
f < 0 and result = TNeg() or
124+
f = 0 and result = TZero() or
125+
f > 0 and result = TPos()
126+
)
127+
}
128+
129+
130+
/** Holds if the sign of `e` is too complicated to determine. */
131+
private predicate unknownSign(Instruction i) {
132+
(
133+
i instanceof UnmodeledDefinitionInstruction or
134+
i instanceof UninitializedInstruction or
135+
i instanceof InitializeParameterInstruction or
136+
i instanceof BuiltInInstruction or
137+
i instanceof CallInstruction
138+
)
139+
}
140+
141+
/**
142+
* Holds if `lowerbound` is a lower bound for `compared` at `pos`. This is restricted
143+
* to only include bounds for which we might determine a sign.
144+
*/
145+
private predicate lowerBound(Instruction lowerbound, Instruction compared, Instruction pos, boolean isStrict) {
146+
exists(int adjustment, IRGuardCondition comp |
147+
pos.getAnOperand() = compared and
148+
/*
149+
* Java library uses guardControlsSsaRead here. I think that the phi node logic doesn't need to
150+
* be duplicated but the implication predicates may need to be ported
151+
*/
152+
(
153+
isStrict = true and
154+
adjustment = 0
155+
or
156+
isStrict = false and
157+
adjustment = 1
158+
) and
159+
comp.ensuresLt(lowerbound, compared, 0, pos.getBlock(), true)
160+
)
161+
}
162+
163+
164+
/**
165+
* Holds if `upperbound` is an upper bound for `compared` at `pos`. This is restricted
166+
* to only include bounds for which we might determine a sign.
167+
*/
168+
private predicate upperBound(Instruction upperbound, Instruction compared, Instruction pos, boolean isStrict) {
169+
exists(int adjustment, IRGuardCondition comp |
170+
pos.getAnOperand() = compared and
171+
/*
172+
* Java library uses guardControlsSsaRead here. I think that the phi node logic doesn't need to
173+
* be duplicated but the implication predicates may need to be ported
174+
*/
175+
(
176+
isStrict = true and
177+
adjustment = 0
178+
or
179+
isStrict = false and
180+
adjustment = 1
181+
) and
182+
comp.ensuresLt(compared, upperbound, 0, pos.getBlock(), true)
183+
)
184+
}
185+
186+
/**
187+
* Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
188+
* restricted to only include bounds for which we might determine a sign. The
189+
* boolean `isEq` gives the polarity:
190+
* - `isEq = true` : `v = eqbound`
191+
* - `isEq = false` : `v != eqbound`
192+
*/
193+
private predicate eqBound(Instruction eqbound, Instruction compared, Instruction pos, boolean isEq) {
194+
exists(IRGuardCondition guard |
195+
pos.getAnOperand() = compared and
196+
guard.ensuresEq(compared, eqbound, 0, pos.getBlock(), isEq)
197+
)
198+
}
199+
200+
201+
202+
/**
203+
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
204+
* order for `v` to be positive.
205+
*/
206+
private predicate posBound(Instruction bound, Instruction v, Instruction pos) {
207+
upperBound(bound, v, pos, _) or
208+
eqBound(bound, v, pos, true)
209+
}
210+
211+
/**
212+
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
213+
* order for `v` to be negative.
214+
*/
215+
private predicate negBound(Instruction bound, Instruction v, Instruction pos) {
216+
lowerBound(bound, v, pos, _) or
217+
eqBound(bound, v, pos, true)
218+
}
219+
220+
/**
221+
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
222+
* can be zero.
223+
*/
224+
private predicate zeroBound(Instruction bound, Instruction v, Instruction pos) {
225+
lowerBound(bound, v, pos, _) or
226+
upperBound(bound, v, pos, _) or
227+
eqBound(bound, v, pos, _)
228+
}
229+
230+
/** Holds if `bound` allows `v` to be positive at `pos`. */
231+
private predicate posBoundOk(Instruction bound, Instruction v, Instruction pos) {
232+
posBound(bound, v, pos) and TPos() = instructionSign(bound)
233+
}
234+
235+
/** Holds if `bound` allows `v` to be negative at `pos`. */
236+
private predicate negBoundOk(Instruction bound, Instruction v, Instruction pos) {
237+
negBound(bound, v, pos) and TNeg() = instructionSign(bound)
238+
}
239+
240+
/** Holds if `bound` allows `v` to be zero at `pos`. */
241+
private predicate zeroBoundOk(Instruction bound, Instruction v, Instruction pos) {
242+
lowerBound(bound, v, pos, _) and TNeg() = instructionSign(bound) or
243+
lowerBound(bound, v, pos, false) and TZero() = instructionSign(bound) or
244+
upperBound(bound, v, pos, _) and TPos() = instructionSign(bound) or
245+
upperBound(bound, v, pos, false) and TZero() = instructionSign(bound) or
246+
eqBound(bound, v, pos, true) and TZero() = instructionSign(bound) or
247+
eqBound(bound, v, pos, false) and TZero() != instructionSign(bound)
248+
}
249+
250+
private Sign binaryOpLhsSign(Instruction i) {
251+
result = operandSign(i, i.(BinaryInstruction).getLeftOperand())
252+
}
253+
254+
private Sign binaryOpRhsSign(Instruction i) {
255+
result = operandSign(i, i.(BinaryInstruction).getRightOperand())
256+
}
257+
pragma[noinline]
258+
private predicate binaryOpSigns(Instruction i, Sign lhs, Sign rhs) {
259+
lhs = binaryOpLhsSign(i) and
260+
rhs = binaryOpRhsSign(i)
261+
}
262+
263+
/**
264+
* Holds if there is a bound that might restrict whether `v` has the sign `s`
265+
* at `pos`.
266+
*/
267+
private predicate hasGuard(Instruction v, Instruction pos, Sign s) {
268+
s = TPos() and posBound(_, v, pos) or
269+
s = TNeg() and negBound(_, v, pos) or
270+
s = TZero() and zeroBound(_, v, pos)
271+
}
272+
273+
cached
274+
private Sign operandSign(Instruction pos, Instruction operand) {
275+
hasGuard(operand, pos, result)
276+
or
277+
not hasGuard(operand, pos, _) and
278+
result = instructionSign(operand)
279+
}
280+
281+
cached
282+
private Sign instructionSign(Instruction i) {
283+
result = certainInstructionSign(i)
284+
or
285+
not exists(certainInstructionSign(i)) and
286+
(
287+
unknownSign(i)
288+
or
289+
exists(Instruction prior |
290+
prior = i.(CopyInstruction).getSourceValue()
291+
|
292+
hasGuard(prior, i, result)
293+
or
294+
not exists(Sign s | hasGuard(prior, i, s)) and
295+
result = instructionSign(prior)
296+
)
297+
or
298+
result = instructionSign(i.(BitComplementInstruction).getOperand()).bitnot()
299+
or
300+
result = instructionSign(i.(AddInstruction))
301+
or
302+
exists(Sign s1, Sign s2 |
303+
binaryOpSigns(i, s1, s2)
304+
|
305+
i instanceof AddInstruction and result = s1.add(s2)
306+
or
307+
i instanceof SubInstruction and result = s1.add(s2.neg())
308+
or
309+
i instanceof MulInstruction and result = s1.mul(s2)
310+
or
311+
i instanceof DivInstruction and result = s1.div(s2)
312+
or
313+
i instanceof RemInstruction and result = s1.rem(s2)
314+
or
315+
i instanceof BitAndInstruction and result = s1.bitand(s2)
316+
or
317+
i instanceof BitOrInstruction and result = s1.bitor(s2)
318+
or
319+
i instanceof BitXorInstruction and result = s1.bitxor(s2)
320+
or
321+
i instanceof ShiftLeftInstruction and result = s1.lshift(s2)
322+
or
323+
i instanceof ShiftRightInstruction and
324+
i.getResultType().(IntegralType).isSigned() and
325+
result = s1.rshift(s2)
326+
or
327+
i instanceof ShiftRightInstruction and
328+
not i.getResultType().(IntegralType).isSigned() and
329+
result = s1.urshift(s2)
330+
)
331+
or
332+
// use hasGuard here?
333+
result = operandSign(i, i.(PhiInstruction).getAnOperand())
334+
)
335+
}
336+
337+
/** Holds if `e` can be positive and cannot be negative. */
338+
predicate positive(Instruction i) {
339+
instructionSign(i) = TPos() and
340+
not instructionSign(i) = TNeg()
341+
}
342+
343+
/** Holds if `e` can be negative and cannot be positive. */
344+
predicate negative(Instruction i) {
345+
instructionSign(i) = TNeg() and
346+
not instructionSign(i) = TPos()
347+
}
348+
349+
/** Holds if `e` is strictly positive. */
350+
predicate strictlyPositive(Instruction i) {
351+
instructionSign(i) = TPos() and
352+
not instructionSign(i) = TNeg() and
353+
not instructionSign(i) = TZero()
354+
}
355+
356+
/** Holds if `e` is strictly negative. */
357+
predicate strictlyNegative(Instruction i) {
358+
instructionSign(i) = TNeg() and
359+
not instructionSign(i) = TPos() and
360+
not instructionSign(i) = TZero()
361+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// The ASM statements are
2+
// causing problems, because our SSA analysis does not notice that they
3+
// might change the value of `x`. This was a latent bug that came out
4+
// of the woodwork when we added support for statement expressions.
5+
6+
int printf(const char *format, ...);
7+
8+
int main() {
9+
unsigned int x = 0, y;
10+
y = 1;
11+
12+
printf("x = %i y = %i\n", x, y); // 0, 1
13+
14+
// exchange x and y
15+
asm volatile ( "xchg %0, %1\n"
16+
: "+r" (x), "+a" (y) // outputs (x and y)
17+
:
18+
:
19+
);
20+
21+
printf("x = %i y = %i\n", x, y); // 1, 0 (but without analysing the ASM: unknown, unknown)
22+
23+
return 0;
24+
}

0 commit comments

Comments
 (0)