Skip to content

Commit 282d3e8

Browse files
authored
Merge pull request #4322 from jbj/range-analysis-custom-defs
C++: Support custom defs in SimpleRangeAnalysis
2 parents 32bf7d6 + 4faeede commit 282d3e8

File tree

5 files changed

+124
-1
lines changed

5 files changed

+124
-1
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* EXPERIMENTAL: The API of this module may change without notice.
3+
*
4+
* Provides a class for modeling `RangeSsaDefinition`s with a restricted range.
5+
*/
6+
7+
import cpp
8+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
9+
10+
/**
11+
* EXPERIMENTAL: The API of this class may change without notice.
12+
*
13+
* An SSA definition for which a range can be deduced. As with
14+
* `RangeSsaDefinition` and `SsaDefinition`, instances of this class
15+
* correspond to points in the program where one or more variables are defined
16+
* or have their value constrained in some way.
17+
*
18+
* Extend this class to add functionality to the range analysis library.
19+
*/
20+
abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition {
21+
/**
22+
* Holds if this `SimpleRangeAnalysisDefinition` adds range information for
23+
* `v`. Because a `SimpleRangeAnalysisDefinition` is just a point in the
24+
* program, it's possible that more than one variable might be defined at
25+
* this point. This predicate clarifies which variable(s) should get range
26+
* information from `this`.
27+
*
28+
* This predicate **must be overridden** to hold for any `v` that can show
29+
* up in the other members of `SimpleRangeAnalysisDefinition`. Conversely,
30+
* the other members **must be accurate** for any `v` in this predicate.
31+
*/
32+
abstract predicate hasRangeInformationFor(StackVariable v);
33+
34+
/**
35+
* Holds if `(this, v)` depends on the range of the unconverted expression
36+
* `e`. This information is used to inform the range analysis about cyclic
37+
* dependencies. Without this information, range analysis might work for
38+
* simple cases but will go into infinite loops on complex code.
39+
*
40+
* For example, when modelling the definition by reference in a call to an
41+
* overloaded `operator=`, written as `v = e`, the definition of `(this, v)`
42+
* depends on `e`.
43+
*/
44+
abstract predicate dependsOnExpr(StackVariable v, Expr e);
45+
46+
/**
47+
* Gets the lower bound of the variable `v` defined by this definition.
48+
*
49+
* Implementations of this predicate should use
50+
* `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
51+
* recursive calls to get the bounds of their dependencies.
52+
*/
53+
abstract float getLowerBounds(StackVariable v);
54+
55+
/**
56+
* Gets the upper bound of the variable `v` defined by this definition.
57+
*
58+
* Implementations of this predicate should use
59+
* `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
60+
* recursive calls to get the bounds of their dependencies.
61+
*/
62+
abstract float getUpperBounds(StackVariable v);
63+
}
64+
65+
import SimpleRangeAnalysisInternal

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import cpp
4646
private import RangeAnalysisUtils
4747
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
48+
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition
4849
import RangeSSA
4950
import SimpleRangeAnalysisCached
5051
private import NanAnalysis
@@ -335,6 +336,11 @@ private predicate defDependsOnDef(
335336
or
336337
// Phi nodes.
337338
phiDependsOnDef(def, v, srcDef, srcVar)
339+
or
340+
// Extensions
341+
exists(Expr expr | def.(SimpleRangeAnalysisDefinition).dependsOnExpr(v, expr) |
342+
exprDependsOnDef(expr, srcDef, srcVar)
343+
)
338344
}
339345

340346
/**
@@ -492,6 +498,9 @@ private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) {
492498
v = def.getAVariable()
493499
or
494500
phiDependsOnDef(def, v, _, _)
501+
or
502+
// A modeled def for range analysis
503+
def.(SimpleRangeAnalysisDefinition).hasRangeInformationFor(v)
495504
}
496505

497506
/**
@@ -1215,6 +1224,9 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) {
12151224
// Phi nodes.
12161225
result = getPhiLowerBounds(v, def)
12171226
or
1227+
// A modeled def for range analysis
1228+
result = def.(SimpleRangeAnalysisDefinition).getLowerBounds(v)
1229+
or
12181230
// Unanalyzable definitions.
12191231
unanalyzableDefBounds(def, v, result, _)
12201232
}
@@ -1248,6 +1260,9 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) {
12481260
// Phi nodes.
12491261
result = getPhiUpperBounds(v, def)
12501262
or
1263+
// A modeled def for range analysis
1264+
result = def.(SimpleRangeAnalysisDefinition).getUpperBounds(v)
1265+
or
12511266
// Unanalyzable definitions.
12521267
unanalyzableDefBounds(def, v, _, result)
12531268
}

cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,9 @@ int test_extensibility_add(int x) {
1111
int test_overridability_sub(int x) {
1212
int result = x - (unsigned char)x; // Returns 0 due to custom modeling for this test being deliberately wrong
1313
return result; // 0
14-
}
14+
}
15+
16+
void test_parameter_override(int magic_name_at_most_10, int magic_name_at_most_20) {
17+
magic_name_at_most_10;
18+
magic_name_at_most_20;
19+
}

cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
| extensibility.c:12:16:12:16 | x | -2.147483648E9 | 2.147483647E9 |
66
| extensibility.c:12:35:12:35 | x | -2.147483648E9 | 2.147483647E9 |
77
| extensibility.c:13:10:13:15 | result | 0.0 | 0.0 |
8+
| extensibility.c:17:3:17:23 | magic_name_at_most_10 | -2.147483648E9 | 10.0 |
9+
| extensibility.c:18:3:18:23 | magic_name_at_most_20 | -2.147483648E9 | 20.0 |

cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
2+
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
23
import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
4+
import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition
35

46
class CustomAddFunctionCall extends SimpleRangeAnalysisExpr, FunctionCall {
57
CustomAddFunctionCall() { this.getTarget().hasGlobalName("custom_add_function") }
@@ -37,6 +39,40 @@ class SelfSub extends SimpleRangeAnalysisExpr, SubExpr {
3739
override predicate dependsOnChild(Expr child) { child = this.getAnOperand() }
3840
}
3941

42+
/**
43+
* A definition for test purposes of a parameter `p` that starts with a
44+
* special prefix. This class is written to exploit how QL behaves when class
45+
* fields are not functionally determined by `this`. When multiple parameters
46+
* of the same function have the special prefix, there is still only one
47+
* instance of this class.
48+
*/
49+
class MagicParameterName extends SimpleRangeAnalysisDefinition {
50+
Parameter p;
51+
float value;
52+
53+
MagicParameterName() {
54+
this.definedByParameter(p) and
55+
value = p.getName().regexpCapture("magic_name_at_most_(\\d+)", 1).toFloat()
56+
}
57+
58+
override predicate hasRangeInformationFor(StackVariable v) { v = p }
59+
60+
override predicate dependsOnExpr(StackVariable v, Expr e) {
61+
// No dependencies. This sample class yields constant values.
62+
none()
63+
}
64+
65+
override float getLowerBounds(StackVariable var) {
66+
var = p and
67+
result = typeLowerBound(p.getUnspecifiedType())
68+
}
69+
70+
override float getUpperBounds(StackVariable var) {
71+
var = p and
72+
result = value
73+
}
74+
}
75+
4076
from VariableAccess expr, float lower, float upper
4177
where
4278
lower = lowerBound(expr) and

0 commit comments

Comments
 (0)