Skip to content

Commit 38ebb45

Browse files
committed
C#: Make getPreUpdateNode Unique Again
1 parent df721f8 commit 38ebb45

File tree

4 files changed

+123
-141
lines changed

4 files changed

+123
-141
lines changed

csharp/ql/consistency-queries/DataFlowConsistency.ql

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,32 +34,18 @@ private module Input implements InputSig<Location, CsharpDataFlow> {
3434
n instanceof FlowSummaryNode
3535
or
3636
n.asExpr().(ObjectCreation).hasInitializer()
37+
or
38+
exists(
39+
n.(PostUpdateNode).getPreUpdateNode().asExprAtNode(LocalFlow::getPostUpdateReverseStep(_))
40+
)
3741
}
3842

3943
predicate argHasPostUpdateExclude(ArgumentNode n) {
4044
n instanceof FlowSummaryNode
4145
or
42-
not exists(LocalFlow::getAPostUpdateNodeForArg(n.getControlFlowNode()))
43-
or
4446
n instanceof ParamsArgumentNode
4547
}
4648

47-
predicate postHasUniquePreExclude(PostUpdateNode n) {
48-
exists(ControlFlow::Nodes::ExprNode e, ControlFlow::Nodes::ExprNode arg |
49-
e = LocalFlow::getAPostUpdateNodeForArg(arg) and
50-
e != arg and
51-
n = TExprPostUpdateNode(e)
52-
)
53-
}
54-
55-
predicate uniquePostUpdateExclude(Node n) {
56-
exists(ControlFlow::Nodes::ExprNode e, ControlFlow::Nodes::ExprNode arg |
57-
e = LocalFlow::getAPostUpdateNodeForArg(arg) and
58-
e != arg and
59-
n.asExpr() = arg.getExpr()
60-
)
61-
}
62-
6349
predicate reverseReadExclude(Node n) { n.asExpr() = any(AwaitExpr ae).getExpr() }
6450

6551
predicate missingArgumentCallExclude(ArgumentNode arg) {

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

Lines changed: 61 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -691,19 +691,22 @@ module LocalFlow {
691691
)
692692
}
693693

694-
/** Gets a node for which to construct a post-update node for argument `arg`. */
695-
ControlFlow::Nodes::ExprNode getAPostUpdateNodeForArg(ControlFlow::Nodes::ExprNode arg) {
696-
arg.getExpr() instanceof Argument and
697-
result = getALastEvalNode*(arg) and
698-
exists(Expr e, Type t | result.getExpr() = e and t = e.stripCasts().getType() |
699-
t instanceof RefType and
700-
not t instanceof NullType
701-
or
702-
t = any(TypeParameter tp | not tp.isValueType())
703-
or
704-
t.isRefLikeType()
705-
) and
706-
not exists(getALastEvalNode(result))
694+
/**
695+
* Holds if a reverse local flow step should be added from the post-update node
696+
* for `e` to the post-update node for the result.
697+
*
698+
* This is needed to allow for side-effects on compound expressions to propagate
699+
* to sub components. For example, in
700+
*
701+
* ```csharp
702+
* m(b ? x : y)
703+
* ```
704+
*
705+
* we add a reverse flow step from `[post] b ? x : y` to `[post] x` and to
706+
* `[post] y`, in order for the side-effect of `m` to reach both `x` and `y`.
707+
*/
708+
ControlFlow::Nodes::ExprNode getPostUpdateReverseStep(ControlFlow::Nodes::ExprNode e) {
709+
result = getALastEvalNode(e)
707710
}
708711

709712
/**
@@ -763,6 +766,13 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
763766
VariableCapture::valueStep(nodeFrom, nodeTo)
764767
or
765768
nodeTo = nodeFrom.(LocalFunctionCreationNode).getAnAccess(true)
769+
or
770+
nodeTo.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode() =
771+
LocalFlow::getPostUpdateReverseStep(nodeFrom
772+
.(PostUpdateNode)
773+
.getPreUpdateNode()
774+
.(ExprNode)
775+
.getControlFlowNode())
766776
) and
767777
model = ""
768778
or
@@ -1106,7 +1116,22 @@ private module Cached {
11061116
cfn.getAstNode().(ObjectCreation).hasInitializer()
11071117
} or
11081118
TExprPostUpdateNode(ControlFlow::Nodes::ExprNode cfn) {
1109-
cfn = LocalFlow::getAPostUpdateNodeForArg(_)
1119+
(
1120+
cfn.getExpr() instanceof Argument
1121+
or
1122+
cfn =
1123+
LocalFlow::getPostUpdateReverseStep(any(ControlFlow::Nodes::ExprNode e |
1124+
exists(any(SourcePostUpdateNode p).getPreUpdateNode().asExprAtNode(e))
1125+
))
1126+
) and
1127+
exists(Expr e, Type t | cfn.getExpr() = e and t = e.stripCasts().getType() |
1128+
t instanceof RefType and
1129+
not t instanceof NullType
1130+
or
1131+
t = any(TypeParameter tp | not tp.isValueType())
1132+
or
1133+
t.isRefLikeType()
1134+
)
11101135
or
11111136
exists(Expr e | e = cfn.getExpr() |
11121137
fieldOrPropertyStore(_, _, _, e, true)
@@ -2722,17 +2747,23 @@ abstract class PostUpdateNode extends Node {
27222747
}
27232748

27242749
module PostUpdateNodes {
2725-
class ObjectCreationNode extends PostUpdateNode, ExprNode, TExprNode {
2750+
abstract class SourcePostUpdateNode extends PostUpdateNode {
2751+
abstract Node getPreUpdateSourceNode();
2752+
2753+
final override Node getPreUpdateNode() { result = this.getPreUpdateSourceNode() }
2754+
}
2755+
2756+
class ObjectCreationNode extends SourcePostUpdateNode, ExprNode, TExprNode {
27262757
private ObjectCreation oc;
27272758

27282759
ObjectCreationNode() { this = TExprNode(oc.getAControlFlowNode()) }
27292760

2730-
override Node getPreUpdateNode() {
2761+
override Node getPreUpdateSourceNode() {
27312762
exists(ControlFlow::Nodes::ElementNode cfn | this = TExprNode(cfn) |
2732-
result.(ObjectInitializerNode).getControlFlowNode() = cfn
2763+
result = TObjectInitializerNode(cfn)
27332764
or
27342765
not oc.hasInitializer() and
2735-
result.(MallocNode).getControlFlowNode() = cfn
2766+
result = TMallocNode(cfn)
27362767
)
27372768
}
27382769
}
@@ -2744,7 +2775,7 @@ module PostUpdateNodes {
27442775
* Such a node acts as both a post-update node for the `MallocNode`, as well as
27452776
* a pre-update node for the `ObjectCreationNode`.
27462777
*/
2747-
class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNodeImpl,
2778+
class ObjectInitializerNode extends SourcePostUpdateNode, NodeImpl, ArgumentNodeImpl,
27482779
TObjectInitializerNode
27492780
{
27502781
private ObjectCreation oc;
@@ -2758,7 +2789,7 @@ module PostUpdateNodes {
27582789
/** Gets the initializer to which this initializer node belongs. */
27592790
ObjectOrCollectionInitializer getInitializer() { result = oc.getInitializer() }
27602791

2761-
override MallocNode getPreUpdateNode() { result.getControlFlowNode() = cfn }
2792+
override MallocNode getPreUpdateSourceNode() { result = TMallocNode(cfn) }
27622793

27632794
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
27642795
pos.isQualifier() and
@@ -2781,23 +2812,12 @@ module PostUpdateNodes {
27812812
override string toStringImpl() { result = "[pre-initializer] " + cfn }
27822813
}
27832814

2784-
class ExprPostUpdateNode extends PostUpdateNode, NodeImpl, TExprPostUpdateNode {
2815+
class ExprPostUpdateNode extends SourcePostUpdateNode, NodeImpl, TExprPostUpdateNode {
27852816
private ControlFlow::Nodes::ElementNode cfn;
27862817

27872818
ExprPostUpdateNode() { this = TExprPostUpdateNode(cfn) }
27882819

2789-
override ExprNode getPreUpdateNode() {
2790-
// For compound arguments, such as `m(b ? x : y)`, we want the leaf nodes
2791-
// `[post] x` and `[post] y` to have two pre-update nodes: (1) the compound argument,
2792-
// `if b then x else y`; and the (2) the underlying expressions; `x` and `y`,
2793-
// respectively.
2794-
//
2795-
// This ensures that we get flow out of the call into both leafs (1), while still
2796-
// maintaining the invariant that the underlying expression is a pre-update node (2).
2797-
cfn = LocalFlow::getAPostUpdateNodeForArg(result.getControlFlowNode())
2798-
or
2799-
cfn = result.getControlFlowNode()
2800-
}
2820+
override ExprNode getPreUpdateSourceNode() { result = TExprNode(cfn) }
28012821

28022822
override DataFlowCallable getEnclosingCallableImpl() {
28032823
result.getAControlFlowNode() = cfn
@@ -2825,49 +2845,49 @@ module PostUpdateNodes {
28252845
override Node getPreUpdateNode() { result.(FlowSummaryNode).getSummaryNode() = preUpdateNode }
28262846
}
28272847

2828-
private class InstanceParameterAccessPostUpdateNode extends PostUpdateNode,
2848+
private class InstanceParameterAccessPostUpdateNode extends SourcePostUpdateNode,
28292849
InstanceParameterAccessNode
28302850
{
28312851
InstanceParameterAccessPostUpdateNode() { isPostUpdate = true }
28322852

2833-
override InstanceParameterAccessPreNode getPreUpdateNode() {
2853+
override InstanceParameterAccessPreNode getPreUpdateSourceNode() {
28342854
result = TInstanceParameterAccessNode(cfn, false)
28352855
}
28362856

28372857
override string toStringImpl() { result = "[post] this" }
28382858
}
28392859

2840-
private class PrimaryConstructorThisAccessPostUpdateNode extends PostUpdateNode,
2860+
private class PrimaryConstructorThisAccessPostUpdateNode extends SourcePostUpdateNode,
28412861
PrimaryConstructorThisAccessNode
28422862
{
28432863
PrimaryConstructorThisAccessPostUpdateNode() { isPostUpdate = true }
28442864

2845-
override PrimaryConstructorThisAccessPreNode getPreUpdateNode() {
2865+
override PrimaryConstructorThisAccessPreNode getPreUpdateSourceNode() {
28462866
result = TPrimaryConstructorThisAccessNode(p, false, callable)
28472867
}
28482868

28492869
override string toStringImpl() { result = "[post] this" }
28502870
}
28512871

2852-
class LocalFunctionCreationPostUpdateNode extends LocalFunctionCreationNode, PostUpdateNode {
2872+
class LocalFunctionCreationPostUpdateNode extends LocalFunctionCreationNode, SourcePostUpdateNode {
28532873
LocalFunctionCreationPostUpdateNode() { isPostUpdate = true }
28542874

2855-
override LocalFunctionCreationPreNode getPreUpdateNode() {
2875+
override LocalFunctionCreationPreNode getPreUpdateSourceNode() {
28562876
result = TLocalFunctionCreationNode(cfn, false)
28572877
}
28582878

28592879
override string toStringImpl() { result = "[post] " + cfn }
28602880
}
28612881

2862-
private class CapturePostUpdateNode extends PostUpdateNode, CaptureNode {
2882+
private class CapturePostUpdateNode extends SourcePostUpdateNode, CaptureNode {
28632883
private CaptureNode pre;
28642884

28652885
CapturePostUpdateNode() {
28662886
VariableCapture::Flow::capturePostUpdateNode(this.getSynthesizedCaptureNode(),
28672887
pre.getSynthesizedCaptureNode())
28682888
}
28692889

2870-
override CaptureNode getPreUpdateNode() { result = pre }
2890+
override CaptureNode getPreUpdateSourceNode() { result = pre }
28712891

28722892
override string toStringImpl() { result = "[post] " + cn }
28732893
}

0 commit comments

Comments
 (0)