Skip to content

Commit 8bf4a42

Browse files
committed
C#: Sign analysis
Synced between Java and C# through `identical-files.json`.
1 parent 441fbe3 commit 8bf4a42

31 files changed

+2592
-664
lines changed

config/identical-files.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@
5050
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
5151
"python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll"
5252
],
53+
"SsaReadPosition Java/C#": [
54+
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
55+
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
56+
],
57+
"Sign Java/C#": [
58+
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
59+
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
60+
],
61+
"SignAnalysis Java/C#": [
62+
"java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
63+
"csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
64+
],
5365
"C++ SubBasicBlocks": [
5466
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
5567
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
@@ -87,7 +99,7 @@
8799
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
88100
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
89101
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
90-
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
102+
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
91103
],
92104
"IR IRType": [
93105
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
@@ -109,11 +121,11 @@
109121
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
110122
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
111123
],
112-
"IR TInstruction":[
124+
"IR TInstruction": [
113125
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
114126
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
115127
],
116-
"IR TIRVariable":[
128+
"IR TIRVariable": [
117129
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
118130
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
119131
],
@@ -381,4 +393,4 @@
381393
"javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp",
382394
"python/ql/src/Lexical/CommentedOutCodeReferences.qhelp"
383395
]
384-
}
396+
}

csharp/ql/src/Linq/Helpers.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ class AnyCall extends MethodCall {
132132
}
133133
}
134134

135+
/** A LINQ Count(...) call. */
136+
class CountCall extends MethodCall {
137+
CountCall() {
138+
exists(Method m |
139+
m = getTarget() and
140+
isEnumerableType(m.getDeclaringType()) and
141+
m.hasName("Count")
142+
)
143+
}
144+
}
145+
135146
/** A variable of type IEnumerable<T>, for some T. */
136147
class IEnumerableSequence extends Variable {
137148
IEnumerableSequence() { isIEnumerableType(getType()) }

csharp/ql/src/semmle/code/csharp/commons/ComparisonTest.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Provides classes for capturing various ways of performing comparison tests.
33
*/
44

5-
import csharp
5+
private import csharp
66
private import semmle.code.csharp.frameworks.System
77
private import semmle.code.csharp.frameworks.system.Collections
88
private import semmle.code.csharp.frameworks.system.collections.Generic

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,33 @@ class Guard extends Expr {
3131
predicate controlsNode(ControlFlow::Nodes::ElementNode cfn, AccessOrCallExpr sub, AbstractValue v) {
3232
isGuardedByNode(cfn, this, sub, v)
3333
}
34+
35+
/**
36+
* Holds if guard is an equality test between `e1` and `e2`. If the test is
37+
* negated, that is `!=`, then `polarity` is false, otherwise `polarity` is
38+
* true.
39+
*/
40+
predicate isEquality(Expr e1, Expr e2, boolean polarity) {
41+
exists(Expr exp1, Expr exp2 | equalityGuard(exp1, exp2, polarity) |
42+
e1 = exp1 and e2 = exp2
43+
or
44+
e2 = exp1 and e1 = exp2
45+
)
46+
}
47+
48+
private predicate equalityGuard(Expr e1, Expr e2, boolean polarity) {
49+
exists(ComparisonTest eqtest |
50+
eqtest.getExpr() = this and
51+
eqtest.getAnArgument() = e1 and
52+
eqtest.getAnArgument() = e2 and
53+
not e1 = e2 and
54+
(
55+
polarity = true and eqtest.getComparisonKind().isEquality()
56+
or
57+
polarity = false and eqtest.getComparisonKind().isInequality()
58+
)
59+
)
60+
}
3461
}
3562

3663
/** An abstract value. */

csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ import csharp
2121
private import ControlFlow
2222
private import internal.CallableReturns
2323
private import semmle.code.csharp.commons.Assertions
24-
private import semmle.code.csharp.commons.ComparisonTest
2524
private import semmle.code.csharp.controlflow.Guards as G
2625
private import semmle.code.csharp.controlflow.Guards::AbstractValues
27-
private import semmle.code.csharp.dataflow.SSA
2826
private import semmle.code.csharp.frameworks.System
2927
private import semmle.code.csharp.frameworks.Test
3028

csharp/ql/src/semmle/code/csharp/dataflow/SSA.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2537,6 +2537,13 @@ module Ssa {
25372537
)
25382538
}
25392539

2540+
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
2541+
predicate hasInputFromBlock(Definition inp, BasicBlock bb) {
2542+
this.getAnInput() = inp and
2543+
this.getBasicBlock().getAPredecessor() = bb and
2544+
inp.isLiveAtEndOfBlock(bb)
2545+
}
2546+
25402547
override string toString() {
25412548
result = getToStringPrefix(this) + "SSA phi(" + getSourceVariable() + ")"
25422549
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
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+
9+
import semmle.code.csharp.dataflow.internal.rangeanalysis.SignAnalysisCommon
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* Provides classes and predicates to represent constant integer expressions.
3+
*/
4+
5+
private import csharp
6+
private import Ssa
7+
8+
/**
9+
* Holds if property `p` matches `property` in `baseClass` or any overrides.
10+
*/
11+
predicate propertyOverrides(Property p, string baseClass, string property) {
12+
exists(Property p2 |
13+
p2.getSourceDeclaration().getDeclaringType().hasQualifiedName(baseClass) and
14+
p2.hasName(property)
15+
|
16+
p.overridesOrImplementsOrEquals(p2)
17+
)
18+
}
19+
20+
/**
21+
* Holds if expression `e` is either
22+
* - a compile time constant with integer value `val`, or
23+
* - a read of a compile time constant with integer value `val`, or
24+
* - a read of the `Length` of an array with `val` lengths.
25+
*/
26+
private predicate constantIntegerExpr(Expr e, int val) {
27+
e.getValue().toInt() = val
28+
or
29+
exists(ExplicitDefinition v, Expr src |
30+
e = v.getARead() and
31+
src = v.getADefinition().getSource() and
32+
constantIntegerExpr(src, val)
33+
)
34+
or
35+
isArrayLengthAccess(e, val)
36+
}
37+
38+
private int getArrayLength(ArrayCreation arrCreation, int index) {
39+
constantIntegerExpr(arrCreation.getLengthArgument(index), result)
40+
}
41+
42+
private int getArrayLengthRec(ArrayCreation arrCreation, int index) {
43+
index = 0 and result = getArrayLength(arrCreation, 0)
44+
or
45+
index > 0 and
46+
result = getArrayLength(arrCreation, index) * getArrayLengthRec(arrCreation, index - 1)
47+
}
48+
49+
private predicate isArrayLengthAccess(PropertyAccess pa, int length) {
50+
propertyOverrides(pa.getTarget(), "System.Array", "Length") and
51+
exists(ExplicitDefinition arr, ArrayCreation arrCreation |
52+
getArrayLengthRec(arrCreation, arrCreation.getNumberOfLengthArguments() - 1) = length and
53+
arrCreation = arr.getADefinition().getSource() and
54+
pa.getQualifier() = arr.getARead()
55+
)
56+
}
57+
58+
/** An expression that always has the same integer value. */
59+
class ConstantIntegerExpr extends Expr {
60+
ConstantIntegerExpr() { constantIntegerExpr(this, _) }
61+
62+
/** Gets the integer value of this expression. */
63+
int getIntValue() { constantIntegerExpr(this, result) }
64+
}

0 commit comments

Comments
 (0)