@@ -21,6 +21,7 @@ private import semmle.code.csharp.frameworks.system.threading.Tasks
21
21
private import semmle.code.cil.Ssa:: Ssa as CilSsa
22
22
private import semmle.code.cil.internal.SsaImpl as CilSsaImpl
23
23
private import codeql.util.Unit
24
+ private import codeql.util.Boolean
24
25
25
26
/** Gets the callable in which this node occurs. */
26
27
DataFlowCallable nodeGetEnclosingCallable ( Node n ) {
@@ -37,6 +38,19 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
37
38
arg .argumentOf ( c , pos )
38
39
}
39
40
41
+ /**
42
+ * Gets a control flow node used for data flow purposes for the primary constructor
43
+ * parameter access `pa`.
44
+ */
45
+ private ControlFlow:: Node getAPrimaryConstructorParameterCfn ( ParameterAccess pa ) {
46
+ pa .getTarget ( ) .getCallable ( ) instanceof PrimaryConstructor and
47
+ (
48
+ result = pa .( ParameterRead ) .getAControlFlowNode ( )
49
+ or
50
+ pa = any ( AssignableDefinition def | result = def .getAControlFlowNode ( ) ) .getTargetAccess ( )
51
+ )
52
+ }
53
+
40
54
abstract class NodeImpl extends Node {
41
55
/** Do not call: use `getEnclosingCallable()` instead. */
42
56
abstract DataFlowCallable getEnclosingCallableImpl ( ) ;
@@ -124,9 +138,22 @@ private module ThisFlow {
124
138
n .( InstanceParameterNode ) .getCallable ( ) = cfn .( ControlFlow:: Nodes:: EntryNode ) .getCallable ( )
125
139
or
126
140
n .asExprAtNode ( cfn ) = any ( Expr e | e instanceof ThisAccess or e instanceof BaseAccess )
141
+ or
142
+ n =
143
+ any ( InstanceParameterAccessNode pan |
144
+ pan .getUnderlyingControlFlowNode ( ) = cfn and pan .isPreUpdate ( )
145
+ )
127
146
}
128
147
129
- private predicate thisAccess ( Node n , BasicBlock bb , int i ) { thisAccess ( n , bb .getNode ( i ) ) }
148
+ private predicate thisAccess ( Node n , BasicBlock bb , int i ) {
149
+ thisAccess ( n , bb .getNode ( i ) )
150
+ or
151
+ exists ( Parameter p | n .( PrimaryConstructorThisAccessNode ) .getParameter ( ) = p |
152
+ bb .getCallable ( ) = p .getCallable ( ) and
153
+ i = p .getPosition ( ) + 1 and
154
+ not n instanceof PostUpdateNode
155
+ )
156
+ }
130
157
131
158
private predicate thisRank ( Node n , BasicBlock bb , int rankix ) {
132
159
exists ( int i |
@@ -202,7 +229,7 @@ CIL::DataFlowNode asCilDataFlowNode(Node node) {
202
229
203
230
/** Provides predicates related to local data flow. */
204
231
module LocalFlow {
205
- private class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
232
+ class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
206
233
LocalExprStepConfiguration ( ) { this = "LocalExprStepConfiguration" }
207
234
208
235
override predicate candidate (
@@ -925,7 +952,17 @@ private module Cached {
925
952
TParamsArgumentNode ( ControlFlow:: Node callCfn ) {
926
953
callCfn = any ( Call c | isParamsArg ( c , _, _) ) .getAControlFlowNode ( )
927
954
} or
928
- TFlowInsensitiveFieldNode ( FieldOrProperty f ) { f .isFieldLike ( ) }
955
+ TFlowInsensitiveFieldNode ( FieldOrProperty f ) { f .isFieldLike ( ) } or
956
+ TInstanceParameterAccessNode ( ControlFlow:: Node cfn , boolean isPostUpdate ) {
957
+ exists ( ParameterAccess pa | cfn = getAPrimaryConstructorParameterCfn ( pa ) |
958
+ isPostUpdate = false
959
+ or
960
+ pa instanceof ParameterWrite and isPostUpdate = true
961
+ )
962
+ } or
963
+ TPrimaryConstructorThisAccessNode ( Parameter p , Boolean isPostUpdate ) {
964
+ p .getCallable ( ) instanceof PrimaryConstructor
965
+ }
929
966
930
967
/**
931
968
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
@@ -961,14 +998,20 @@ private module Cached {
961
998
TFieldContent ( Field f ) { f .isUnboundDeclaration ( ) } or
962
999
TPropertyContent ( Property p ) { p .isUnboundDeclaration ( ) } or
963
1000
TElementContent ( ) or
964
- TSyntheticFieldContent ( SyntheticField f )
1001
+ TSyntheticFieldContent ( SyntheticField f ) or
1002
+ TPrimaryConstructorParameterContent ( Parameter p ) {
1003
+ p .getCallable ( ) instanceof PrimaryConstructor
1004
+ }
965
1005
966
1006
cached
967
1007
newtype TContentApprox =
968
1008
TFieldApproxContent ( string firstChar ) { firstChar = approximateFieldContent ( _) } or
969
1009
TPropertyApproxContent ( string firstChar ) { firstChar = approximatePropertyContent ( _) } or
970
1010
TElementApproxContent ( ) or
971
- TSyntheticFieldApproxContent ( )
1011
+ TSyntheticFieldApproxContent ( ) or
1012
+ TPrimaryConstructorParameterApproxContent ( string firstChar ) {
1013
+ firstChar = approximatePrimaryConstructorParameterContent ( _)
1014
+ }
972
1015
973
1016
pragma [ nomagic]
974
1017
private predicate commonSubTypeGeneral ( DataFlowTypeOrUnifiable t1 , RelevantGvnType t2 ) {
@@ -1037,6 +1080,10 @@ predicate nodeIsHidden(Node n) {
1037
1080
n .asExpr ( ) = any ( WithExpr we ) .getInitializer ( )
1038
1081
or
1039
1082
n instanceof FlowInsensitiveFieldNode
1083
+ or
1084
+ n instanceof InstanceParameterAccessNode
1085
+ or
1086
+ n instanceof PrimaryConstructorThisAccessNode
1040
1087
}
1041
1088
1042
1089
/** A CIL SSA definition, viewed as a node in a data flow graph. */
@@ -1745,6 +1792,100 @@ class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
1745
1792
override string toStringImpl ( ) { result = this .getSummaryNode ( ) .toString ( ) }
1746
1793
}
1747
1794
1795
+ /**
1796
+ * A data-flow node used to model reading and writing of primary constructor parameters.
1797
+ * For example, in
1798
+ * ```csharp
1799
+ * public class C(object o)
1800
+ * {
1801
+ * public object GetParam() => o; // (1)
1802
+ *
1803
+ * public void SetParam(object value) => o = value; // (2)
1804
+ * }
1805
+ * ```
1806
+ * the first access to `o` (1) is modeled as `this.o_backing_field` and
1807
+ * the second access to `o` (2) is modeled as `this.o_backing_field = value`.
1808
+ * Both models need a pre-update this node, and the latter need an additional post-update this access,
1809
+ * all of which are represented by an `InstanceParameterAccessNode` node.
1810
+ */
1811
+ class InstanceParameterAccessNode extends NodeImpl , TInstanceParameterAccessNode {
1812
+ private ControlFlow:: Node cfn ;
1813
+ private boolean isPostUpdate ;
1814
+ private Parameter p ;
1815
+
1816
+ InstanceParameterAccessNode ( ) {
1817
+ this = TInstanceParameterAccessNode ( cfn , isPostUpdate ) and
1818
+ exists ( ParameterAccess pa | cfn = getAPrimaryConstructorParameterCfn ( pa ) and pa .getTarget ( ) = p )
1819
+ }
1820
+
1821
+ override DataFlowCallable getEnclosingCallableImpl ( ) {
1822
+ result .asCallable ( ) = cfn .getEnclosingCallable ( )
1823
+ }
1824
+
1825
+ override Type getTypeImpl ( ) { result = cfn .getEnclosingCallable ( ) .getDeclaringType ( ) }
1826
+
1827
+ override ControlFlow:: Nodes:: ElementNode getControlFlowNodeImpl ( ) { none ( ) }
1828
+
1829
+ override Location getLocationImpl ( ) { result = cfn .getLocation ( ) }
1830
+
1831
+ override string toStringImpl ( ) { result = "this" }
1832
+
1833
+ /**
1834
+ * Gets the underlying control flow node.
1835
+ */
1836
+ ControlFlow:: Node getUnderlyingControlFlowNode ( ) { result = cfn }
1837
+
1838
+ /**
1839
+ * Gets the primary constructor parameter that this is a this access to.
1840
+ */
1841
+ Parameter getParameter ( ) { result = p }
1842
+
1843
+ /**
1844
+ * Holds if the this parameter access node corresponds to a pre-update node.
1845
+ */
1846
+ predicate isPreUpdate ( ) { isPostUpdate = false }
1847
+ }
1848
+
1849
+ /**
1850
+ * A data-flow node used to synthesize the body of a primary constructor.
1851
+ *
1852
+ * For example, in
1853
+ * ```csharp
1854
+ * public class C(object o) { }
1855
+ * ```
1856
+ * we synthesize the primary constructor for `C` as
1857
+ * ```csharp
1858
+ * public C(object o) => this.o_backing_field = o;
1859
+ * ```
1860
+ * The synthesized (pre/post-update) this access is represented an `PrimaryConstructorThisAccessNode` node.
1861
+ */
1862
+ class PrimaryConstructorThisAccessNode extends NodeImpl , TPrimaryConstructorThisAccessNode {
1863
+ private Parameter p ;
1864
+ private boolean isPostUpdate ;
1865
+
1866
+ PrimaryConstructorThisAccessNode ( ) { this = TPrimaryConstructorThisAccessNode ( p , isPostUpdate ) }
1867
+
1868
+ override DataFlowCallable getEnclosingCallableImpl ( ) { result .asCallable ( ) = p .getCallable ( ) }
1869
+
1870
+ override Type getTypeImpl ( ) { result = p .getCallable ( ) .getDeclaringType ( ) }
1871
+
1872
+ override ControlFlow:: Nodes:: ElementNode getControlFlowNodeImpl ( ) { none ( ) }
1873
+
1874
+ override Location getLocationImpl ( ) { result = p .getLocation ( ) }
1875
+
1876
+ override string toStringImpl ( ) { result = "this" }
1877
+
1878
+ /**
1879
+ * Gets the primary constructor parameter that this is a this access to.
1880
+ */
1881
+ Parameter getParameter ( ) { result = p }
1882
+
1883
+ /**
1884
+ * Holds if this is a this access for a primary constructor parameter write.
1885
+ */
1886
+ predicate isPostUpdate ( ) { isPostUpdate = true }
1887
+ }
1888
+
1748
1889
/** A field or a property. */
1749
1890
class FieldOrProperty extends Assignable , Modifiable {
1750
1891
FieldOrProperty ( ) {
@@ -1881,6 +2022,18 @@ private PropertyContent getResultContent() {
1881
2022
result .getProperty ( ) = any ( SystemThreadingTasksTaskTClass c_ ) .getResultProperty ( )
1882
2023
}
1883
2024
2025
+ private predicate primaryConstructorParameterStore (
2026
+ SsaDefinitionExtNode node1 , PrimaryConstructorParameterContent c , Node node2
2027
+ ) {
2028
+ exists ( Ssa:: ExplicitDefinition def , ControlFlow:: Node cfn , Parameter p |
2029
+ def = node1 .getDefinitionExt ( ) and
2030
+ p = def .getSourceVariable ( ) .getAssignable ( ) and
2031
+ cfn = def .getControlFlowNode ( ) and
2032
+ node2 = TInstanceParameterAccessNode ( cfn , true ) and
2033
+ c .getParameter ( ) = p
2034
+ )
2035
+ }
2036
+
1884
2037
/**
1885
2038
* Holds if data can flow from `node1` to `node2` via an assignment to
1886
2039
* content `c`.
@@ -1918,6 +2071,14 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
1918
2071
c = getResultContent ( )
1919
2072
)
1920
2073
or
2074
+ primaryConstructorParameterStore ( node1 , c , node2 )
2075
+ or
2076
+ exists ( Parameter p |
2077
+ node1 = TExplicitParameterNode ( p ) and
2078
+ node2 = TPrimaryConstructorThisAccessNode ( p , true ) and
2079
+ c .( PrimaryConstructorParameterContent ) .getParameter ( ) = p
2080
+ )
2081
+ or
1921
2082
FlowSummaryImpl:: Private:: Steps:: summaryStoreStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
1922
2083
node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
1923
2084
}
@@ -2010,6 +2171,14 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
2010
2171
node2 .asExpr ( ) .( AwaitExpr ) .getExpr ( ) = node1 .asExpr ( ) and
2011
2172
c = getResultContent ( )
2012
2173
or
2174
+ node1 =
2175
+ any ( InstanceParameterAccessNode n |
2176
+ n .getUnderlyingControlFlowNode ( ) = node2 .( ExprNode ) .getControlFlowNode ( ) and
2177
+ n .getParameter ( ) = c .( PrimaryConstructorParameterContent ) .getParameter ( ) and
2178
+ n .isPreUpdate ( )
2179
+ ) and
2180
+ node2 .asExpr ( ) instanceof ParameterRead
2181
+ or
2013
2182
// node1 = (..., node2, ...)
2014
2183
// node1.ItemX flows to node2
2015
2184
exists ( TupleExpr te , int i , Expr item |
@@ -2072,6 +2241,8 @@ predicate clearsContent(Node n, ContentSet c) {
2072
2241
c .( FieldContent ) .getField ( ) = f .getUnboundDeclaration ( ) and
2073
2242
not f .isRef ( )
2074
2243
)
2244
+ or
2245
+ n = any ( PostUpdateNode n1 | primaryConstructorParameterStore ( _, c , n1 ) ) .getPreUpdateNode ( )
2075
2246
}
2076
2247
2077
2248
/**
@@ -2361,6 +2532,32 @@ module PostUpdateNodes {
2361
2532
2362
2533
override Node getPreUpdateNode ( ) { result .( FlowSummaryNode ) .getSummaryNode ( ) = preUpdateNode }
2363
2534
}
2535
+
2536
+ private class InstanceParameterAccessPostUpdateNode extends PostUpdateNode ,
2537
+ InstanceParameterAccessNode
2538
+ {
2539
+ private ControlFlow:: Node cfn ;
2540
+
2541
+ InstanceParameterAccessPostUpdateNode ( ) { this = TInstanceParameterAccessNode ( cfn , true ) }
2542
+
2543
+ override Node getPreUpdateNode ( ) { result = TInstanceParameterAccessNode ( cfn , false ) }
2544
+
2545
+ override string toStringImpl ( ) { result = "[post] this" }
2546
+ }
2547
+
2548
+ private class PrimaryConstructorThisAccessPostUpdateNode extends PostUpdateNode ,
2549
+ PrimaryConstructorThisAccessNode
2550
+ {
2551
+ private Parameter p ;
2552
+
2553
+ PrimaryConstructorThisAccessPostUpdateNode ( ) {
2554
+ this = TPrimaryConstructorThisAccessNode ( p , true )
2555
+ }
2556
+
2557
+ override Node getPreUpdateNode ( ) { result = TPrimaryConstructorThisAccessNode ( p , false ) }
2558
+
2559
+ override string toStringImpl ( ) { result = "[post] this" }
2560
+ }
2364
2561
}
2365
2562
2366
2563
private import PostUpdateNodes
@@ -2537,6 +2734,11 @@ class ContentApprox extends TContentApprox {
2537
2734
this = TElementApproxContent ( ) and result = "element"
2538
2735
or
2539
2736
this = TSyntheticFieldApproxContent ( ) and result = "approximated synthetic field"
2737
+ or
2738
+ exists ( string firstChar |
2739
+ this = TPrimaryConstructorParameterApproxContent ( firstChar ) and
2740
+ result = "approximated parameter field " + firstChar
2741
+ )
2540
2742
}
2541
2743
}
2542
2744
@@ -2550,6 +2752,14 @@ private string approximatePropertyContent(PropertyContent pc) {
2550
2752
result = pc .getProperty ( ) .getName ( ) .prefix ( 1 )
2551
2753
}
2552
2754
2755
+ /**
2756
+ * Gets a string for approximating the name of a synthetic field corresponding
2757
+ * to a primary constructor parameter.
2758
+ */
2759
+ private string approximatePrimaryConstructorParameterContent ( PrimaryConstructorParameterContent pc ) {
2760
+ result = pc .getParameter ( ) .getName ( ) .prefix ( 1 )
2761
+ }
2762
+
2553
2763
/** Gets an approximated value for content `c`. */
2554
2764
pragma [ nomagic]
2555
2765
ContentApprox getContentApprox ( Content c ) {
@@ -2560,6 +2770,9 @@ ContentApprox getContentApprox(Content c) {
2560
2770
c instanceof ElementContent and result = TElementApproxContent ( )
2561
2771
or
2562
2772
c instanceof SyntheticFieldContent and result = TSyntheticFieldApproxContent ( )
2773
+ or
2774
+ result =
2775
+ TPrimaryConstructorParameterApproxContent ( approximatePrimaryConstructorParameterContent ( c ) )
2563
2776
}
2564
2777
2565
2778
/**
0 commit comments