Skip to content

Commit 1a75c05

Browse files
authored
Merge pull request #19128 from hvitved/csharp/pre-update-unique
C#: Make `getPreUpdateNode` Unique Again
2 parents f209f53 + 023ffe2 commit 1a75c05

File tree

4 files changed

+132
-141
lines changed

4 files changed

+132
-141
lines changed

csharp/ql/consistency-queries/DataFlowConsistency.ql

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,30 +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
45-
}
46-
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-
)
47+
or
48+
n.asExpr() = any(Expr e | not exprMayHavePostUpdateNode(e))
6149
}
6250

6351
predicate reverseReadExclude(Node n) { n.asExpr() = any(AwaitExpr ae).getExpr() }

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

Lines changed: 68 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
@@ -1061,6 +1071,20 @@ private class FieldOrPropertyUsedInSource extends FieldOrProperty {
10611071
}
10621072
}
10631073

1074+
/**
1075+
* Hold if `e` has a type that allows for it to have a post-update node.
1076+
*/
1077+
predicate exprMayHavePostUpdateNode(Expr e) {
1078+
exists(Type t | t = e.stripCasts().getType() |
1079+
t instanceof RefType and
1080+
not t instanceof NullType
1081+
or
1082+
t = any(TypeParameter tp | not tp.isValueType())
1083+
or
1084+
t.isRefLikeType()
1085+
)
1086+
}
1087+
10641088
/** A collection of cached types and predicates to be evaluated in the same stage. */
10651089
cached
10661090
private module Cached {
@@ -1106,7 +1130,15 @@ private module Cached {
11061130
cfn.getAstNode().(ObjectCreation).hasInitializer()
11071131
} or
11081132
TExprPostUpdateNode(ControlFlow::Nodes::ExprNode cfn) {
1109-
cfn = LocalFlow::getAPostUpdateNodeForArg(_)
1133+
(
1134+
cfn.getExpr() instanceof Argument
1135+
or
1136+
cfn =
1137+
LocalFlow::getPostUpdateReverseStep(any(ControlFlow::Nodes::ExprNode e |
1138+
exists(any(SourcePostUpdateNode p).getPreUpdateNode().asExprAtNode(e))
1139+
))
1140+
) and
1141+
exprMayHavePostUpdateNode(cfn.getExpr())
11101142
or
11111143
exists(Expr e | e = cfn.getExpr() |
11121144
fieldOrPropertyStore(_, _, _, e, true)
@@ -2722,17 +2754,23 @@ abstract class PostUpdateNode extends Node {
27222754
}
27232755

27242756
module PostUpdateNodes {
2725-
class ObjectCreationNode extends PostUpdateNode, ExprNode, TExprNode {
2757+
abstract class SourcePostUpdateNode extends PostUpdateNode {
2758+
abstract Node getPreUpdateSourceNode();
2759+
2760+
final override Node getPreUpdateNode() { result = this.getPreUpdateSourceNode() }
2761+
}
2762+
2763+
class ObjectCreationNode extends SourcePostUpdateNode, ExprNode, TExprNode {
27262764
private ObjectCreation oc;
27272765

27282766
ObjectCreationNode() { this = TExprNode(oc.getAControlFlowNode()) }
27292767

2730-
override Node getPreUpdateNode() {
2768+
override Node getPreUpdateSourceNode() {
27312769
exists(ControlFlow::Nodes::ElementNode cfn | this = TExprNode(cfn) |
2732-
result.(ObjectInitializerNode).getControlFlowNode() = cfn
2770+
result = TObjectInitializerNode(cfn)
27332771
or
27342772
not oc.hasInitializer() and
2735-
result.(MallocNode).getControlFlowNode() = cfn
2773+
result = TMallocNode(cfn)
27362774
)
27372775
}
27382776
}
@@ -2744,7 +2782,7 @@ module PostUpdateNodes {
27442782
* Such a node acts as both a post-update node for the `MallocNode`, as well as
27452783
* a pre-update node for the `ObjectCreationNode`.
27462784
*/
2747-
class ObjectInitializerNode extends PostUpdateNode, NodeImpl, ArgumentNodeImpl,
2785+
class ObjectInitializerNode extends SourcePostUpdateNode, NodeImpl, ArgumentNodeImpl,
27482786
TObjectInitializerNode
27492787
{
27502788
private ObjectCreation oc;
@@ -2758,7 +2796,7 @@ module PostUpdateNodes {
27582796
/** Gets the initializer to which this initializer node belongs. */
27592797
ObjectOrCollectionInitializer getInitializer() { result = oc.getInitializer() }
27602798

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

27632801
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
27642802
pos.isQualifier() and
@@ -2781,23 +2819,12 @@ module PostUpdateNodes {
27812819
override string toStringImpl() { result = "[pre-initializer] " + cfn }
27822820
}
27832821

2784-
class ExprPostUpdateNode extends PostUpdateNode, NodeImpl, TExprPostUpdateNode {
2822+
class ExprPostUpdateNode extends SourcePostUpdateNode, NodeImpl, TExprPostUpdateNode {
27852823
private ControlFlow::Nodes::ElementNode cfn;
27862824

27872825
ExprPostUpdateNode() { this = TExprPostUpdateNode(cfn) }
27882826

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-
}
2827+
override ExprNode getPreUpdateSourceNode() { result = TExprNode(cfn) }
28012828

28022829
override DataFlowCallable getEnclosingCallableImpl() {
28032830
result.getAControlFlowNode() = cfn
@@ -2825,49 +2852,49 @@ module PostUpdateNodes {
28252852
override Node getPreUpdateNode() { result.(FlowSummaryNode).getSummaryNode() = preUpdateNode }
28262853
}
28272854

2828-
private class InstanceParameterAccessPostUpdateNode extends PostUpdateNode,
2855+
private class InstanceParameterAccessPostUpdateNode extends SourcePostUpdateNode,
28292856
InstanceParameterAccessNode
28302857
{
28312858
InstanceParameterAccessPostUpdateNode() { isPostUpdate = true }
28322859

2833-
override InstanceParameterAccessPreNode getPreUpdateNode() {
2860+
override InstanceParameterAccessPreNode getPreUpdateSourceNode() {
28342861
result = TInstanceParameterAccessNode(cfn, false)
28352862
}
28362863

28372864
override string toStringImpl() { result = "[post] this" }
28382865
}
28392866

2840-
private class PrimaryConstructorThisAccessPostUpdateNode extends PostUpdateNode,
2867+
private class PrimaryConstructorThisAccessPostUpdateNode extends SourcePostUpdateNode,
28412868
PrimaryConstructorThisAccessNode
28422869
{
28432870
PrimaryConstructorThisAccessPostUpdateNode() { isPostUpdate = true }
28442871

2845-
override PrimaryConstructorThisAccessPreNode getPreUpdateNode() {
2872+
override PrimaryConstructorThisAccessPreNode getPreUpdateSourceNode() {
28462873
result = TPrimaryConstructorThisAccessNode(p, false, callable)
28472874
}
28482875

28492876
override string toStringImpl() { result = "[post] this" }
28502877
}
28512878

2852-
class LocalFunctionCreationPostUpdateNode extends LocalFunctionCreationNode, PostUpdateNode {
2879+
class LocalFunctionCreationPostUpdateNode extends LocalFunctionCreationNode, SourcePostUpdateNode {
28532880
LocalFunctionCreationPostUpdateNode() { isPostUpdate = true }
28542881

2855-
override LocalFunctionCreationPreNode getPreUpdateNode() {
2882+
override LocalFunctionCreationPreNode getPreUpdateSourceNode() {
28562883
result = TLocalFunctionCreationNode(cfn, false)
28572884
}
28582885

28592886
override string toStringImpl() { result = "[post] " + cfn }
28602887
}
28612888

2862-
private class CapturePostUpdateNode extends PostUpdateNode, CaptureNode {
2889+
private class CapturePostUpdateNode extends SourcePostUpdateNode, CaptureNode {
28632890
private CaptureNode pre;
28642891

28652892
CapturePostUpdateNode() {
28662893
VariableCapture::Flow::capturePostUpdateNode(this.getSynthesizedCaptureNode(),
28672894
pre.getSynthesizedCaptureNode())
28682895
}
28692896

2870-
override CaptureNode getPreUpdateNode() { result = pre }
2897+
override CaptureNode getPreUpdateSourceNode() { result = pre }
28712898

28722899
override string toStringImpl() { result = "[post] " + cn }
28732900
}

0 commit comments

Comments
 (0)