Skip to content

Commit 49798b9

Browse files
committed
C#: Use parameter CFG nodes in SSA
1 parent 46d44ca commit 49798b9

21 files changed

Lines changed: 217 additions & 79 deletions

csharp/ql/lib/semmle/code/csharp/Assignable.qll

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,8 @@ module AssignableInternal {
271271
def = TPatternDefinition(result)
272272
or
273273
def = TAssignOperationDefinition(result)
274+
or
275+
def = TParameterDefaultDefinition(_, result)
274276
}
275277

276278
/** A local variable declaration at the top-level of a pattern. */
@@ -308,10 +310,17 @@ module AssignableInternal {
308310
exists(Callable c | p = c.getAParameter() |
309311
c.hasBody()
310312
or
311-
// Same as `c.(Constructor).hasInitializer()`, but avoids negative recursion warning
312-
c.getAChildExpr() instanceof @constructor_init_expr
313+
c.(Constructor).hasInitializer()
313314
)
314315
} or
316+
TParameterDefaultDefinition(Parameter p, Expr default) {
317+
exists(Callable c | p = c.getAParameter() |
318+
c.hasBody()
319+
or
320+
c.(Constructor).hasInitializer()
321+
) and
322+
default = p.getDefaultValue()
323+
} or
315324
TAddressOfDefinition(AddressOfExpr aoe) or
316325
TPatternDefinition(TopLevelPatternDecl tlpd) or
317326
TAssignOperationDefinition(AssignOperation ao) {
@@ -349,6 +358,8 @@ module AssignableInternal {
349358
any(AssignableDefinitions::PatternDefinition pd | result = pd.getDeclaration().getVariable())
350359
or
351360
def = any(AssignableDefinitions::InitializerDefinition init | result = init.getAssignable())
361+
or
362+
def = TParameterDefaultDefinition(result, _)
352363
}
353364

354365
// Not defined by dispatch in order to avoid too conservative negative recursion error
@@ -492,11 +503,6 @@ class AssignableDefinition extends TAssignableDefinition {
492503
exists(Ssa::ExplicitDefinition def | result = def.getAFirstReadAtNode(cfn) |
493504
this = def.getADefinition()
494505
)
495-
or
496-
exists(Ssa::ImplicitParameterDefinition def | result = def.getAFirstReadAtNode(cfn) |
497-
this.(AssignableDefinitions::ImplicitParameterDefinition).getParameter() =
498-
def.getParameter()
499-
)
500506
)
501507
}
502508

@@ -688,7 +694,33 @@ module AssignableDefinitions {
688694

689695
override string toString() { result = p.toString() }
690696

691-
override Location getLocation() { result = this.getTarget().getLocation() }
697+
override Location getLocation() { result = p.getLocation() }
698+
}
699+
700+
/**
701+
* A default value assigned to a parameter.
702+
*/
703+
class ParameterDefaultDefinition extends AssignableDefinition, TParameterDefaultDefinition {
704+
Parameter p;
705+
Expr default;
706+
707+
ParameterDefaultDefinition() { this = TParameterDefaultDefinition(p, default) }
708+
709+
/** Gets the underlying parameter. */
710+
Parameter getParameter() { result = p }
711+
712+
/** Gets the default value expression for the parameter. */
713+
Expr getDefaultValue() { result = default }
714+
715+
override Expr getSource() { result = default }
716+
717+
override Expr getElement() { result = default }
718+
719+
override Callable getEnclosingCallable() { result = p.getCallable() }
720+
721+
override string toString() { result = p.toString() + " = ..." }
722+
723+
override Location getLocation() { result = default.getLocation() }
692724
}
693725

694726
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ private module LogicInput implements GuardsImpl::LogicInputSig {
205205
}
206206
}
207207

208-
class SsaParameterInit extends SsaDefinition instanceof Ssa::ImplicitParameterDefinition {
208+
class SsaParameterInit extends SsaDefinition instanceof Ssa::ParameterDefinition {
209209
Parameter getParameter() { result = super.getParameter() }
210210
}
211211

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ private predicate dereferenceAt(ControlFlowNode node, Ssa::Definition def, Deref
130130
d = def.getAReadAtNode(node)
131131
}
132132

133-
private predicate isMaybeNullArgument(Ssa::ImplicitParameterDefinition def, MaybeNullExpr arg) {
133+
private predicate isMaybeNullArgument(Ssa::ParameterDefinition def, MaybeNullExpr arg) {
134134
exists(AssignableDefinitions::ImplicitParameterDefinition pdef, Parameter p |
135135
p = def.getParameter()
136136
|
@@ -181,7 +181,7 @@ private predicate hasMultipleParamsArguments(Call c) {
181181
)
182182
}
183183

184-
private predicate isNullDefaultArgument(Ssa::ImplicitParameterDefinition def, AlwaysNullExpr arg) {
184+
private predicate isNullDefaultArgument(Ssa::ParameterDefinition def, AlwaysNullExpr arg) {
185185
exists(AssignableDefinitions::ImplicitParameterDefinition pdef, Parameter p |
186186
p = def.getParameter()
187187
|
@@ -337,8 +337,7 @@ class Dereference extends G::DereferenceableExpr {
337337
or
338338
p.fromSource() and
339339
exists(
340-
Ssa::ImplicitParameterDefinition def,
341-
AssignableDefinitions::ImplicitParameterDefinition pdef
340+
Ssa::ParameterDefinition def, AssignableDefinitions::ImplicitParameterDefinition pdef
342341
|
343342
p = def.getParameter()
344343
|

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

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import csharp
99
*/
1010
module Ssa {
1111
private import internal.SsaImpl as SsaImpl
12-
private import semmle.code.csharp.internal.Location
1312

1413
pragma[nomagic]
1514
private predicate assignableDefinitionLocalScopeVariable(
@@ -19,15 +18,6 @@ module Ssa {
1918
ad.getEnclosingCallable() = c
2019
}
2120

22-
pragma[nomagic]
23-
private predicate localScopeSourceVariable(
24-
SourceVariables::LocalScopeSourceVariable sv, LocalScopeVariable v, Callable c1, Callable c2
25-
) {
26-
sv.getAssignable() = v and
27-
sv.getEnclosingCallable() = c1 and
28-
v.getCallable() = c2
29-
}
30-
3121
/**
3222
* A variable that can be SSA converted.
3323
*
@@ -54,7 +44,7 @@ module Ssa {
5444
not exists(result.getTargetAccess()) and
5545
exists(LocalScopeVariable v, Callable c |
5646
assignableDefinitionLocalScopeVariable(result, v, c) and
57-
localScopeSourceVariable(this, v, c, _)
47+
SsaImpl::localScopeSourceVariable(this, v, c, _)
5848
)
5949
}
6050

@@ -482,8 +472,8 @@ module Ssa {
482472

483473
/**
484474
* An SSA definition representing the implicit initialization of a variable
485-
* at the beginning of a callable. Either a parameter, a local scope variable
486-
* captured by the callable, or a field or property accessed inside the callable.
475+
* at the beginning of a callable. Either a local scope variable captured by
476+
* the callable or a field or property accessed inside the callable.
487477
*/
488478
class ImplicitEntryDefinition extends ImplicitDefinition {
489479
ImplicitEntryDefinition() {
@@ -507,51 +497,37 @@ module Ssa {
507497
override Location getLocation() { result = this.getCallable().getLocation() }
508498
}
509499

510-
private module NearestLocationInput implements NearestLocationInputSig {
511-
class C = ImplicitParameterDefinition;
500+
deprecated class ImplicitParameterDefinition = ParameterDefinition;
512501

513-
predicate relevantLocations(ImplicitParameterDefinition def, Location l1, Location l2) {
514-
not def.getBasicBlock() instanceof EntryBasicBlock and
515-
l1 = def.getParameter().getALocation() and
516-
l2 = def.getBasicBlock().getLocation()
517-
}
518-
}
502+
final class ParameterDefinition = SsaImpl::ParameterDefinitionImpl;
519503

520-
pragma[nomagic]
521-
private predicate implicitEntryDef(ImplicitEntryDefinition def, SourceVariable v, Callable c) {
522-
v = def.getSourceVariable() and
523-
c = def.getCallable()
504+
private class ExplicitParameterDefinition extends ExplicitDefinition,
505+
SsaImpl::ParameterDefinitionImpl
506+
{
507+
private Parameter p;
508+
override AssignableDefinitions::ImplicitParameterDefinition ad;
509+
510+
ExplicitParameterDefinition() { p = ad.getParameter() }
511+
512+
override Parameter getParameter() { result = p }
513+
514+
override string toString() { result = SsaImpl::ParameterDefinitionImpl.super.toString() }
524515
}
525516

526517
/**
527-
* An SSA definition representing the implicit initialization of a parameter
528-
* at the beginning of a callable.
518+
* An SSA definition representing the default value of a parameter.
529519
*/
530-
class ImplicitParameterDefinition extends ImplicitEntryDefinition {
520+
class ParameterDefaultDefinition extends ExplicitDefinition {
531521
private Parameter p;
522+
override AssignableDefinitions::ParameterDefaultDefinition ad;
532523

533-
ImplicitParameterDefinition() {
534-
exists(SourceVariable sv, Callable c |
535-
implicitEntryDef(this, sv, c) and
536-
localScopeSourceVariable(sv, p, _, c)
537-
)
538-
}
524+
ParameterDefaultDefinition() { p = ad.getParameter() }
539525

540526
/** Gets the parameter that this entry definition represents. */
541527
Parameter getParameter() { result = p }
542528

543-
override Element getElement() { result = this.getParameter() }
544-
545529
override string toString() {
546-
result = "SSA param(" + pragma[only_bind_out](this.getParameter()) + ")"
547-
}
548-
549-
override Location getLocation() {
550-
not NearestLocation<NearestLocationInput>::nearestLocation(this, _, _) and
551-
result = p.getLocation()
552-
or
553-
// multi-bodied method: use matching parameter location
554-
NearestLocation<NearestLocationInput>::nearestLocation(this, result, _)
530+
result = "SSA param_default(" + pragma[only_bind_out](this.getParameter()) + ")"
555531
}
556532
}
557533

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,7 @@ private module NearestLocationInputParamAfterCallable implements NearestLocation
12831283

12841284
private module ParameterNodes {
12851285
pragma[nomagic]
1286-
private predicate ssaParamDef(Ssa::ImplicitParameterDefinition ssaDef, Parameter p, Location l) {
1286+
private predicate ssaParamDef(Ssa::ParameterDefinition ssaDef, Parameter p, Location l) {
12871287
p = ssaDef.getParameter() and
12881288
l = ssaDef.getLocation()
12891289
}
@@ -1338,7 +1338,7 @@ private module ParameterNodes {
13381338
}
13391339

13401340
/** Gets the SSA definition corresponding to this parameter, if any. */
1341-
Ssa::ImplicitParameterDefinition getSsaDefinition() {
1341+
Ssa::ParameterDefinition getSsaDefinition() {
13421342
exists(Parameter p, Location l |
13431343
l = this.getParameterLocation(p) and
13441344
ssaParamDef(result, p, l)

csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ private module SourceVariableImpl {
126126
*/
127127
predicate variableDefinition(BasicBlock bb, int i, Ssa::SourceVariable v, AssignableDefinition ad) {
128128
ad = v.getADefinition() and
129-
ad.getExpr().getControlFlowNode() = bb.getNode(i) and
130129
// In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
131130
not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second | first = ad |
132131
second.getAssignment() = first.getAssignment() and
@@ -136,7 +135,13 @@ private module SourceVariableImpl {
136135
// In cases like `M(out x, out x)`, there is no inherent evaluation order, so we
137136
// collapse the two definitions of `x`, using the first access as the representative,
138137
// and expose both definitions in `ExplicitDefinition.getADefinition()`
139-
not ad = getASameOutRefDefAfter(v, _)
138+
not ad = getASameOutRefDefAfter(v, _) and
139+
(
140+
ad.getExpr().getControlFlowNode() = bb.getNode(i)
141+
or
142+
ad.(AssignableDefinitions::ImplicitParameterDefinition).getParameter().getControlFlowNode() =
143+
bb.getNode(i)
144+
)
140145
}
141146

142147
/**
@@ -754,9 +759,6 @@ private module Cached {
754759
cached
755760
predicate implicitEntryDefinition(BasicBlock bb, Ssa::SourceVariable v) {
756761
exists(Callable c |
757-
// In case `c` has multiple bodies, we want each body to get its own implicit
758-
// entry definition, so we use the basic block containing the body instead of
759-
// the entry block.
760762
bb = c.getBody().getBasicBlock() and
761763
c = v.getEnclosingCallable()
762764
|
@@ -770,6 +772,10 @@ private module Cached {
770772
// Each tracked field and property has an implicit entry definition
771773
v instanceof PlainFieldOrPropSourceVariable
772774
or
775+
// In case `c` has multiple bodies, we want each body to get its own implicit
776+
// entry definition, so we use the basic block containing the body instead of
777+
// the entry block.
778+
strictcount(c.getBody()) > 1 and
773779
v.getAssignable() instanceof Parameter
774780
)
775781
}
@@ -961,7 +967,7 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
961967
predicate ssaDefHasSource(WriteDefinition def) {
962968
// exclude flow directly from RHS to SSA definition, as we instead want to
963969
// go from RHS to matching assignable definition, and from there to SSA definition
964-
def instanceof Ssa::ImplicitParameterDefinition
970+
def instanceof Ssa::ParameterDefinition
965971
}
966972

967973
/**
@@ -993,3 +999,64 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
993999
}
9941000

9951001
private module DataFlowIntegrationImpl = Impl::DataFlowIntegration<DataFlowIntegrationInput>;
1002+
1003+
private import semmle.code.csharp.internal.Location
1004+
private import semmle.code.csharp.dataflow.SSA
1005+
1006+
private module NearestLocationInput implements NearestLocationInputSig {
1007+
class C = MultiBodyParameterDefinition;
1008+
1009+
predicate relevantLocations(MultiBodyParameterDefinition def, Location l1, Location l2) {
1010+
l1 = def.getParameter().getALocation() and
1011+
l2 = def.getBasicBlock().getLocation()
1012+
}
1013+
}
1014+
1015+
pragma[nomagic]
1016+
private predicate implicitEntryDef(
1017+
Ssa::ImplicitEntryDefinition def, Ssa::SourceVariable v, Callable c
1018+
) {
1019+
v = def.getSourceVariable() and
1020+
c = def.getCallable()
1021+
}
1022+
1023+
pragma[nomagic]
1024+
predicate localScopeSourceVariable(
1025+
Ssa::SourceVariables::LocalScopeSourceVariable sv, LocalScopeVariable v, Callable c1, Callable c2
1026+
) {
1027+
sv.getAssignable() = v and
1028+
sv.getEnclosingCallable() = c1 and
1029+
v.getCallable() = c2
1030+
}
1031+
1032+
/**
1033+
* An SSA definition representing the implicit initialization of a parameter
1034+
* at the beginning of a callable.
1035+
*/
1036+
abstract class ParameterDefinitionImpl extends Ssa::Definition {
1037+
/** Gets the parameter that this definition represents. */
1038+
abstract Parameter getParameter();
1039+
1040+
override string toString() {
1041+
result = "SSA param(" + pragma[only_bind_out](this.getParameter()) + ")"
1042+
}
1043+
}
1044+
1045+
class MultiBodyParameterDefinition extends ParameterDefinitionImpl, Ssa::ImplicitEntryDefinition {
1046+
private Parameter p;
1047+
1048+
MultiBodyParameterDefinition() {
1049+
exists(Ssa::SourceVariable sv, Callable c |
1050+
implicitEntryDef(this, sv, c) and
1051+
localScopeSourceVariable(sv, p, _, c)
1052+
)
1053+
}
1054+
1055+
override Parameter getParameter() { result = p }
1056+
1057+
override string toString() { result = ParameterDefinitionImpl.super.toString() }
1058+
1059+
override Location getLocation() {
1060+
NearestLocation<NearestLocationInput>::nearestLocation(this, result, _)
1061+
}
1062+
}

csharp/ql/test/library-tests/csharp7/DefUse.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ where
66
(
77
ult.(Ssa::ExplicitDefinition).getADefinition() = def
88
or
9-
ult.(Ssa::ImplicitParameterDefinition).getParameter() =
9+
ult.(Ssa::ParameterDefinition).getParameter() =
1010
def.(AssignableDefinitions::ImplicitParameterDefinition).getParameter()
1111
) and
1212
read = ssaDef.getARead()

csharp/ql/test/library-tests/dataflow/defuse/parameterUseEquivalence.ql

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ private LocalScopeVariableRead getAReachableUncertainRead(
2424
AssignableDefinitions::ImplicitParameterDefinition p
2525
) {
2626
exists(Ssa::Definition ssaDef |
27-
p.getParameter() =
28-
ssaDef.getAnUltimateDefinition().(Ssa::ImplicitParameterDefinition).getParameter()
27+
p.getParameter() = ssaDef.getAnUltimateDefinition().(Ssa::ParameterDefinition).getParameter()
2928
|
3029
result = ssaDef.getARead()
3130
)

csharp/ql/test/library-tests/dataflow/local/DataFlow.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
| LocalDataFlow.cs:315:15:315:20 | access to local variable sink73 |
66
| LocalDataFlow.cs:316:15:316:20 | access to local variable sink74 |
77
| LocalDataFlow.cs:342:15:342:21 | access to parameter tainted |
8+
| LocalDataFlow.cs:387:15:387:15 | access to parameter s |
89
| SSA.cs:9:15:9:22 | access to local variable ssaSink0 |
910
| SSA.cs:25:15:25:22 | access to local variable ssaSink1 |
1011
| SSA.cs:43:15:43:22 | access to local variable ssaSink2 |

0 commit comments

Comments
 (0)