Skip to content

Commit aa3b268

Browse files
authored
Merge pull request #4162 from jbj/ssa-ref-parameters
C++: SSA and range analysis for reference parameters
2 parents c25dd4b + 023f2e9 commit aa3b268

File tree

6 files changed

+78
-10
lines changed

6 files changed

+78
-10
lines changed

cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,20 @@ private predicate addressTakenVariable(StackVariable var) {
7373
)
7474
}
7575

76+
/**
77+
* Holds if `v` is a stack-allocated reference-typed local variable. We don't
78+
* build SSA for such variables since they are likely to change values even
79+
* when not syntactically mentioned. For the same reason,
80+
* `addressTakenVariable` is used to prevent tracking variables that may be
81+
* aliased by such a reference.
82+
*
83+
* Reference-typed parameters are treated as if they weren't references.
84+
* That's because it's in practice highly unlikely that they alias other data
85+
* accessible from the function body.
86+
*/
7687
private predicate isReferenceVar(StackVariable v) {
77-
v.getUnspecifiedType() instanceof ReferenceType
88+
v.getUnspecifiedType() instanceof ReferenceType and
89+
not v instanceof Parameter
7890
}
7991

8092
/**

cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ private predicate linearAccessImpl(Expr expr, VariableAccess v, float p, float q
191191
// Base case
192192
expr = v and p = 1.0 and q = 0.0
193193
or
194+
expr.(ReferenceDereferenceExpr).getExpr() = v and p = 1.0 and q = 0.0
195+
or
194196
// a+(p*v+b) == p*v + (a+b)
195197
exists(AddExpr addExpr, float a, float b |
196198
addExpr.getLeftOperand().isConstant() and
@@ -349,21 +351,28 @@ private predicate typeBounds(ArithmeticType t, float lb, float ub) {
349351
t instanceof FloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
350352
}
351353

354+
private Type stripReference(Type t) {
355+
if t instanceof ReferenceType then result = t.(ReferenceType).getBaseType() else result = t
356+
}
357+
358+
/** Gets the type used by range analysis for the given `StackVariable`. */
359+
Type getVariableRangeType(StackVariable v) { result = stripReference(v.getUnspecifiedType()) }
360+
352361
/**
353362
* Gets the lower bound for the unspecified type `t`.
354363
*
355364
* For example, if `t` is a signed 32-bit type then the result is
356365
* `-2^31`.
357366
*/
358-
float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) }
367+
float typeLowerBound(Type t) { typeBounds(stripReference(t), result, _) }
359368

360369
/**
361370
* Gets the upper bound for the unspecified type `t`.
362371
*
363372
* For example, if `t` is a signed 32-bit type then the result is
364373
* `2^31 - 1`.
365374
*/
366-
float typeUpperBound(ArithmeticType t) { typeBounds(t, _, result) }
375+
float typeUpperBound(Type t) { typeBounds(stripReference(t), _, result) }
367376

368377
/**
369378
* Gets the minimum value that this expression could represent, based on

cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) {
485485
* This predicate finds all the definitions in the first set.
486486
*/
487487
private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr expr) {
488-
v.getUnspecifiedType() instanceof ArithmeticType and
488+
getVariableRangeType(v) instanceof ArithmeticType and
489489
(
490490
def = v.getInitializer().getExpr() and def = expr
491491
or
@@ -1329,7 +1329,7 @@ private float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) {
13291329
// recursion from exploding.
13301330
result =
13311331
max(float widenLB |
1332-
widenLB = wideningLowerBounds(v.getUnspecifiedType()) and
1332+
widenLB = wideningLowerBounds(getVariableRangeType(v)) and
13331333
not widenLB > truncatedLB
13341334
|
13351335
widenLB
@@ -1359,7 +1359,7 @@ private float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) {
13591359
// from exploding.
13601360
result =
13611361
min(float widenUB |
1362-
widenUB = wideningUpperBounds(v.getUnspecifiedType()) and
1362+
widenUB = wideningUpperBounds(getVariableRangeType(v)) and
13631363
not widenUB < truncatedUB
13641364
|
13651365
widenUB
@@ -1391,9 +1391,10 @@ private predicate unanalyzableDefBounds(RangeSsaDefinition def, StackVariable v,
13911391
*/
13921392
bindingset[guard, v, branch]
13931393
predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boolean branch) {
1394-
v.getUnspecifiedType() instanceof IntegralType
1394+
getVariableRangeType(v.getTarget()) instanceof IntegralType
13951395
or
1396-
v.getUnspecifiedType() instanceof FloatingPointType and v instanceof NonNanVariableAccess
1396+
getVariableRangeType(v.getTarget()) instanceof FloatingPointType and
1397+
v instanceof NonNanVariableAccess
13971398
or
13981399
// The reason the following case is here is to ensure that when we say
13991400
// `if (x > 5) { ...then... } else { ...else... }`
@@ -1418,7 +1419,7 @@ private predicate lowerBoundFromGuard(
14181419
then
14191420
if
14201421
strictness = Nonstrict() or
1421-
not v.getUnspecifiedType() instanceof IntegralType
1422+
not getVariableRangeType(v.getTarget()) instanceof IntegralType
14221423
then lb = childLB
14231424
else lb = childLB + 1
14241425
else lb = varMinVal(v.getTarget())
@@ -1440,7 +1441,7 @@ private predicate upperBoundFromGuard(
14401441
then
14411442
if
14421443
strictness = Nonstrict() or
1443-
not v.getUnspecifiedType() instanceof IntegralType
1444+
not getVariableRangeType(v.getTarget()) instanceof IntegralType
14441445
then ub = childUB
14451446
else ub = childUB - 1
14461447
else ub = varMaxVal(v.getTarget())

cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,3 +573,15 @@
573573
| test.cpp:75:22:75:30 | c_times_2 | 0 |
574574
| test.cpp:77:5:77:13 | c_times_2 | 0 |
575575
| test.cpp:79:3:79:11 | c_times_2 | 0 |
576+
| test.cpp:83:16:83:22 | aliased | -2147483648 |
577+
| test.cpp:85:7:85:7 | i | -2147483648 |
578+
| test.cpp:86:12:86:12 | i | 2 |
579+
| test.cpp:88:7:88:8 | ci | -2147483648 |
580+
| test.cpp:89:12:89:13 | ci | 2 |
581+
| test.cpp:91:7:91:13 | aliased | -2147483648 |
582+
| test.cpp:92:12:92:18 | aliased | -2147483648 |
583+
| test.cpp:94:7:94:11 | alias | -2147483648 |
584+
| test.cpp:95:12:95:16 | alias | -2147483648 |
585+
| test.cpp:97:10:97:10 | i | -2147483648 |
586+
| test.cpp:97:22:97:22 | i | -2147483648 |
587+
| test.cpp:98:5:98:5 | i | -2147483648 |

cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,25 @@ void use_after_cast(unsigned char c)
7878
}
7979
c_times_2;
8080
}
81+
82+
int ref_to_number(int &i, const int &ci, int &aliased) {
83+
int &alias = aliased; // no range analysis for either of the two aliased variables
84+
85+
if (i >= 2)
86+
return i;
87+
88+
if (ci >= 2)
89+
return ci;
90+
91+
if (aliased >= 2)
92+
return aliased;
93+
94+
if (alias >= 2)
95+
return alias;
96+
97+
for (; i <= 12345; i++) { // test that widening works for references
98+
i;
99+
}
100+
101+
return 0;
102+
}

cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,3 +573,15 @@
573573
| test.cpp:75:22:75:30 | c_times_2 | 510 |
574574
| test.cpp:77:5:77:13 | c_times_2 | 510 |
575575
| test.cpp:79:3:79:11 | c_times_2 | 510 |
576+
| test.cpp:83:16:83:22 | aliased | 2147483647 |
577+
| test.cpp:85:7:85:7 | i | 2147483647 |
578+
| test.cpp:86:12:86:12 | i | 2147483647 |
579+
| test.cpp:88:7:88:8 | ci | 2147483647 |
580+
| test.cpp:89:12:89:13 | ci | 2147483647 |
581+
| test.cpp:91:7:91:13 | aliased | 2147483647 |
582+
| test.cpp:92:12:92:18 | aliased | 2147483647 |
583+
| test.cpp:94:7:94:11 | alias | 2147483647 |
584+
| test.cpp:95:12:95:16 | alias | 2147483647 |
585+
| test.cpp:97:10:97:10 | i | 65535 |
586+
| test.cpp:97:22:97:22 | i | 32767 |
587+
| test.cpp:98:5:98:5 | i | 32767 |

0 commit comments

Comments
 (0)